@djangocfg/ui-tools 2.1.418 → 2.1.420

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 (310) hide show
  1. package/README.md +51 -224
  2. package/dist/file-icon/index.cjs +3 -3
  3. package/dist/file-icon/index.cjs.map +1 -1
  4. package/dist/file-icon/index.mjs +3 -3
  5. package/dist/file-icon/index.mjs.map +1 -1
  6. package/package.json +93 -28
  7. package/src/common/FloatingToolbar/actions/CopyAction.tsx +31 -0
  8. package/src/{components → common}/FloatingToolbar/actions/DownloadAction.tsx +15 -10
  9. package/src/common/FloatingToolbar/actions/ExpandAction.tsx +33 -0
  10. package/src/common/FloatingToolbar/actions/FullscreenAction.tsx +38 -0
  11. package/src/{components → common}/FloatingToolbar/index.tsx +39 -0
  12. package/src/lib/http.ts +64 -0
  13. package/src/tools/chat/index.ts +1 -1
  14. package/src/tools/chat/launcher/ChatFAB.tsx +66 -74
  15. package/src/tools/chat/launcher/header/ChatHeaderActionButton.tsx +2 -3
  16. package/src/tools/chat/lazy.tsx +1 -1
  17. package/src/tools/chat/messages/MessageBubble.tsx +1 -1
  18. package/src/tools/chat/messages/blocks/builtin.tsx +1 -1
  19. package/src/tools/chat/messages/blocks/renderers/CodeBlock.tsx +2 -2
  20. package/src/tools/chat/messages/blocks/renderers/JsonBlock.tsx +12 -1
  21. package/src/tools/data/DataGrid/README.md +48 -0
  22. package/src/tools/data/DataGrid/lazy.tsx +1 -1
  23. package/src/tools/data/DataTable/README.md +42 -0
  24. package/src/tools/data/DataTable/lazy.tsx +1 -1
  25. package/src/tools/data/JsonTree/JsonViewer.tsx +720 -0
  26. package/src/tools/data/JsonTree/README.md +126 -73
  27. package/src/tools/data/JsonTree/index.tsx +3 -95
  28. package/src/tools/data/JsonTree/lazy.tsx +10 -50
  29. package/src/tools/data/JsonTree/types.ts +82 -63
  30. package/src/tools/data/Kanban/lazy.tsx +1 -1
  31. package/src/tools/data/Listbox/lazy.tsx +1 -1
  32. package/src/tools/data/Masonry/lazy.tsx +1 -1
  33. package/src/tools/data/Timeline/lazy.tsx +1 -1
  34. package/src/tools/data/Tree/lazy.tsx +1 -1
  35. package/src/tools/dev/Map/lazy.tsx +1 -1
  36. package/src/tools/dev/Mermaid/Mermaid.client.tsx +2 -2
  37. package/src/tools/dev/Mermaid/README.md +46 -0
  38. package/src/tools/dev/Mermaid/lazy.tsx +1 -1
  39. package/src/tools/dev/api/ApiRefTable/ApiRefTable.tsx +65 -0
  40. package/src/tools/dev/api/ApiRefTable/README.md +31 -0
  41. package/src/tools/dev/api/ApiRefTable/components/Row.tsx +96 -0
  42. package/src/tools/dev/api/ApiRefTable/components/TypeDisplay.tsx +44 -0
  43. package/src/tools/dev/api/ApiRefTable/index.ts +6 -0
  44. package/src/tools/dev/api/ApiRefTable/lazy.tsx +21 -0
  45. package/src/tools/dev/api/ApiRefTable/types.ts +82 -0
  46. package/src/tools/dev/api/ApiRefTable/utils.ts +42 -0
  47. package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/ApiIntroSection.tsx +1 -1
  48. package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/EndpointDoc/CodeSamples/index.tsx +1 -1
  49. package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/EndpointDoc/Header/index.tsx +1 -1
  50. package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/EndpointDoc/Responses/ResponseBody.tsx +7 -21
  51. package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/shared/RequestPanel.tsx +1 -1
  52. package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/shared/ResponsePanel/PrettyView.tsx +13 -19
  53. package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/shared/ResponsePanel/types.ts +1 -1
  54. package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/lazy.tsx +1 -1
  55. package/src/tools/dev/api/RequestViewer/README.md +33 -0
  56. package/src/tools/dev/api/RequestViewer/RequestViewer.tsx +121 -0
  57. package/src/tools/dev/api/RequestViewer/components/BodyTab.tsx +44 -0
  58. package/src/tools/dev/api/RequestViewer/components/EmptyState.tsx +13 -0
  59. package/src/tools/dev/api/RequestViewer/components/HeadersTab.tsx +78 -0
  60. package/src/tools/dev/api/RequestViewer/components/TimingTab.tsx +113 -0
  61. package/src/tools/dev/api/RequestViewer/components/utils.ts +31 -0
  62. package/src/tools/dev/api/RequestViewer/index.ts +16 -0
  63. package/src/tools/dev/api/RequestViewer/lazy.tsx +30 -0
  64. package/src/tools/dev/api/RequestViewer/types.ts +81 -0
  65. package/src/tools/dev/code/DiffViewer/DiffViewer.tsx +144 -0
  66. package/src/tools/dev/code/DiffViewer/README.md +33 -0
  67. package/src/tools/dev/code/DiffViewer/components/CopyButton.tsx +49 -0
  68. package/src/tools/dev/code/DiffViewer/components/DiffLineContent.tsx +48 -0
  69. package/src/tools/dev/code/DiffViewer/components/SplitView.tsx +220 -0
  70. package/src/tools/dev/code/DiffViewer/components/UnifiedView.tsx +154 -0
  71. package/src/tools/dev/code/DiffViewer/hooks/useDiff.ts +47 -0
  72. package/src/tools/dev/code/DiffViewer/hooks/useHighlighter.ts +54 -0
  73. package/src/tools/dev/code/DiffViewer/index.ts +22 -0
  74. package/src/tools/dev/code/DiffViewer/lazy.tsx +22 -0
  75. package/src/tools/dev/code/DiffViewer/types.ts +109 -0
  76. package/src/tools/dev/code/DiffViewer/utils/computeDiff.ts +159 -0
  77. package/src/tools/dev/{MarkdownMessage → code/MarkdownMessage}/CollapseToggle.tsx +1 -1
  78. package/src/tools/dev/{MarkdownMessage → code/MarkdownMessage}/MarkdownMessage.tsx +1 -1
  79. package/src/tools/dev/{MarkdownMessage → code/MarkdownMessage}/components.tsx +2 -2
  80. package/src/tools/dev/{PrettyCode → code/PrettyCode}/PrettyCode.client.tsx +2 -2
  81. package/src/tools/dev/{PrettyCode → code/PrettyCode}/lazy.tsx +1 -1
  82. package/src/tools/dev/ops/EnvTable/EnvTable.tsx +228 -0
  83. package/src/tools/dev/ops/EnvTable/README.md +29 -0
  84. package/src/tools/dev/ops/EnvTable/hooks/useEnvMask.ts +121 -0
  85. package/src/tools/dev/ops/EnvTable/index.ts +12 -0
  86. package/src/tools/dev/ops/EnvTable/lazy.tsx +21 -0
  87. package/src/tools/dev/ops/EnvTable/types.ts +76 -0
  88. package/src/tools/dev/ops/LogViewer/LogViewer.tsx +194 -0
  89. package/src/tools/dev/ops/LogViewer/README.md +30 -0
  90. package/src/tools/dev/ops/LogViewer/components/LogRow.tsx +151 -0
  91. package/src/tools/dev/ops/LogViewer/components/Toolbar.tsx +199 -0
  92. package/src/tools/dev/ops/LogViewer/hooks/useAutoScroll.ts +68 -0
  93. package/src/tools/dev/ops/LogViewer/hooks/useLogFilter.ts +58 -0
  94. package/src/tools/dev/ops/LogViewer/index.ts +18 -0
  95. package/src/tools/dev/ops/LogViewer/lazy.tsx +25 -0
  96. package/src/tools/dev/ops/LogViewer/types.ts +142 -0
  97. package/src/tools/dev/ops/LogViewer/utils/ansi.ts +231 -0
  98. package/src/tools/forms/CodeEditor/hooks/useEditorTheme.ts +13 -73
  99. package/src/tools/forms/CodeEditor/lazy.tsx +1 -1
  100. package/src/tools/forms/FileUpload/lazy.tsx +1 -1
  101. package/src/tools/forms/JsonEditor/JsonEditor.tsx +115 -0
  102. package/src/tools/forms/JsonEditor/index.ts +1 -0
  103. package/src/tools/forms/JsonEditor/lazy.tsx +24 -0
  104. package/src/tools/forms/JsonForm/index.ts +1 -1
  105. package/src/tools/forms/JsonForm/lazy.tsx +1 -1
  106. package/src/tools/forms/MarkdownEditor/lazy.tsx +1 -1
  107. package/src/tools/forms/NotionEditor/README.md +237 -0
  108. package/src/tools/forms/NotionEditor/lazy.tsx +1 -1
  109. package/src/tools/index.ts +153 -13
  110. package/src/tools/input/Combobox/lazy.tsx +1 -1
  111. package/src/tools/input/CronScheduler/README.md +61 -0
  112. package/src/tools/input/CronScheduler/components/CronPreview.README.md +28 -0
  113. package/src/tools/input/CronScheduler/components/CronPreview.tsx +136 -0
  114. package/src/tools/input/CronScheduler/components/index.ts +3 -0
  115. package/src/tools/input/CronScheduler/index.tsx +5 -1
  116. package/src/tools/input/CronScheduler/lazy.tsx +5 -1
  117. package/src/tools/input/CronScheduler/utils/cron-next-runs.ts +122 -0
  118. package/src/tools/input/CronScheduler/utils/index.ts +1 -0
  119. package/src/tools/input/Scroller/lazy.tsx +1 -1
  120. package/src/tools/input/Sortable/lazy.tsx +1 -1
  121. package/src/tools/input/SpeechRecognition/lazy.tsx +1 -1
  122. package/src/tools/input/SpeechRecognition/widgets/VoiceComposerSlot.tsx +41 -36
  123. package/src/tools/media/ImageViewer/components/ImageToolbar.tsx +58 -47
  124. package/src/tools/media/ImageViewer/components/ImageViewer.tsx +27 -19
  125. package/src/tools/media/ImageViewer/lazy.tsx +1 -1
  126. package/src/tools/media/LottiePlayer/lazy.tsx +1 -1
  127. package/src/tools/media/VideoPlayer/VideoPlayer.tsx +28 -1
  128. package/src/tools/media/VideoPlayer/parts/fullscreen.tsx +21 -4
  129. package/src/tools/media/VideoPlayer/parts/pip.tsx +21 -4
  130. package/src/tools/media/VideoPlayer/parts/play-button.tsx +21 -4
  131. package/src/tools/media/VideoPlayer/parts/playback-rate.tsx +19 -3
  132. package/src/tools/media/VideoPlayer/parts/volume.tsx +237 -18
  133. package/src/tools/media/VideoPlayer/styles/video-player.css +87 -7
  134. package/src/tools/overlay/ResponsiveDialog/lazy.tsx +1 -1
  135. package/src/tools/overlay/ScrollSpy/lazy.tsx +1 -1
  136. package/src/tools/overlay/SelectionToolbar/lazy.tsx +1 -1
  137. package/src/tools/overlay/Tour/lazy.tsx +1 -1
  138. package/src/tools/visual/Marquee/lazy.tsx +1 -1
  139. package/src/tools/visual/QRCode/lazy.tsx +1 -1
  140. package/src/tools/visual/charts/ActivityGraph/ActivityGraph.tsx +195 -0
  141. package/src/tools/visual/charts/ActivityGraph/README.md +28 -0
  142. package/src/tools/visual/charts/ActivityGraph/index.ts +8 -0
  143. package/src/tools/visual/charts/ActivityGraph/lazy.tsx +21 -0
  144. package/src/tools/visual/charts/ActivityGraph/types.ts +59 -0
  145. package/src/tools/visual/charts/ActivityGraph/utils.ts +88 -0
  146. package/src/tools/visual/charts/CommitGraph/CommitGraph.tsx +80 -0
  147. package/src/tools/visual/charts/CommitGraph/README.md +28 -0
  148. package/src/tools/visual/charts/CommitGraph/components/CommitDetail.tsx +107 -0
  149. package/src/tools/visual/charts/CommitGraph/components/CommitRow.tsx +122 -0
  150. package/src/tools/visual/charts/CommitGraph/components/Rails.tsx +171 -0
  151. package/src/tools/visual/charts/CommitGraph/hooks/useGraphLayout.ts +169 -0
  152. package/src/tools/visual/charts/CommitGraph/hooks/useLaneColors.ts +45 -0
  153. package/src/tools/visual/charts/CommitGraph/index.ts +14 -0
  154. package/src/tools/visual/charts/CommitGraph/lazy.tsx +25 -0
  155. package/src/tools/visual/charts/CommitGraph/types.ts +85 -0
  156. package/src/tools/visual/charts/CommitGraph/utils.ts +53 -0
  157. package/src/tools/visual/charts/Gauge/README.md +45 -0
  158. package/src/tools/visual/{Gauge → charts/Gauge}/lazy.tsx +1 -1
  159. package/src/tools/visual/charts/Sparkline/README.md +29 -0
  160. package/src/tools/visual/charts/Sparkline/Sparkline.tsx +217 -0
  161. package/src/tools/visual/charts/Sparkline/index.ts +9 -0
  162. package/src/tools/visual/charts/Sparkline/lazy.tsx +26 -0
  163. package/src/tools/visual/charts/Sparkline/types.ts +58 -0
  164. package/src/tools/visual/design/ColorPalette/ColorPalette.tsx +129 -0
  165. package/src/tools/visual/design/ColorPalette/README.md +34 -0
  166. package/src/tools/visual/design/ColorPalette/components/Swatch.tsx +102 -0
  167. package/src/tools/visual/design/ColorPalette/hooks/useCopyToClipboard.ts +41 -0
  168. package/src/tools/visual/design/ColorPalette/index.ts +12 -0
  169. package/src/tools/visual/design/ColorPalette/lazy.tsx +21 -0
  170. package/src/tools/visual/design/ColorPalette/types.ts +63 -0
  171. package/src/tools/visual/design/ColorPalette/utils.ts +83 -0
  172. package/src/tools/visual/{ColorPicker → design/ColorPicker}/lazy.tsx +1 -1
  173. package/src/tools/visual/{FileIcon → design/FileIcon}/treeAdapter.tsx +1 -1
  174. package/src/tools/visual/{Fps → indicators/Fps}/lazy.tsx +1 -1
  175. package/src/tools/visual/{Rating → indicators/Rating}/lazy.tsx +1 -1
  176. package/src/tools/visual/indicators/StatusIndicator/README.md +28 -0
  177. package/src/tools/visual/indicators/StatusIndicator/StatusIndicator.tsx +83 -0
  178. package/src/tools/visual/indicators/StatusIndicator/index.ts +14 -0
  179. package/src/tools/visual/indicators/StatusIndicator/lazy.tsx +21 -0
  180. package/src/tools/visual/indicators/StatusIndicator/types.ts +133 -0
  181. package/src/components/FloatingToolbar/actions/CopyAction.tsx +0 -22
  182. package/src/components/FloatingToolbar/actions/ExpandAction.tsx +0 -25
  183. package/src/components/FloatingToolbar/actions/FullscreenAction.tsx +0 -30
  184. package/src/tools/data/JsonTree/components/JsonContent.tsx +0 -197
  185. package/src/tools/data/JsonTree/hooks/useJsonExpand.ts +0 -50
  186. /package/src/{components → common}/FloatingToolbar/FloatingToolbar.css +0 -0
  187. /package/src/{components → common}/FloatingToolbar/actions/index.ts +0 -0
  188. /package/src/{components → common}/FloatingToolbar/hooks/useElementCorner.ts +0 -0
  189. /package/src/{components → common}/FloatingToolbar/hooks/useScrollIsolation.ts +0 -0
  190. /package/src/{components → common}/index.ts +0 -0
  191. /package/src/{components → common}/lazy-wrapper.tsx +0 -0
  192. /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/README.md +0 -0
  193. /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/DocsView.tsx +0 -0
  194. /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/EndpointDoc/CodeSamples/LanguageTabs.tsx +0 -0
  195. /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/EndpointDoc/CodeSamples/useCodeSnippet.ts +0 -0
  196. /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/EndpointDoc/Header/MetaActions.tsx +0 -0
  197. /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/EndpointDoc/Header/MethodBadge.tsx +0 -0
  198. /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/EndpointDoc/Header/PathDisplay.tsx +0 -0
  199. /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/EndpointDoc/Parameters/ParamGroup.tsx +0 -0
  200. /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/EndpointDoc/Parameters/ParamRow.tsx +0 -0
  201. /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/EndpointDoc/Parameters/index.tsx +0 -0
  202. /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/EndpointDoc/RequestBody/index.tsx +0 -0
  203. /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/EndpointDoc/Responses/ResponseRow.tsx +0 -0
  204. /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/EndpointDoc/Responses/StatusTag.tsx +0 -0
  205. /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/EndpointDoc/Responses/index.tsx +0 -0
  206. /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/EndpointDoc/SchemaFields/FieldRow.tsx +0 -0
  207. /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/EndpointDoc/SchemaFields/buildTree.ts +0 -0
  208. /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/EndpointDoc/SchemaFields/index.tsx +0 -0
  209. /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/EndpointDoc/SchemaFields/types.ts +0 -0
  210. /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/EndpointDoc/Section/SectionHeader.tsx +0 -0
  211. /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/EndpointDoc/Section/defaults.ts +0 -0
  212. /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/EndpointDoc/Section/index.tsx +0 -0
  213. /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/EndpointDoc/context.tsx +0 -0
  214. /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/EndpointDoc/hooks/useSectionHash.ts +0 -0
  215. /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/EndpointDoc/index.tsx +0 -0
  216. /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/EndpointDoc/store/index.ts +0 -0
  217. /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/EndpointDoc/store/selectors.ts +0 -0
  218. /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/EndpointDoc/types.ts +0 -0
  219. /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/SchemaCopyMenu.tsx +0 -0
  220. /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/Sidebar/BrandHeader.tsx +0 -0
  221. /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/Sidebar/CategoryBlock.tsx +0 -0
  222. /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/Sidebar/EndpointRow.tsx +0 -0
  223. /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/Sidebar/SchemaSection.tsx +0 -0
  224. /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/Sidebar/SearchInput.tsx +0 -0
  225. /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/Sidebar/SidebarBody.tsx +0 -0
  226. /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/Sidebar/Toolbar.tsx +0 -0
  227. /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/Sidebar/buildVM.ts +0 -0
  228. /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/Sidebar/index.tsx +0 -0
  229. /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/Sidebar/types.ts +0 -0
  230. /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/Sidebar/useDebouncedValue.ts +0 -0
  231. /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/SlideInPlayground.tsx +0 -0
  232. /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/TryItSheet.tsx +0 -0
  233. /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/anchor.ts +0 -0
  234. /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/grouping.ts +0 -0
  235. /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/index.tsx +0 -0
  236. /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/sidebarLabel.ts +0 -0
  237. /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/index.ts +0 -0
  238. /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/shared/BodyFormEditor.tsx +0 -0
  239. /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/shared/EndpointDraftSync.tsx +0 -0
  240. /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/shared/EndpointResetButton.tsx +0 -0
  241. /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/shared/ResponsePanel/PreviewView.tsx +0 -0
  242. /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/shared/ResponsePanel/RawView.tsx +0 -0
  243. /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/shared/ResponsePanel/StatusBar.tsx +0 -0
  244. /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/shared/ResponsePanel/ViewTabs.tsx +0 -0
  245. /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/shared/ResponsePanel/detectContent.ts +0 -0
  246. /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/shared/ResponsePanel/index.tsx +0 -0
  247. /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/shared/ResponsePanel/useResponseView.ts +0 -0
  248. /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/shared/SendButton.tsx +0 -0
  249. /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/shared/ui.tsx +0 -0
  250. /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/constants.ts +0 -0
  251. /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/context/PlaygroundContext.tsx +0 -0
  252. /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/hooks/index.ts +0 -0
  253. /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/hooks/useDocsUrlSync.ts +0 -0
  254. /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/hooks/useEndpointDraft.ts +0 -0
  255. /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/hooks/useMobile.ts +0 -0
  256. /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/hooks/useOpenApiSchema.ts +0 -0
  257. /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/index.tsx +0 -0
  258. /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/types.ts +0 -0
  259. /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/utils/apiKeyManager.ts +0 -0
  260. /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/utils/codeSamples.ts +0 -0
  261. /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/utils/formatters.ts +0 -0
  262. /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/utils/index.ts +0 -0
  263. /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/utils/operationToHar.ts +0 -0
  264. /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/utils/sampler.ts +0 -0
  265. /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/utils/schemaExport.ts +0 -0
  266. /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/utils/scrollParent.ts +0 -0
  267. /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/utils/url.ts +0 -0
  268. /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/utils/versionManager.ts +0 -0
  269. /package/src/tools/dev/{MarkdownMessage → code/MarkdownMessage}/ActionRow.tsx +0 -0
  270. /package/src/tools/dev/{MarkdownMessage → code/MarkdownMessage}/ChatMessageRow.tsx +0 -0
  271. /package/src/tools/dev/{MarkdownMessage → code/MarkdownMessage}/CodeBlock.tsx +0 -0
  272. /package/src/tools/dev/{MarkdownMessage → code/MarkdownMessage}/README.md +0 -0
  273. /package/src/tools/dev/{MarkdownMessage → code/MarkdownMessage}/index.ts +0 -0
  274. /package/src/tools/dev/{MarkdownMessage → code/MarkdownMessage}/linkRules.ts +0 -0
  275. /package/src/tools/dev/{MarkdownMessage → code/MarkdownMessage}/plainText.ts +0 -0
  276. /package/src/tools/dev/{MarkdownMessage → code/MarkdownMessage}/sanitize.ts +0 -0
  277. /package/src/tools/dev/{MarkdownMessage → code/MarkdownMessage}/types.ts +0 -0
  278. /package/src/tools/dev/{PrettyCode → code/PrettyCode}/README.md +0 -0
  279. /package/src/tools/dev/{PrettyCode → code/PrettyCode}/index.tsx +0 -0
  280. /package/src/tools/dev/{PrettyCode → code/PrettyCode}/registerPrismLanguages.ts +0 -0
  281. /package/src/tools/visual/{Gauge → charts/Gauge}/Gauge.tsx +0 -0
  282. /package/src/tools/visual/{Gauge → charts/Gauge}/index.ts +0 -0
  283. /package/src/tools/visual/{Gauge → charts/Gauge}/types.ts +0 -0
  284. /package/src/tools/visual/{ColorPicker → design/ColorPicker}/ColorPicker.tsx +0 -0
  285. /package/src/tools/visual/{ColorPicker → design/ColorPicker}/context/ColorPickerContext.tsx +0 -0
  286. /package/src/tools/visual/{ColorPicker → design/ColorPicker}/context/ColorPickerStore.tsx +0 -0
  287. /package/src/tools/visual/{ColorPicker → design/ColorPicker}/context/index.ts +0 -0
  288. /package/src/tools/visual/{ColorPicker → design/ColorPicker}/index.ts +0 -0
  289. /package/src/tools/visual/{ColorPicker → design/ColorPicker}/lib/color-utils.ts +0 -0
  290. /package/src/tools/visual/{ColorPicker → design/ColorPicker}/parts/ColorPickerAlphaSlider.tsx +0 -0
  291. /package/src/tools/visual/{ColorPicker → design/ColorPicker}/parts/ColorPickerArea.tsx +0 -0
  292. /package/src/tools/visual/{ColorPicker → design/ColorPicker}/parts/ColorPickerEyeDropper.tsx +0 -0
  293. /package/src/tools/visual/{ColorPicker → design/ColorPicker}/parts/ColorPickerFormatSelect.tsx +0 -0
  294. /package/src/tools/visual/{ColorPicker → design/ColorPicker}/parts/ColorPickerHueSlider.tsx +0 -0
  295. /package/src/tools/visual/{ColorPicker → design/ColorPicker}/parts/ColorPickerInput.tsx +0 -0
  296. /package/src/tools/visual/{ColorPicker → design/ColorPicker}/parts/ColorPickerSwatch.tsx +0 -0
  297. /package/src/tools/visual/{ColorPicker → design/ColorPicker}/parts/index.ts +0 -0
  298. /package/src/tools/visual/{ColorPicker → design/ColorPicker}/types.ts +0 -0
  299. /package/src/tools/visual/{FileIcon → design/FileIcon}/FileIcon.tsx +0 -0
  300. /package/src/tools/visual/{FileIcon → design/FileIcon}/get-file-icon.ts +0 -0
  301. /package/src/tools/visual/{FileIcon → design/FileIcon}/icons/icon-data.ts +0 -0
  302. /package/src/tools/visual/{FileIcon → design/FileIcon}/index.ts +0 -0
  303. /package/src/tools/visual/{FileIcon → design/FileIcon}/loader.ts +0 -0
  304. /package/src/tools/visual/{FileIcon → design/FileIcon}/specialFolders.ts +0 -0
  305. /package/src/tools/visual/{Fps → indicators/Fps}/Fps.tsx +0 -0
  306. /package/src/tools/visual/{Fps → indicators/Fps}/index.ts +0 -0
  307. /package/src/tools/visual/{Fps → indicators/Fps}/types.ts +0 -0
  308. /package/src/tools/visual/{Rating → indicators/Rating}/Rating.tsx +0 -0
  309. /package/src/tools/visual/{Rating → indicators/Rating}/index.ts +0 -0
  310. /package/src/tools/visual/{Rating → indicators/Rating}/types.ts +0 -0
@@ -0,0 +1,136 @@
1
+ 'use client';
2
+
3
+ /**
4
+ * CronPreview
5
+ *
6
+ * Read-only sibling of `CronScheduler`. Renders a human-readable summary
7
+ * for a cron expression plus an optional list of upcoming run times.
8
+ *
9
+ * Unlike `SchedulePreview` (which lives inside the editor and pulls its
10
+ * state from `CronSchedulerProvider`), `CronPreview` is fully standalone:
11
+ * pass a value, get a preview. Use it in dashboards, list rows, and
12
+ * read-only schedule views where the heavier editor is not needed.
13
+ *
14
+ * @example
15
+ * <CronPreview value="0 9 * * 1-5" />
16
+ *
17
+ * @example
18
+ * <CronPreview value="*\/15 * * * *" nextRuns={5} />
19
+ */
20
+
21
+ import * as React from 'react';
22
+ import { Calendar } from 'lucide-react';
23
+ import { cn } from '@djangocfg/ui-core/lib';
24
+ import { humanizeCron } from '../utils/cron-humanize';
25
+ import { getNextRuns, formatNextRun } from '../utils/cron-next-runs';
26
+ import { isValidCron } from '../utils/cron-parser';
27
+
28
+ export interface CronPreviewProps extends Omit<React.ComponentPropsWithoutRef<'div'>, 'children'> {
29
+ /** Cron expression (standard 5-field format, e.g. `"0 9 * * 1-5"`). */
30
+ value: string;
31
+ /** Optional heading shown above the summary. */
32
+ title?: string;
33
+ /**
34
+ * Number of upcoming run times to display.
35
+ * `0` hides the list. Defaults to `5`.
36
+ */
37
+ nextRuns?: number;
38
+ /** Show the raw cron expression alongside the summary. Defaults to `true`. */
39
+ showExpression?: boolean;
40
+ /** Base date for computing next runs. Defaults to "now". */
41
+ referenceDate?: Date;
42
+ /** Additional CSS classes for the outer container. */
43
+ className?: string;
44
+ }
45
+
46
+ export function CronPreview({
47
+ value,
48
+ title,
49
+ nextRuns = 5,
50
+ showExpression = true,
51
+ referenceDate,
52
+ className,
53
+ ...rest
54
+ }: CronPreviewProps) {
55
+ const trimmed = (value ?? '').trim();
56
+ const valid = isValidCron(trimmed);
57
+
58
+ const summary = React.useMemo(() => humanizeCron(trimmed), [trimmed]);
59
+
60
+ const runs = React.useMemo(() => {
61
+ if (!valid || nextRuns <= 0) return [];
62
+ return getNextRuns(trimmed, nextRuns, referenceDate ?? new Date());
63
+ }, [trimmed, valid, nextRuns, referenceDate]);
64
+
65
+ if (!valid) {
66
+ return (
67
+ <div
68
+ data-slot="cron-preview"
69
+ className={cn(
70
+ 'rounded-xl border border-destructive/30 bg-destructive/5 px-4 py-3 text-sm text-destructive',
71
+ className
72
+ )}
73
+ {...rest}
74
+ >
75
+ Invalid cron expression: <code className="font-mono">{trimmed || '(empty)'}</code>
76
+ </div>
77
+ );
78
+ }
79
+
80
+ return (
81
+ <div
82
+ data-slot="cron-preview"
83
+ role="status"
84
+ aria-live="polite"
85
+ className={cn(
86
+ 'overflow-hidden rounded-xl border border-border bg-card text-card-foreground shadow-sm',
87
+ className
88
+ )}
89
+ {...rest}
90
+ >
91
+ {/* Header */}
92
+ <div className="flex items-start justify-between gap-3 border-b border-border/60 px-4 py-3">
93
+ <div className="flex min-w-0 items-start gap-2">
94
+ <Calendar aria-hidden="true" className="mt-0.5 h-4 w-4 shrink-0 text-muted-foreground" />
95
+ <div className="flex min-w-0 flex-col gap-0.5">
96
+ {title && (
97
+ <h3 className="text-sm font-semibold text-foreground">{title}</h3>
98
+ )}
99
+ <p className="text-sm text-muted-foreground">{summary}</p>
100
+ </div>
101
+ </div>
102
+ {showExpression && (
103
+ <code className="shrink-0 rounded-md bg-muted px-2.5 py-1 font-mono text-xs text-foreground">
104
+ {trimmed}
105
+ </code>
106
+ )}
107
+ </div>
108
+
109
+ {/* Next runs */}
110
+ {runs.length > 0 && (
111
+ <div className="px-4 py-3">
112
+ <p className="mb-2 text-[10px] font-medium uppercase tracking-wide text-muted-foreground">
113
+ Next {runs.length === 1 ? 'run' : `${runs.length} runs`}
114
+ </p>
115
+ <ol className="flex flex-col gap-1">
116
+ {runs.map((run, i) => (
117
+ <li
118
+ key={run.toISOString()}
119
+ className="flex items-center gap-2 text-sm"
120
+ >
121
+ <span className="flex size-5 shrink-0 items-center justify-center rounded-full bg-muted text-[10px] font-semibold text-muted-foreground">
122
+ {i + 1}
123
+ </span>
124
+ <span className="font-mono text-xs text-foreground">
125
+ {formatNextRun(run)}
126
+ </span>
127
+ </li>
128
+ ))}
129
+ </ol>
130
+ </div>
131
+ )}
132
+ </div>
133
+ );
134
+ }
135
+
136
+ export default CronPreview;
@@ -22,3 +22,6 @@ export type { SchedulePreviewProps } from './SchedulePreview';
22
22
 
23
23
  export { CronCheatsheet } from './CronCheatsheet';
24
24
  export type { CronCheatsheetProps } from './CronCheatsheet';
25
+
26
+ export { CronPreview } from './CronPreview';
27
+ export type { CronPreviewProps } from './CronPreview';
@@ -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
  // ============================================================================