@djangocfg/ui-tools 2.1.407 → 2.1.409

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 (297) hide show
  1. package/README.md +9 -10
  2. package/dist/file-icon/index.cjs +449 -61
  3. package/dist/file-icon/index.cjs.map +1 -1
  4. package/dist/file-icon/index.d.cts +56 -18
  5. package/dist/file-icon/index.d.ts +56 -18
  6. package/dist/file-icon/index.mjs +448 -62
  7. package/dist/file-icon/index.mjs.map +1 -1
  8. package/dist/tree/index.cjs +49 -22
  9. package/dist/tree/index.cjs.map +1 -1
  10. package/dist/tree/index.d.cts +9 -3
  11. package/dist/tree/index.d.ts +9 -3
  12. package/dist/tree/index.mjs +49 -22
  13. package/dist/tree/index.mjs.map +1 -1
  14. package/dist/{types-B_zhyAqR.d.cts → types-eEu8SeiQ.d.cts} +4 -0
  15. package/dist/{types-B_zhyAqR.d.ts → types-eEu8SeiQ.d.ts} +4 -0
  16. package/package.json +8 -13
  17. package/src/components/FloatingToolbar/index.tsx +37 -3
  18. package/src/lib/page-snapshot/__tests__/capture-integration.test.ts +85 -0
  19. package/src/lib/page-snapshot/__tests__/engine.test.ts +36 -0
  20. package/src/lib/page-snapshot/__tests__/redaction-integration.test.ts +99 -0
  21. package/src/lib/page-snapshot/__tests__/tokens.test.ts +17 -0
  22. package/src/lib/page-snapshot/capture/__tests__/budget.test.ts +49 -0
  23. package/src/lib/page-snapshot/capture/__tests__/chrome-filter.test.ts +47 -0
  24. package/src/lib/page-snapshot/capture/__tests__/fold.test.ts +66 -0
  25. package/src/lib/page-snapshot/capture/__tests__/scope.test.ts +74 -0
  26. package/src/lib/page-snapshot/capture/__tests__/walk.test.ts +129 -0
  27. package/src/lib/page-snapshot/capture/accessible-name.ts +73 -0
  28. package/src/lib/page-snapshot/capture/budget.ts +95 -0
  29. package/src/lib/page-snapshot/capture/chrome-filter.ts +81 -0
  30. package/src/lib/page-snapshot/capture/classify.ts +111 -0
  31. package/src/lib/page-snapshot/capture/dom-utils.ts +111 -0
  32. package/src/lib/page-snapshot/capture/fold.ts +96 -0
  33. package/src/lib/page-snapshot/capture/scope.ts +169 -0
  34. package/src/lib/page-snapshot/capture/walk.ts +250 -0
  35. package/src/lib/page-snapshot/cst/__tests__/serialize.test.ts +50 -0
  36. package/src/lib/page-snapshot/cst/directives.ts +47 -0
  37. package/src/lib/page-snapshot/cst/payload.ts +50 -0
  38. package/src/lib/page-snapshot/cst/serialize.ts +84 -0
  39. package/src/lib/page-snapshot/cst/types.ts +115 -0
  40. package/src/lib/page-snapshot/engine.ts +176 -0
  41. package/src/lib/page-snapshot/index.ts +93 -0
  42. package/src/lib/page-snapshot/react/PageSnapshotChip.tsx +72 -0
  43. package/src/lib/page-snapshot/react/PageSnapshotPreview.tsx +78 -0
  44. package/src/lib/page-snapshot/react/__tests__/PageSnapshotChip.test.tsx +54 -0
  45. package/src/lib/page-snapshot/react/__tests__/provider.test.tsx +103 -0
  46. package/src/lib/page-snapshot/react/__tests__/use-page-snapshot-toggle.test.tsx +62 -0
  47. package/src/lib/page-snapshot/react/provider.tsx +162 -0
  48. package/src/lib/page-snapshot/react/use-page-snapshot-toggle.ts +47 -0
  49. package/src/lib/page-snapshot/react/use-page-snapshot.ts +67 -0
  50. package/src/lib/page-snapshot/redaction/__tests__/audit.test.ts +25 -0
  51. package/src/lib/page-snapshot/redaction/__tests__/heuristics.test.ts +73 -0
  52. package/src/lib/page-snapshot/redaction/__tests__/luhn.test.ts +26 -0
  53. package/src/lib/page-snapshot/redaction/__tests__/patterns.test.ts +60 -0
  54. package/src/lib/page-snapshot/redaction/audit.ts +58 -0
  55. package/src/lib/page-snapshot/redaction/heuristics.ts +75 -0
  56. package/src/lib/page-snapshot/redaction/index.ts +75 -0
  57. package/src/lib/page-snapshot/redaction/luhn.ts +25 -0
  58. package/src/lib/page-snapshot/redaction/patterns.ts +111 -0
  59. package/src/lib/page-snapshot/refs/__tests__/registry.test.ts +24 -0
  60. package/src/lib/page-snapshot/refs/registry.ts +46 -0
  61. package/src/lib/page-snapshot/staleness/__tests__/hash.test.ts +34 -0
  62. package/src/lib/page-snapshot/staleness/hash.ts +20 -0
  63. package/src/lib/page-snapshot/tokens.ts +15 -0
  64. package/src/tools/AudioPlayer/context/PlayerProvider.tsx +13 -14
  65. package/src/tools/AudioPlayer/hooks/useAudioElementEvents.ts +55 -6
  66. package/src/tools/AudioPlayer/parts/Meta/TimeDisplay.tsx +2 -5
  67. package/src/tools/Chat/README.md +277 -39
  68. package/src/tools/Chat/composer/Composer.tsx +471 -0
  69. package/src/tools/Chat/composer/ComposerActionBar.tsx +65 -0
  70. package/src/tools/Chat/composer/ComposerBanner.tsx +128 -0
  71. package/src/tools/Chat/composer/ComposerButton.tsx +64 -0
  72. package/src/tools/Chat/composer/ComposerFooter.tsx +90 -0
  73. package/src/tools/Chat/composer/ComposerMenuButton.tsx +62 -0
  74. package/src/tools/Chat/composer/ComposerModelPicker.tsx +104 -0
  75. package/src/tools/Chat/composer/ComposerRichTextarea.tsx +88 -0
  76. package/src/tools/Chat/composer/ComposerToolPill.tsx +95 -0
  77. package/src/tools/Chat/composer/index.ts +45 -0
  78. package/src/tools/Chat/composer/size-context.tsx +26 -0
  79. package/src/tools/Chat/composer/types.ts +143 -0
  80. package/src/tools/Chat/composer/useComposerActions.tsx +164 -0
  81. package/src/tools/Chat/context/ChatProvider.tsx +54 -3
  82. package/src/tools/Chat/core/__tests__/metadata.test.ts +69 -0
  83. package/src/tools/Chat/core/index.ts +23 -1
  84. package/src/tools/Chat/core/markdown.ts +1 -1
  85. package/src/tools/Chat/core/metadata.ts +47 -0
  86. package/src/tools/Chat/core/payload-dispatch.ts +1 -1
  87. package/src/tools/Chat/core/transport/http.ts +71 -32
  88. package/src/tools/Chat/core/transport/sse.ts +18 -10
  89. package/src/tools/Chat/highlight/HighlightOverlay.tsx +101 -0
  90. package/src/tools/Chat/highlight/README.md +103 -0
  91. package/src/tools/Chat/highlight/SpotlightCanvas.tsx +153 -0
  92. package/src/tools/Chat/highlight/__tests__/HighlightOverlay.test.tsx +112 -0
  93. package/src/tools/Chat/highlight/__tests__/resolveRef.test.ts +55 -0
  94. package/src/tools/Chat/highlight/index.ts +21 -0
  95. package/src/tools/Chat/highlight/resolveRef.ts +42 -0
  96. package/src/tools/Chat/highlight/types.ts +49 -0
  97. package/src/tools/Chat/highlight/useHighlightTargets.ts +128 -0
  98. package/src/tools/Chat/hooks/index.ts +0 -5
  99. package/src/tools/Chat/hooks/useAutoFocusOnStreamEnd.ts +28 -47
  100. package/src/tools/Chat/hooks/useChat.ts +47 -14
  101. package/src/tools/Chat/hooks/useChatComposer.ts +2 -2
  102. package/src/tools/Chat/hooks/useChatLayout.ts +1 -1
  103. package/src/tools/Chat/hooks/useStreamEndFocus.ts +54 -0
  104. package/src/tools/Chat/index.ts +25 -219
  105. package/src/tools/Chat/launcher/ChatDock.tsx +1 -1
  106. package/src/tools/Chat/launcher/ChatLauncher.tsx +1 -1
  107. package/src/tools/Chat/launcher/{ChatHeader.tsx → header/ChatHeader.tsx} +24 -11
  108. package/src/tools/Chat/launcher/{ChatHeaderActionButton.tsx → header/ChatHeaderActionButton.tsx} +34 -3
  109. package/src/tools/Chat/launcher/{ChatHeaderLanguageButton.tsx → header/ChatHeaderLanguageButton.tsx} +2 -2
  110. package/src/tools/Chat/launcher/{ChatHeaderModeToggle.tsx → header/ChatHeaderModeToggle.tsx} +1 -1
  111. package/src/tools/Chat/launcher/{ChatHeaderResetButton.tsx → header/ChatHeaderResetButton.tsx} +2 -1
  112. package/src/tools/Chat/launcher/{HeaderSlots.tsx → header/HeaderSlots.tsx} +3 -3
  113. package/src/tools/Chat/launcher/header/index.ts +26 -0
  114. package/src/tools/Chat/launcher/index.ts +3 -10
  115. package/src/tools/Chat/lazy.tsx +38 -284
  116. package/src/tools/Chat/{components → messages}/MessageBubble.tsx +58 -5
  117. package/src/tools/Chat/{components → messages}/MessageList.tsx +8 -25
  118. package/src/tools/Chat/messages/blocks/MessageBlocks.tsx +131 -0
  119. package/src/tools/Chat/messages/blocks/builtin.tsx +91 -0
  120. package/src/tools/Chat/messages/blocks/index.ts +12 -0
  121. package/src/tools/Chat/messages/blocks/registry.tsx +42 -0
  122. package/src/tools/Chat/messages/blocks/renderers/AudioBlock.tsx +20 -0
  123. package/src/tools/Chat/messages/blocks/renderers/CodeBlock.tsx +19 -0
  124. package/src/tools/Chat/messages/blocks/renderers/GalleryBlock.tsx +26 -0
  125. package/src/tools/Chat/messages/blocks/renderers/ImageBlock.tsx +27 -0
  126. package/src/tools/Chat/messages/blocks/renderers/JsonBlock.tsx +12 -0
  127. package/src/tools/Chat/messages/blocks/renderers/LottieBlock.tsx +11 -0
  128. package/src/tools/Chat/messages/blocks/renderers/MapBlock.tsx +36 -0
  129. package/src/tools/Chat/messages/blocks/renderers/MermaidBlock.tsx +11 -0
  130. package/src/tools/Chat/messages/blocks/renderers/VideoBlock.tsx +24 -0
  131. package/src/tools/Chat/messages/blocks/renderers/types.ts +8 -0
  132. package/src/tools/Chat/{components → messages}/index.ts +11 -5
  133. package/src/tools/Chat/public.ts +212 -0
  134. package/src/tools/Chat/shell/ChatRoot.tsx +345 -0
  135. package/src/tools/Chat/{components → shell}/EmptyState.tsx +4 -2
  136. package/src/tools/Chat/shell/index.ts +15 -0
  137. package/src/tools/Chat/types/block.ts +120 -0
  138. package/src/tools/Chat/types/config.ts +0 -5
  139. package/src/tools/Chat/types/index.ts +17 -0
  140. package/src/tools/Chat/types/message.ts +3 -0
  141. package/src/tools/Chat/utils/index.ts +4 -0
  142. package/src/tools/CodeEditor/README.md +4 -6
  143. package/src/tools/CodeEditor/components/DiffEditor.tsx +48 -13
  144. package/src/tools/CodeEditor/components/Editor.tsx +96 -44
  145. package/src/tools/CodeEditor/context/EditorProvider.tsx +34 -17
  146. package/src/tools/CodeEditor/hooks/useEditorTheme.ts +92 -99
  147. package/src/tools/CodeEditor/hooks/useMonaco.ts +37 -22
  148. package/src/tools/CodeEditor/lazy.tsx +6 -0
  149. package/src/tools/CodeEditor/lib/index.ts +1 -1
  150. package/src/tools/CodeEditor/lib/themes.ts +3 -39
  151. package/src/tools/CronScheduler/CronScheduler.client.tsx +230 -61
  152. package/src/tools/CronScheduler/components/CustomInput.tsx +21 -4
  153. package/src/tools/CronScheduler/components/DayChips.tsx +13 -11
  154. package/src/tools/CronScheduler/components/MonthDayGrid.tsx +4 -4
  155. package/src/tools/CronScheduler/components/SchedulePreview.tsx +7 -3
  156. package/src/tools/CronScheduler/components/TimeSelector.tsx +1 -1
  157. package/src/tools/CronScheduler/index.tsx +1 -1
  158. package/src/tools/CronScheduler/types/index.ts +8 -3
  159. package/src/tools/CronScheduler/utils/cron-humanize.ts +61 -16
  160. package/src/tools/CronScheduler/utils/cron-parser.ts +13 -4
  161. package/src/tools/FileIcon/FileIcon.tsx +24 -39
  162. package/src/tools/FileIcon/get-file-icon.ts +73 -0
  163. package/src/tools/FileIcon/icons/icon-data.ts +399 -0
  164. package/src/tools/FileIcon/index.ts +4 -0
  165. package/src/tools/FileIcon/loader.ts +17 -35
  166. package/src/tools/FileIcon/specialFolders.ts +18 -0
  167. package/src/tools/Gallery/components/lightbox/GalleryLightbox.tsx +112 -35
  168. package/src/tools/Gallery/components/media/GalleryVideo.tsx +21 -2
  169. package/src/tools/Gallery/components/preview/GalleryCarousel.tsx +11 -1
  170. package/src/tools/Gallery/hooks/usePreloadImages.ts +54 -7
  171. package/src/tools/ImageViewer/components/ImageInfo.tsx +12 -1
  172. package/src/tools/ImageViewer/components/ImageToolbar.tsx +51 -43
  173. package/src/tools/ImageViewer/components/ImageViewer.tsx +96 -24
  174. package/src/tools/ImageViewer/hooks/useImageLoading.ts +13 -0
  175. package/src/tools/ImageViewer/utils/constants.ts +3 -0
  176. package/src/tools/ImageViewer/utils/index.ts +1 -0
  177. package/src/tools/JsonForm/JsonSchemaForm.tsx +4 -1
  178. package/src/tools/JsonForm/templates/ArrayFieldTemplate.tsx +5 -3
  179. package/src/tools/JsonForm/templates/BaseInputTemplate.tsx +7 -4
  180. package/src/tools/JsonForm/templates/ErrorListTemplate.tsx +3 -1
  181. package/src/tools/JsonForm/templates/ObjectFieldTemplate.tsx +23 -3
  182. package/src/tools/JsonForm/widgets/ColorWidget.tsx +20 -12
  183. package/src/tools/JsonForm/widgets/NumberWidget.tsx +14 -9
  184. package/src/tools/JsonForm/widgets/RadioWidget.tsx +78 -0
  185. package/src/tools/JsonForm/widgets/SelectWidget.tsx +1 -0
  186. package/src/tools/JsonForm/widgets/SliderWidget.tsx +7 -4
  187. package/src/tools/JsonForm/widgets/TextWidget.tsx +41 -17
  188. package/src/tools/JsonForm/widgets/index.ts +1 -0
  189. package/src/tools/JsonTree/components/JsonContent.tsx +115 -40
  190. package/src/tools/LottiePlayer/LottiePlayer.client.tsx +177 -72
  191. package/src/tools/LottiePlayer/index.tsx +14 -4
  192. package/src/tools/LottiePlayer/lazy.tsx +11 -3
  193. package/src/tools/LottiePlayer/types.ts +31 -1
  194. package/src/tools/LottiePlayer/useLottie.ts +32 -9
  195. package/src/tools/LottiePlayer/usePrefersReducedMotion.ts +46 -0
  196. package/src/tools/Map/components/LayerSwitcher.tsx +54 -21
  197. package/src/tools/Map/components/MapCluster.tsx +28 -21
  198. package/src/tools/Map/components/MapContainer.tsx +11 -4
  199. package/src/tools/Map/components/MapLegend.tsx +46 -15
  200. package/src/tools/Map/components/MapMarker.tsx +31 -2
  201. package/src/tools/Map/hooks/useMapEvents.ts +64 -105
  202. package/src/tools/MarkdownEditor/MarkdownEditor.tsx +61 -6
  203. package/src/tools/MarkdownEditor/MentionList.tsx +37 -4
  204. package/src/tools/MarkdownEditor/createMentionSuggestion.ts +11 -0
  205. package/src/tools/MarkdownEditor/lazy.tsx +32 -7
  206. package/src/tools/MarkdownEditor/styles.css +13 -0
  207. package/src/tools/MarkdownMessage/CodeBlock.tsx +40 -17
  208. package/src/tools/MarkdownMessage/MarkdownMessage.tsx +26 -6
  209. package/src/tools/MarkdownMessage/components.tsx +22 -9
  210. package/src/tools/MarkdownMessage/types.ts +24 -1
  211. package/src/tools/Mermaid/Mermaid.client.tsx +27 -5
  212. package/src/tools/Mermaid/components/MermaidErrorPanel.tsx +31 -0
  213. package/src/tools/Mermaid/components/MermaidFullscreenModal.tsx +14 -17
  214. package/src/tools/Mermaid/hooks/useMermaidRenderer.ts +264 -168
  215. package/src/tools/Mermaid/hooks/useMermaidValidation.ts +76 -10
  216. package/src/tools/Mermaid/index.tsx +6 -0
  217. package/src/tools/Mermaid/utils/mermaid-helpers.ts +141 -18
  218. package/src/tools/OpenapiViewer/components/DocsLayout/EndpointDoc/SchemaFields/FieldRow.tsx +11 -1
  219. package/src/tools/OpenapiViewer/components/DocsLayout/EndpointDoc/SchemaFields/buildTree.ts +49 -20
  220. package/src/tools/OpenapiViewer/components/DocsLayout/EndpointDoc/index.tsx +7 -0
  221. package/src/tools/OpenapiViewer/components/DocsLayout/grouping.ts +7 -4
  222. package/src/tools/OpenapiViewer/constants.ts +3 -0
  223. package/src/tools/OpenapiViewer/hooks/useOpenApiSchema.ts +73 -11
  224. package/src/tools/OpenapiViewer/utils/schemaExport.ts +26 -6
  225. package/src/tools/PrettyCode/PrettyCode.client.tsx +23 -16
  226. package/src/tools/PrettyCode/lazy.tsx +1 -1
  227. package/src/tools/SpeechRecognition/README.md +1 -1
  228. package/src/tools/SpeechRecognition/__tests__/language.test.ts +9 -3
  229. package/src/tools/SpeechRecognition/components/RecordingPulse.tsx +59 -0
  230. package/src/tools/SpeechRecognition/components/index.ts +2 -0
  231. package/src/tools/SpeechRecognition/core/engine/external.ts +24 -7
  232. package/src/tools/SpeechRecognition/core/language.ts +23 -6
  233. package/src/tools/SpeechRecognition/hooks/usePushToTalk.ts +36 -5
  234. package/src/tools/SpeechRecognition/hooks/useSpeechRecognition.ts +18 -11
  235. package/src/tools/SpeechRecognition/widgets/VoiceComposerSlot.tsx +94 -26
  236. package/src/tools/SpeechRecognition/widgets/index.ts +1 -1
  237. package/src/tools/Tree/README.md +4 -8
  238. package/src/tools/Tree/TreeRoot.tsx +22 -10
  239. package/src/tools/Tree/components/TreeContent.tsx +24 -4
  240. package/src/tools/Tree/components/TreeLabel.tsx +8 -2
  241. package/src/tools/Tree/components/TreeRow.tsx +16 -6
  242. package/src/tools/Tree/data/flatten.ts +10 -4
  243. package/src/tools/Tree/types.ts +4 -0
  244. package/src/tools/Uploader/components/UploadAddButton.tsx +29 -6
  245. package/src/tools/Uploader/components/UploadDropzone.tsx +63 -7
  246. package/src/tools/Uploader/components/UploadPageDropOverlay.tsx +19 -5
  247. package/src/tools/Uploader/components/UploadPreviewItem.tsx +47 -17
  248. package/src/tools/Uploader/components/UploadPreviewList.tsx +24 -12
  249. package/src/tools/Uploader/utils/formatters.ts +8 -3
  250. package/src/tools/VideoPlayer/canvas/hls-canvas.tsx +1 -0
  251. package/src/tools/VideoPlayer/canvas/{jsx.d.ts → jsx-augmentation.ts} +12 -19
  252. package/src/tools/VideoPlayer/canvas/vimeo-canvas.tsx +1 -0
  253. package/src/tools/VideoPlayer/canvas/youtube-canvas.tsx +1 -0
  254. package/src/tools/VideoPlayer/parts/fullscreen.tsx +1 -1
  255. package/src/tools/VideoPlayer/parts/pip.tsx +1 -1
  256. package/src/tools/VideoPlayer/parts/playback-rate.tsx +1 -1
  257. package/src/tools/VideoPlayer/parts/seek-bar.tsx +2 -2
  258. package/src/tools/VideoPlayer/parts/volume.tsx +2 -2
  259. package/src/tools/index.ts +2 -1
  260. package/src/tools/Chat/components/AudioToggle.tsx +0 -78
  261. package/src/tools/Chat/components/ChatRoot.tsx +0 -305
  262. package/src/tools/Chat/components/Composer.tsx +0 -216
  263. package/src/tools/Chat/hooks/useChatScroll.ts +0 -145
  264. package/src/tools/Chat/types.ts +0 -9
  265. package/src/tools/JsonTree/components/JsonToolbar.tsx +0 -95
  266. package/src/tools/JsonTree/hooks/useElementCorner.ts +0 -84
  267. package/src/tools/JsonTree/hooks/useNavbarHeight.ts +0 -83
  268. package/src/tools/OpenapiViewer/components/DocsLayout/schemaFields.ts +0 -121
  269. package/src/tools/Tour/README.md +0 -373
  270. package/src/tools/Tour/components/Tour.tsx +0 -12
  271. package/src/tools/Tour/components/TourContent.tsx +0 -171
  272. package/src/tools/Tour/components/TourNavigation.tsx +0 -77
  273. package/src/tools/Tour/components/TourProgress.tsx +0 -88
  274. package/src/tools/Tour/components/TourSpotlight.tsx +0 -199
  275. package/src/tools/Tour/components/index.ts +0 -5
  276. package/src/tools/Tour/context/TourContext.ts +0 -19
  277. package/src/tools/Tour/context/TourProvider.tsx +0 -292
  278. package/src/tools/Tour/context/index.ts +0 -2
  279. package/src/tools/Tour/hooks/index.ts +0 -3
  280. package/src/tools/Tour/hooks/useKeyboardNavigation.ts +0 -59
  281. package/src/tools/Tour/hooks/useStepTarget.ts +0 -121
  282. package/src/tools/Tour/hooks/useTour.ts +0 -42
  283. package/src/tools/Tour/index.ts +0 -38
  284. package/src/tools/Tour/types/index.ts +0 -224
  285. package/src/tools/Tour/utils/dom.ts +0 -98
  286. package/src/tools/Tour/utils/index.ts +0 -3
  287. package/src/tools/Tour/utils/logger.ts +0 -3
  288. package/src/tools/Tour/utils/scrollIntoView.ts +0 -24
  289. /package/src/tools/Chat/{config.ts → constants.ts} +0 -0
  290. /package/src/tools/Chat/launcher/{ChatHeaderAudioToggle.tsx → header/ChatHeaderAudioToggle.tsx} +0 -0
  291. /package/src/tools/Chat/{components → messages}/Attachments.tsx +0 -0
  292. /package/src/tools/Chat/{components → messages}/JumpToLatest.tsx +0 -0
  293. /package/src/tools/Chat/{components → messages}/MessageActions.tsx +0 -0
  294. /package/src/tools/Chat/{components → messages}/Sources.tsx +0 -0
  295. /package/src/tools/Chat/{components → messages}/StreamingIndicator.tsx +0 -0
  296. /package/src/tools/Chat/{components → messages}/ToolCalls.tsx +0 -0
  297. /package/src/tools/Chat/{components → shell}/ErrorBanner.tsx +0 -0
@@ -1,31 +1,33 @@
1
1
  'use client';
2
2
 
3
- import { useEffect, useRef } from 'react';
4
- import { useResolvedTheme } from '@djangocfg/ui-core/hooks';
3
+ import { useEffect, useState } from 'react';
4
+
5
+ /** Background used for the editor and its loading/error placeholders. */
6
+ export const EDITOR_BACKGROUND = '#0a0a0a';
5
7
 
6
8
  /**
7
- * Hook that syncs Monaco editor theme with the app theme.
9
+ * Hook that registers and returns the Monaco editor theme.
8
10
  *
9
- * Reads CSS variables from the document and creates custom Monaco themes
10
- * that match the current light/dark mode. Falls back to built-in 'vs' / 'vs-dark'.
11
+ * The editor is always dark devtool-style regardless of the app theme.
12
+ * Colors are read from the document's CSS variables so the editor still
13
+ * matches the design tokens, with a dark fallback for SSR.
11
14
  *
12
15
  * @param monaco - Monaco namespace (null during loading)
13
- * @param themeOverride - Optional explicit theme name (skips auto-detection)
16
+ * @param themeOverride - Optional explicit theme name (skips the app theme)
14
17
  * @returns Resolved Monaco theme name to pass to editor options
15
18
  */
16
19
  export function useEditorTheme(
17
20
  monaco: typeof import('monaco-editor') | null,
18
21
  themeOverride?: string,
19
22
  ): string {
20
- const appTheme = useResolvedTheme();
21
- const registered = useRef(false);
23
+ // State (not ref) so consumers re-render once the custom theme is registered.
24
+ const [registered, setRegistered] = useState(false);
22
25
 
23
- // Register custom themes once Monaco is loaded
24
26
  useEffect(() => {
25
- if (!monaco || registered.current) return;
27
+ if (!monaco) return;
26
28
 
27
29
  try {
28
- const colors = _readCSSColors();
30
+ const colors = readEditorColors();
29
31
 
30
32
  monaco.editor.defineTheme('app-dark', {
31
33
  base: 'vs-dark',
@@ -53,106 +55,97 @@ export function useEditorTheme(
53
55
  },
54
56
  });
55
57
 
56
- monaco.editor.defineTheme('app-light', {
57
- base: 'vs',
58
- inherit: true,
59
- rules: [
60
- { token: 'comment', foreground: '008000', fontStyle: 'italic' },
61
- { token: 'keyword', foreground: 'AF00DB' },
62
- { token: 'string', foreground: 'A31515' },
63
- { token: 'number', foreground: '098658' },
64
- { token: 'type', foreground: '267F99' },
65
- { token: 'function', foreground: '795E26' },
66
- { token: 'variable', foreground: '001080' },
67
- ],
68
- colors: {
69
- 'editor.background': colors.backgroundLight,
70
- 'editor.foreground': colors.foregroundLight,
71
- 'editor.lineHighlightBackground': colors.lineHighlightLight,
72
- 'editor.selectionBackground': colors.selectionLight,
73
- 'editorLineNumber.foreground': colors.mutedForegroundLight,
74
- 'editorWidget.background': colors.cardLight,
75
- 'editorWidget.border': colors.borderLight,
76
- },
77
- });
78
-
79
- registered.current = true;
58
+ setRegistered(true);
80
59
  } catch {
81
- // Fallback — use built-in themes
60
+ // Fallback — use the built-in dark theme.
61
+ setRegistered(false);
82
62
  }
83
63
  }, [monaco]);
84
64
 
85
- // If explicit override provided, use it
86
65
  if (themeOverride) return themeOverride;
87
-
88
- // Use registered app themes if available, fallback to built-in
89
- if (registered.current) {
90
- return appTheme === 'dark' ? 'app-dark' : 'app-light';
91
- }
92
- return appTheme === 'dark' ? 'vs-dark' : 'vs';
66
+ return registered ? 'app-dark' : 'vs-dark';
93
67
  }
94
68
 
95
-
96
- /** Read CSS variables and convert HSL to hex for Monaco */
97
- function _readCSSColors() {
98
- const get = (varName: string): string => {
99
- if (typeof document === 'undefined') return '';
100
- return getComputedStyle(document.documentElement).getPropertyValue(varName).trim();
69
+ /**
70
+ * Read editor colors from the document's dark theme CSS variables.
71
+ *
72
+ * Tokens in `tokens.css` are stored wrapped, e.g. `--background: hsl(0 0% 4%)`,
73
+ * so we resolve them through the browser by painting onto a probe element and
74
+ * reading back the computed (already-rgb) color. This works for any color
75
+ * syntax (hsl/oklch/hex).
76
+ */
77
+ function readEditorColors() {
78
+ // Dark defaults — used during SSR and as a safety net.
79
+ const fallback = {
80
+ background: EDITOR_BACKGROUND,
81
+ foreground: '#f5f5f5',
82
+ card: '#141414',
83
+ border: '#262626',
84
+ mutedForeground: '#858585',
85
+ lineHighlight: '#1a1a1a',
86
+ selection: '#264F78',
101
87
  };
102
88
 
103
- const hslToHex = (hsl: string): string => {
104
- if (!hsl) return '';
105
- // CSS vars are "0 0% 96%" — strip % before parsing
106
- const parts = hsl.split(/\s+/).map(s => parseFloat(s.replace('%', '')));
107
- if (parts.length < 3 || parts.some(isNaN)) return '';
108
- const [h, s, l] = [parts[0], parts[1] / 100, parts[2] / 100];
109
-
110
- const a = s * Math.min(l, 1 - l);
111
- const f = (n: number) => {
112
- const k = (n + h / 30) % 12;
113
- const color = l - a * Math.max(Math.min(k - 3, 9 - k, 1), -1);
114
- return Math.round(255 * color).toString(16).padStart(2, '0');
115
- };
116
- return `#${f(0)}${f(8)}${f(4)}`;
117
- };
89
+ if (typeof document === 'undefined' || typeof window === 'undefined') {
90
+ return fallback;
91
+ }
118
92
 
119
- // Read current (dark) theme CSS variables
120
- const background = hslToHex(get('--background')) || '#0a0a0a';
121
- const foreground = hslToHex(get('--foreground')) || '#f5f5f5';
122
- const card = hslToHex(get('--card')) || '#141414';
123
- const border = hslToHex(get('--border')) || '#262626';
124
- const mutedForeground = hslToHex(get('--muted-foreground')) || '#858585';
125
-
126
- // Compute derived colors
127
- const lineHighlight = _adjustBrightness(background, 10);
128
- const primary = hslToHex(get('--primary'));
129
- const selection = primary ? _adjustBrightness(primary, -40) : '#264F78';
130
-
131
- // Light theme colors (hardcoded since we can't switch CSS context)
132
- return {
133
- background,
134
- foreground,
135
- card,
136
- border,
137
- mutedForeground,
138
- lineHighlight,
139
- selection,
140
- // Light variants
141
- backgroundLight: '#ffffff',
142
- foregroundLight: '#1a1a1a',
143
- cardLight: '#ffffff',
144
- borderLight: '#e5e5e5',
145
- mutedForegroundLight: '#737373',
146
- lineHighlightLight: '#f5f5f5',
147
- selectionLight: '#ADD6FF',
93
+ const probe = document.createElement('div');
94
+ probe.style.position = 'absolute';
95
+ probe.style.pointerEvents = 'none';
96
+ probe.style.opacity = '0';
97
+ document.body.appendChild(probe);
98
+
99
+ /** Resolve a CSS var to a hex color via computed style; '' on failure. */
100
+ const resolve = (varName: string): string => {
101
+ probe.style.color = `var(${varName})`;
102
+ const computed = getComputedStyle(probe).color;
103
+ return rgbToHex(computed);
148
104
  };
105
+
106
+ try {
107
+ const background = resolve('--background') || fallback.background;
108
+ const foreground = resolve('--foreground') || fallback.foreground;
109
+ const card = resolve('--card') || fallback.card;
110
+ const border = resolve('--border') || fallback.border;
111
+ const mutedForeground = resolve('--muted-foreground') || fallback.mutedForeground;
112
+ const primary = resolve('--primary');
113
+
114
+ return {
115
+ background,
116
+ foreground,
117
+ card,
118
+ border,
119
+ mutedForeground,
120
+ lineHighlight: mixHex(background, foreground, 0.06),
121
+ selection: primary ? mixHex(primary, background, 0.55) : fallback.selection,
122
+ };
123
+ } finally {
124
+ probe.remove();
125
+ }
149
126
  }
150
127
 
151
- function _adjustBrightness(hex: string, amount: number): string {
152
- const num = parseInt(hex.replace('#', ''), 16);
153
- const r = Math.min(255, Math.max(0, ((num >> 16) & 0xff) + amount));
154
- const g = Math.min(255, Math.max(0, ((num >> 8) & 0xff) + amount));
155
- const b = Math.min(255, Math.max(0, (num & 0xff) + amount));
156
- return `#${((r << 16) | (g << 8) | b).toString(16).padStart(6, '0')}`;
128
+ /** Convert a computed `rgb(r, g, b)` / `rgba(...)` string to `#rrggbb`. */
129
+ function rgbToHex(rgb: string): string {
130
+ const match = rgb.match(/(\d+(?:\.\d+)?)/g);
131
+ if (!match || match.length < 3) return '';
132
+ const [r, g, b] = match.slice(0, 3).map((n) => Math.round(parseFloat(n)));
133
+ const toHex = (v: number) => Math.min(255, Math.max(0, v)).toString(16).padStart(2, '0');
134
+ return `#${toHex(r)}${toHex(g)}${toHex(b)}`;
157
135
  }
158
136
 
137
+ /** Linearly blend `a` toward `b` by `t` (0..1). Both must be `#rrggbb`. */
138
+ function mixHex(a: string, b: string, t: number): string {
139
+ const pa = parseInt(a.slice(1), 16);
140
+ const pb = parseInt(b.slice(1), 16);
141
+ if (Number.isNaN(pa) || Number.isNaN(pb)) return a;
142
+ const lerp = (shift: number) => {
143
+ const ca = (pa >> shift) & 0xff;
144
+ const cb = (pb >> shift) & 0xff;
145
+ return Math.round(ca + (cb - ca) * t);
146
+ };
147
+ const r = lerp(16);
148
+ const g = lerp(8);
149
+ const bch = lerp(0);
150
+ return `#${((r << 16) | (g << 8) | bch).toString(16).padStart(6, '0')}`;
151
+ }
@@ -6,11 +6,36 @@ import type * as MonacoEditor from 'monaco-editor';
6
6
  import { setupMonacoWorkers } from '../workers/setup';
7
7
  import type { UseMonacoReturn } from '../types';
8
8
 
9
+ /**
10
+ * Shared, module-level Monaco loader.
11
+ *
12
+ * Monaco is ~550KB — we load it exactly once per page and hand the same
13
+ * module to every `useMonaco()` consumer. Without this each Editor /
14
+ * DiffEditor / EditorProvider would manage its own loading state and
15
+ * briefly flash a spinner even though the module is already in memory.
16
+ */
17
+ let monacoPromise: Promise<typeof MonacoEditor> | null = null;
18
+
19
+ function loadMonaco(): Promise<typeof MonacoEditor> {
20
+ if (!monacoPromise) {
21
+ monacoPromise = (async () => {
22
+ setupMonacoWorkers();
23
+ return import('monaco-editor');
24
+ })().catch((err) => {
25
+ // Reset so a later mount can retry instead of being stuck on a
26
+ // permanently-rejected promise.
27
+ monacoPromise = null;
28
+ throw err;
29
+ });
30
+ }
31
+ return monacoPromise;
32
+ }
33
+
9
34
  /**
10
35
  * Hook to load and access Monaco Editor instance
11
36
  *
12
37
  * Handles:
13
- * - Dynamic import of Monaco (client-side only)
38
+ * - Dynamic import of Monaco (client-side only, loaded once and shared)
14
39
  * - Web worker configuration
15
40
  * - Loading state management
16
41
  *
@@ -33,27 +58,17 @@ export function useMonaco(): UseMonacoReturn {
33
58
  useEffect(() => {
34
59
  let mounted = true;
35
60
 
36
- async function loadMonaco() {
37
- try {
38
- // Setup workers first
39
- setupMonacoWorkers();
40
-
41
- // Dynamic import Monaco
42
- const monacoModule = await import('monaco-editor');
43
-
44
- if (mounted) {
45
- setMonaco(monacoModule);
46
- setIsLoading(false);
47
- }
48
- } catch (err) {
49
- if (mounted) {
50
- setError(err instanceof Error ? err : new Error('Failed to load Monaco Editor'));
51
- setIsLoading(false);
52
- }
53
- }
54
- }
55
-
56
- loadMonaco();
61
+ loadMonaco()
62
+ .then((monacoModule) => {
63
+ if (!mounted) return;
64
+ setMonaco(monacoModule);
65
+ setIsLoading(false);
66
+ })
67
+ .catch((err) => {
68
+ if (!mounted) return;
69
+ setError(err instanceof Error ? err : new Error('Failed to load Monaco Editor'));
70
+ setIsLoading(false);
71
+ });
57
72
 
58
73
  return () => {
59
74
  mounted = false;
@@ -43,6 +43,12 @@ export const LazyDiffEditor = createLazyComponent<DiffEditorProps>(
43
43
  },
44
44
  );
45
45
 
46
+ // `Editor` / `DiffEditor` are the plain named exports — same lazy components,
47
+ // kept so callers can `import { Editor } from '@djangocfg/ui-tools/code-editor'`
48
+ // without knowing about the `Lazy*` naming.
49
+ export { LazyEditor as Editor };
50
+ export { LazyDiffEditor as DiffEditor };
51
+
46
52
  // ============================================================================
47
53
  // Light surface
48
54
  // ============================================================================
@@ -1,2 +1,2 @@
1
1
  export { getLanguageByExtension, getLanguageByFilename, LANGUAGE_MAP } from './languages';
2
- export { EDITOR_THEMES, getDefaultTheme } from './themes';
2
+ export { EDITOR_THEMES } from './themes';
@@ -1,32 +1,19 @@
1
1
  /**
2
2
  * Editor Themes
3
3
  *
4
- * Built-in Monaco themes and custom CMDOP themes
4
+ * The editor is always dark (devtool-style), so only dark themes are exposed.
5
5
  */
6
6
 
7
- export type EditorTheme = 'vs' | 'vs-dark' | 'hc-black' | 'hc-light' | 'cmdop-dark' | 'cmdop-light';
7
+ export type EditorTheme = 'vs-dark' | 'hc-black' | 'cmdop-dark';
8
8
 
9
9
  export const EDITOR_THEMES = {
10
10
  // Built-in themes
11
- 'vs': 'Light (VS Code)',
12
11
  'vs-dark': 'Dark (VS Code)',
13
12
  'hc-black': 'High Contrast Dark',
14
- 'hc-light': 'High Contrast Light',
15
- // Custom themes (to be defined)
13
+ // Custom themes
16
14
  'cmdop-dark': 'CMDOP Dark',
17
- 'cmdop-light': 'CMDOP Light',
18
15
  } as const;
19
16
 
20
- /**
21
- * Get default theme based on system preference
22
- */
23
- export function getDefaultTheme(): EditorTheme {
24
- if (typeof window === 'undefined') return 'vs-dark';
25
-
26
- const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
27
- return prefersDark ? 'vs-dark' : 'vs';
28
- }
29
-
30
17
  /**
31
18
  * CMDOP Dark Theme Definition
32
19
  * Can be registered with monaco.editor.defineTheme()
@@ -53,26 +40,3 @@ export const cmdopDarkTheme = {
53
40
  'editorLineNumber.activeForeground': '#C6C6C6',
54
41
  },
55
42
  };
56
-
57
- /**
58
- * CMDOP Light Theme Definition
59
- */
60
- export const cmdopLightTheme = {
61
- base: 'vs' as const,
62
- inherit: true,
63
- rules: [
64
- { token: 'comment', foreground: '008000', fontStyle: 'italic' },
65
- { token: 'keyword', foreground: 'AF00DB' },
66
- { token: 'string', foreground: 'A31515' },
67
- { token: 'number', foreground: '098658' },
68
- { token: 'type', foreground: '267F99' },
69
- { token: 'function', foreground: '795E26' },
70
- { token: 'variable', foreground: '001080' },
71
- ],
72
- colors: {
73
- 'editor.background': '#FFFFFF',
74
- 'editor.foreground': '#000000',
75
- 'editor.lineHighlightBackground': '#F5F5F5',
76
- 'editor.selectionBackground': '#ADD6FF',
77
- },
78
- };