@djangocfg/ui-tools 2.1.404 → 2.1.408
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +9 -11
- package/dist/file-icon/index.cjs +449 -61
- package/dist/file-icon/index.cjs.map +1 -1
- package/dist/file-icon/index.d.cts +56 -18
- package/dist/file-icon/index.d.ts +56 -18
- package/dist/file-icon/index.mjs +448 -62
- package/dist/file-icon/index.mjs.map +1 -1
- package/dist/tree/index.cjs +49 -22
- package/dist/tree/index.cjs.map +1 -1
- package/dist/tree/index.d.cts +9 -3
- package/dist/tree/index.d.ts +9 -3
- package/dist/tree/index.mjs +49 -22
- package/dist/tree/index.mjs.map +1 -1
- package/dist/{types-B_zhyAqR.d.cts → types-eEu8SeiQ.d.cts} +4 -0
- package/dist/{types-B_zhyAqR.d.ts → types-eEu8SeiQ.d.ts} +4 -0
- package/package.json +13 -16
- package/src/components/FloatingToolbar/index.tsx +37 -3
- package/src/lib/page-snapshot/__tests__/capture-integration.test.ts +85 -0
- package/src/lib/page-snapshot/__tests__/engine.test.ts +36 -0
- package/src/lib/page-snapshot/__tests__/redaction-integration.test.ts +99 -0
- package/src/lib/page-snapshot/__tests__/tokens.test.ts +17 -0
- package/src/lib/page-snapshot/capture/__tests__/budget.test.ts +49 -0
- package/src/lib/page-snapshot/capture/__tests__/chrome-filter.test.ts +47 -0
- package/src/lib/page-snapshot/capture/__tests__/fold.test.ts +66 -0
- package/src/lib/page-snapshot/capture/__tests__/scope.test.ts +74 -0
- package/src/lib/page-snapshot/capture/__tests__/walk.test.ts +129 -0
- package/src/lib/page-snapshot/capture/accessible-name.ts +73 -0
- package/src/lib/page-snapshot/capture/budget.ts +95 -0
- package/src/lib/page-snapshot/capture/chrome-filter.ts +81 -0
- package/src/lib/page-snapshot/capture/classify.ts +111 -0
- package/src/lib/page-snapshot/capture/dom-utils.ts +111 -0
- package/src/lib/page-snapshot/capture/fold.ts +96 -0
- package/src/lib/page-snapshot/capture/scope.ts +169 -0
- package/src/lib/page-snapshot/capture/walk.ts +250 -0
- package/src/lib/page-snapshot/cst/__tests__/serialize.test.ts +50 -0
- package/src/lib/page-snapshot/cst/directives.ts +47 -0
- package/src/lib/page-snapshot/cst/payload.ts +50 -0
- package/src/lib/page-snapshot/cst/serialize.ts +84 -0
- package/src/lib/page-snapshot/cst/types.ts +115 -0
- package/src/lib/page-snapshot/engine.ts +176 -0
- package/src/lib/page-snapshot/index.ts +93 -0
- package/src/lib/page-snapshot/react/PageSnapshotChip.tsx +72 -0
- package/src/lib/page-snapshot/react/PageSnapshotPreview.tsx +78 -0
- package/src/lib/page-snapshot/react/__tests__/PageSnapshotChip.test.tsx +54 -0
- package/src/lib/page-snapshot/react/__tests__/provider.test.tsx +103 -0
- package/src/lib/page-snapshot/react/__tests__/use-page-snapshot-toggle.test.tsx +62 -0
- package/src/lib/page-snapshot/react/provider.tsx +162 -0
- package/src/lib/page-snapshot/react/use-page-snapshot-toggle.ts +47 -0
- package/src/lib/page-snapshot/react/use-page-snapshot.ts +67 -0
- package/src/lib/page-snapshot/redaction/__tests__/audit.test.ts +25 -0
- package/src/lib/page-snapshot/redaction/__tests__/heuristics.test.ts +73 -0
- package/src/lib/page-snapshot/redaction/__tests__/luhn.test.ts +26 -0
- package/src/lib/page-snapshot/redaction/__tests__/patterns.test.ts +60 -0
- package/src/lib/page-snapshot/redaction/audit.ts +58 -0
- package/src/lib/page-snapshot/redaction/heuristics.ts +75 -0
- package/src/lib/page-snapshot/redaction/index.ts +75 -0
- package/src/lib/page-snapshot/redaction/luhn.ts +25 -0
- package/src/lib/page-snapshot/redaction/patterns.ts +111 -0
- package/src/lib/page-snapshot/refs/__tests__/registry.test.ts +24 -0
- package/src/lib/page-snapshot/refs/registry.ts +46 -0
- package/src/lib/page-snapshot/staleness/__tests__/hash.test.ts +34 -0
- package/src/lib/page-snapshot/staleness/hash.ts +20 -0
- package/src/lib/page-snapshot/tokens.ts +15 -0
- package/src/tools/AudioPlayer/context/PlayerProvider.tsx +13 -14
- package/src/tools/AudioPlayer/hooks/useAudioElementEvents.ts +55 -6
- package/src/tools/AudioPlayer/lazy.tsx +13 -27
- package/src/tools/AudioPlayer/parts/Meta/TimeDisplay.tsx +2 -5
- package/src/tools/Chat/README.md +267 -39
- package/src/tools/Chat/composer/Composer.tsx +471 -0
- package/src/tools/Chat/composer/ComposerActionBar.tsx +65 -0
- package/src/tools/Chat/composer/ComposerBanner.tsx +128 -0
- package/src/tools/Chat/composer/ComposerButton.tsx +64 -0
- package/src/tools/Chat/composer/ComposerFooter.tsx +90 -0
- package/src/tools/Chat/composer/ComposerMenuButton.tsx +62 -0
- package/src/tools/Chat/composer/ComposerModelPicker.tsx +104 -0
- package/src/tools/Chat/composer/ComposerRichTextarea.tsx +88 -0
- package/src/tools/Chat/composer/ComposerToolPill.tsx +95 -0
- package/src/tools/Chat/composer/index.ts +45 -0
- package/src/tools/Chat/composer/size-context.tsx +26 -0
- package/src/tools/Chat/composer/types.ts +143 -0
- package/src/tools/Chat/composer/useComposerActions.tsx +164 -0
- package/src/tools/Chat/context/ChatProvider.tsx +54 -3
- package/src/tools/Chat/core/__tests__/metadata.test.ts +69 -0
- package/src/tools/Chat/core/index.ts +23 -1
- package/src/tools/Chat/core/markdown.ts +1 -1
- package/src/tools/Chat/core/metadata.ts +47 -0
- package/src/tools/Chat/core/payload-dispatch.ts +1 -1
- package/src/tools/Chat/core/transport/http.ts +71 -32
- package/src/tools/Chat/core/transport/sse.ts +18 -10
- package/src/tools/Chat/highlight/HighlightOverlay.tsx +101 -0
- package/src/tools/Chat/highlight/README.md +103 -0
- package/src/tools/Chat/highlight/SpotlightCanvas.tsx +153 -0
- package/src/tools/Chat/highlight/__tests__/HighlightOverlay.test.tsx +112 -0
- package/src/tools/Chat/highlight/__tests__/resolveRef.test.ts +55 -0
- package/src/tools/Chat/highlight/index.ts +21 -0
- package/src/tools/Chat/highlight/resolveRef.ts +42 -0
- package/src/tools/Chat/highlight/types.ts +49 -0
- package/src/tools/Chat/highlight/useHighlightTargets.ts +128 -0
- package/src/tools/Chat/hooks/index.ts +0 -5
- package/src/tools/Chat/hooks/useAutoFocusOnStreamEnd.ts +28 -47
- package/src/tools/Chat/hooks/useChat.ts +47 -14
- package/src/tools/Chat/hooks/useChatComposer.ts +2 -2
- package/src/tools/Chat/hooks/useChatLayout.ts +1 -1
- package/src/tools/Chat/hooks/useStreamEndFocus.ts +54 -0
- package/src/tools/Chat/index.ts +25 -219
- package/src/tools/Chat/launcher/ChatDock.tsx +1 -1
- package/src/tools/Chat/launcher/ChatLauncher.tsx +1 -1
- package/src/tools/Chat/launcher/{ChatHeader.tsx → header/ChatHeader.tsx} +24 -11
- package/src/tools/Chat/launcher/{ChatHeaderActionButton.tsx → header/ChatHeaderActionButton.tsx} +34 -3
- package/src/tools/Chat/launcher/{ChatHeaderLanguageButton.tsx → header/ChatHeaderLanguageButton.tsx} +2 -2
- package/src/tools/Chat/launcher/{ChatHeaderModeToggle.tsx → header/ChatHeaderModeToggle.tsx} +1 -1
- package/src/tools/Chat/launcher/{ChatHeaderResetButton.tsx → header/ChatHeaderResetButton.tsx} +2 -1
- package/src/tools/Chat/launcher/{HeaderSlots.tsx → header/HeaderSlots.tsx} +3 -3
- package/src/tools/Chat/launcher/header/index.ts +26 -0
- package/src/tools/Chat/launcher/index.ts +3 -10
- package/src/tools/Chat/lazy.tsx +38 -284
- package/src/tools/Chat/{components → messages}/MessageBubble.tsx +58 -5
- package/src/tools/Chat/{components → messages}/MessageList.tsx +8 -25
- package/src/tools/Chat/messages/blocks/MessageBlocks.tsx +131 -0
- package/src/tools/Chat/messages/blocks/builtin.tsx +91 -0
- package/src/tools/Chat/messages/blocks/index.ts +12 -0
- package/src/tools/Chat/messages/blocks/registry.tsx +42 -0
- package/src/tools/Chat/messages/blocks/renderers/AudioBlock.tsx +20 -0
- package/src/tools/Chat/messages/blocks/renderers/CodeBlock.tsx +19 -0
- package/src/tools/Chat/messages/blocks/renderers/GalleryBlock.tsx +26 -0
- package/src/tools/Chat/messages/blocks/renderers/ImageBlock.tsx +27 -0
- package/src/tools/Chat/messages/blocks/renderers/JsonBlock.tsx +12 -0
- package/src/tools/Chat/messages/blocks/renderers/LottieBlock.tsx +11 -0
- package/src/tools/Chat/messages/blocks/renderers/MapBlock.tsx +36 -0
- package/src/tools/Chat/messages/blocks/renderers/MermaidBlock.tsx +11 -0
- package/src/tools/Chat/messages/blocks/renderers/VideoBlock.tsx +24 -0
- package/src/tools/Chat/messages/blocks/renderers/types.ts +8 -0
- package/src/tools/Chat/{components → messages}/index.ts +11 -5
- package/src/tools/Chat/public.ts +212 -0
- package/src/tools/Chat/shell/ChatRoot.tsx +326 -0
- package/src/tools/Chat/{components → shell}/EmptyState.tsx +4 -2
- package/src/tools/Chat/shell/index.ts +15 -0
- package/src/tools/Chat/types/block.ts +120 -0
- package/src/tools/Chat/types/config.ts +0 -5
- package/src/tools/Chat/types/index.ts +17 -0
- package/src/tools/Chat/types/message.ts +3 -0
- package/src/tools/Chat/utils/index.ts +4 -0
- package/src/tools/CodeEditor/README.md +4 -6
- package/src/tools/CodeEditor/components/DiffEditor.tsx +48 -13
- package/src/tools/CodeEditor/components/Editor.tsx +96 -44
- package/src/tools/CodeEditor/context/EditorProvider.tsx +34 -17
- package/src/tools/CodeEditor/hooks/useEditorTheme.ts +92 -99
- package/src/tools/CodeEditor/hooks/useMonaco.ts +37 -22
- package/src/tools/CodeEditor/lazy.tsx +6 -0
- package/src/tools/CodeEditor/lib/index.ts +1 -1
- package/src/tools/CodeEditor/lib/themes.ts +3 -39
- package/src/tools/CronScheduler/CronScheduler.client.tsx +230 -61
- package/src/tools/CronScheduler/components/CustomInput.tsx +21 -4
- package/src/tools/CronScheduler/components/DayChips.tsx +13 -11
- package/src/tools/CronScheduler/components/MonthDayGrid.tsx +4 -4
- package/src/tools/CronScheduler/components/SchedulePreview.tsx +7 -3
- package/src/tools/CronScheduler/components/TimeSelector.tsx +1 -1
- package/src/tools/CronScheduler/index.tsx +1 -1
- package/src/tools/CronScheduler/types/index.ts +8 -3
- package/src/tools/CronScheduler/utils/cron-humanize.ts +61 -16
- package/src/tools/CronScheduler/utils/cron-parser.ts +13 -4
- package/src/tools/FileIcon/FileIcon.tsx +24 -39
- package/src/tools/FileIcon/get-file-icon.ts +73 -0
- package/src/tools/FileIcon/icons/icon-data.ts +399 -0
- package/src/tools/FileIcon/index.ts +4 -0
- package/src/tools/FileIcon/loader.ts +17 -35
- package/src/tools/FileIcon/specialFolders.ts +18 -0
- package/src/tools/Gallery/components/lightbox/GalleryLightbox.tsx +112 -35
- package/src/tools/Gallery/components/media/GalleryVideo.tsx +21 -2
- package/src/tools/Gallery/components/preview/GalleryCarousel.tsx +11 -1
- package/src/tools/Gallery/hooks/usePreloadImages.ts +54 -7
- package/src/tools/ImageViewer/components/ImageInfo.tsx +12 -1
- package/src/tools/ImageViewer/components/ImageToolbar.tsx +51 -43
- package/src/tools/ImageViewer/components/ImageViewer.tsx +106 -26
- package/src/tools/ImageViewer/hooks/useImageLoading.ts +13 -0
- package/src/tools/ImageViewer/utils/constants.ts +3 -0
- package/src/tools/ImageViewer/utils/index.ts +1 -0
- package/src/tools/JsonForm/JsonSchemaForm.tsx +4 -1
- package/src/tools/JsonForm/templates/ArrayFieldTemplate.tsx +5 -3
- package/src/tools/JsonForm/templates/BaseInputTemplate.tsx +7 -4
- package/src/tools/JsonForm/templates/ErrorListTemplate.tsx +3 -1
- package/src/tools/JsonForm/templates/ObjectFieldTemplate.tsx +23 -3
- package/src/tools/JsonForm/widgets/ColorWidget.tsx +20 -12
- package/src/tools/JsonForm/widgets/NumberWidget.tsx +14 -9
- package/src/tools/JsonForm/widgets/RadioWidget.tsx +78 -0
- package/src/tools/JsonForm/widgets/SelectWidget.tsx +1 -0
- package/src/tools/JsonForm/widgets/SliderWidget.tsx +7 -4
- package/src/tools/JsonForm/widgets/TextWidget.tsx +41 -17
- package/src/tools/JsonForm/widgets/index.ts +1 -0
- package/src/tools/JsonTree/components/JsonContent.tsx +115 -40
- package/src/tools/LottiePlayer/LottiePlayer.client.tsx +177 -72
- package/src/tools/LottiePlayer/index.tsx +14 -4
- package/src/tools/LottiePlayer/lazy.tsx +11 -3
- package/src/tools/LottiePlayer/types.ts +31 -1
- package/src/tools/LottiePlayer/useLottie.ts +32 -9
- package/src/tools/LottiePlayer/usePrefersReducedMotion.ts +46 -0
- package/src/tools/Map/components/LayerSwitcher.tsx +54 -21
- package/src/tools/Map/components/MapCluster.tsx +28 -21
- package/src/tools/Map/components/MapContainer.tsx +11 -4
- package/src/tools/Map/components/MapLegend.tsx +46 -15
- package/src/tools/Map/components/MapMarker.tsx +31 -2
- package/src/tools/Map/hooks/useMapEvents.ts +64 -105
- package/src/tools/MarkdownEditor/MarkdownEditor.tsx +61 -6
- package/src/tools/MarkdownEditor/MentionList.tsx +37 -4
- package/src/tools/MarkdownEditor/createMentionSuggestion.ts +11 -0
- package/src/tools/MarkdownEditor/lazy.tsx +32 -7
- package/src/tools/MarkdownEditor/styles.css +13 -0
- package/src/tools/MarkdownMessage/CodeBlock.tsx +40 -17
- package/src/tools/MarkdownMessage/MarkdownMessage.tsx +26 -6
- package/src/tools/MarkdownMessage/components.tsx +22 -9
- package/src/tools/MarkdownMessage/types.ts +24 -1
- package/src/tools/Mermaid/Mermaid.client.tsx +27 -5
- package/src/tools/Mermaid/components/MermaidErrorPanel.tsx +31 -0
- package/src/tools/Mermaid/components/MermaidFullscreenModal.tsx +14 -17
- package/src/tools/Mermaid/hooks/useMermaidRenderer.ts +264 -168
- package/src/tools/Mermaid/hooks/useMermaidValidation.ts +76 -10
- package/src/tools/Mermaid/index.tsx +6 -0
- package/src/tools/Mermaid/utils/mermaid-helpers.ts +141 -18
- package/src/tools/OpenapiViewer/components/DocsLayout/EndpointDoc/SchemaFields/FieldRow.tsx +11 -1
- package/src/tools/OpenapiViewer/components/DocsLayout/EndpointDoc/SchemaFields/buildTree.ts +49 -20
- package/src/tools/OpenapiViewer/components/DocsLayout/EndpointDoc/index.tsx +7 -0
- package/src/tools/OpenapiViewer/components/DocsLayout/grouping.ts +7 -4
- package/src/tools/OpenapiViewer/constants.ts +3 -0
- package/src/tools/OpenapiViewer/hooks/useOpenApiSchema.ts +73 -11
- package/src/tools/OpenapiViewer/utils/schemaExport.ts +26 -6
- package/src/tools/PrettyCode/PrettyCode.client.tsx +23 -16
- package/src/tools/PrettyCode/lazy.tsx +1 -1
- package/src/tools/SpeechRecognition/README.md +1 -1
- package/src/tools/SpeechRecognition/__tests__/language.test.ts +9 -3
- package/src/tools/SpeechRecognition/components/RecordingPulse.tsx +59 -0
- package/src/tools/SpeechRecognition/components/index.ts +2 -0
- package/src/tools/SpeechRecognition/core/engine/external.ts +24 -7
- package/src/tools/SpeechRecognition/core/language.ts +23 -6
- package/src/tools/SpeechRecognition/hooks/usePushToTalk.ts +36 -5
- package/src/tools/SpeechRecognition/hooks/useSpeechRecognition.ts +18 -11
- package/src/tools/SpeechRecognition/widgets/VoiceComposerSlot.tsx +94 -26
- package/src/tools/SpeechRecognition/widgets/index.ts +1 -1
- package/src/tools/Tree/README.md +4 -8
- package/src/tools/Tree/TreeRoot.tsx +22 -10
- package/src/tools/Tree/components/TreeContent.tsx +24 -4
- package/src/tools/Tree/components/TreeLabel.tsx +8 -2
- package/src/tools/Tree/components/TreeRow.tsx +16 -6
- package/src/tools/Tree/data/flatten.ts +10 -4
- package/src/tools/Tree/types.ts +4 -0
- package/src/tools/Uploader/components/UploadAddButton.tsx +29 -6
- package/src/tools/Uploader/components/UploadDropzone.tsx +63 -7
- package/src/tools/Uploader/components/UploadPageDropOverlay.tsx +19 -5
- package/src/tools/Uploader/components/UploadPreviewItem.tsx +47 -17
- package/src/tools/Uploader/components/UploadPreviewList.tsx +24 -12
- package/src/tools/Uploader/utils/formatters.ts +8 -3
- package/src/tools/VideoPlayer/README.md +87 -230
- package/src/tools/VideoPlayer/VideoPlayer.tsx +82 -0
- package/src/tools/VideoPlayer/canvas/canvas-dispatcher.tsx +34 -0
- package/src/tools/VideoPlayer/canvas/hls-canvas.tsx +39 -0
- package/src/tools/VideoPlayer/canvas/iframe-canvas.tsx +33 -0
- package/src/tools/VideoPlayer/canvas/index.ts +12 -0
- package/src/tools/VideoPlayer/canvas/jsx-augmentation.ts +47 -0
- package/src/tools/VideoPlayer/canvas/native-canvas.tsx +38 -0
- package/src/tools/VideoPlayer/canvas/vimeo-canvas.tsx +40 -0
- package/src/tools/VideoPlayer/canvas/youtube-canvas.tsx +78 -0
- package/src/tools/VideoPlayer/index.ts +51 -65
- package/src/tools/VideoPlayer/lazy.tsx +11 -54
- package/src/tools/VideoPlayer/parts/controls-bar.tsx +35 -0
- package/src/tools/VideoPlayer/parts/fullscreen.tsx +19 -0
- package/src/tools/VideoPlayer/parts/index.ts +15 -0
- package/src/tools/VideoPlayer/parts/pip.tsx +19 -0
- package/src/tools/VideoPlayer/parts/play-button.tsx +19 -0
- package/src/tools/VideoPlayer/parts/playback-rate.tsx +31 -0
- package/src/tools/VideoPlayer/parts/poster.tsx +3 -0
- package/src/tools/VideoPlayer/parts/seek-bar.tsx +26 -0
- package/src/tools/VideoPlayer/parts/volume.tsx +32 -0
- package/src/tools/VideoPlayer/styles/video-player.css +141 -0
- package/src/tools/VideoPlayer/types.ts +82 -0
- package/src/tools/VideoPlayer/utils/parse-embed-url.ts +70 -0
- package/src/tools/VideoPlayer/utils/vimeo-id.ts +24 -0
- package/src/tools/VideoPlayer/utils/youtube-id.ts +64 -0
- package/src/tools/index.ts +37 -29
- package/src/tools/Chat/components/AudioToggle.tsx +0 -78
- package/src/tools/Chat/components/ChatRoot.tsx +0 -305
- package/src/tools/Chat/components/Composer.tsx +0 -216
- package/src/tools/Chat/hooks/useChatScroll.ts +0 -145
- package/src/tools/Chat/types.ts +0 -9
- package/src/tools/JsonTree/components/JsonToolbar.tsx +0 -95
- package/src/tools/JsonTree/hooks/useElementCorner.ts +0 -84
- package/src/tools/JsonTree/hooks/useNavbarHeight.ts +0 -83
- package/src/tools/OpenapiViewer/components/DocsLayout/schemaFields.ts +0 -121
- package/src/tools/Tour/README.md +0 -373
- package/src/tools/Tour/components/Tour.tsx +0 -12
- package/src/tools/Tour/components/TourContent.tsx +0 -171
- package/src/tools/Tour/components/TourNavigation.tsx +0 -77
- package/src/tools/Tour/components/TourProgress.tsx +0 -88
- package/src/tools/Tour/components/TourSpotlight.tsx +0 -199
- package/src/tools/Tour/components/index.ts +0 -5
- package/src/tools/Tour/context/TourContext.ts +0 -19
- package/src/tools/Tour/context/TourProvider.tsx +0 -292
- package/src/tools/Tour/context/index.ts +0 -2
- package/src/tools/Tour/hooks/index.ts +0 -3
- package/src/tools/Tour/hooks/useKeyboardNavigation.ts +0 -59
- package/src/tools/Tour/hooks/useStepTarget.ts +0 -121
- package/src/tools/Tour/hooks/useTour.ts +0 -42
- package/src/tools/Tour/index.ts +0 -38
- package/src/tools/Tour/types/index.ts +0 -224
- package/src/tools/Tour/utils/dom.ts +0 -98
- package/src/tools/Tour/utils/index.ts +0 -3
- package/src/tools/Tour/utils/logger.ts +0 -3
- package/src/tools/Tour/utils/scrollIntoView.ts +0 -24
- package/src/tools/VideoPlayer/components/VideoControls.tsx +0 -138
- package/src/tools/VideoPlayer/components/VideoErrorFallback.tsx +0 -172
- package/src/tools/VideoPlayer/components/VideoPlayer.tsx +0 -201
- package/src/tools/VideoPlayer/components/index.ts +0 -14
- package/src/tools/VideoPlayer/context/VideoPlayerContext.tsx +0 -52
- package/src/tools/VideoPlayer/context/index.ts +0 -8
- package/src/tools/VideoPlayer/hooks/index.ts +0 -12
- package/src/tools/VideoPlayer/hooks/useVideoPlayerSettings.ts +0 -71
- package/src/tools/VideoPlayer/hooks/useVideoPositionCache.ts +0 -117
- package/src/tools/VideoPlayer/providers/NativeProvider.tsx +0 -284
- package/src/tools/VideoPlayer/providers/StreamProvider.tsx +0 -505
- package/src/tools/VideoPlayer/providers/VidstackProvider.tsx +0 -397
- package/src/tools/VideoPlayer/providers/index.ts +0 -8
- package/src/tools/VideoPlayer/types/index.ts +0 -38
- package/src/tools/VideoPlayer/types/player.ts +0 -116
- package/src/tools/VideoPlayer/types/provider.ts +0 -93
- package/src/tools/VideoPlayer/types/sources.ts +0 -97
- package/src/tools/VideoPlayer/utils/debug.ts +0 -14
- package/src/tools/VideoPlayer/utils/fileSource.ts +0 -78
- package/src/tools/VideoPlayer/utils/index.ts +0 -12
- package/src/tools/VideoPlayer/utils/resolvers.ts +0 -75
- /package/src/tools/Chat/{config.ts → constants.ts} +0 -0
- /package/src/tools/Chat/launcher/{ChatHeaderAudioToggle.tsx → header/ChatHeaderAudioToggle.tsx} +0 -0
- /package/src/tools/Chat/{components → messages}/Attachments.tsx +0 -0
- /package/src/tools/Chat/{components → messages}/JumpToLatest.tsx +0 -0
- /package/src/tools/Chat/{components → messages}/MessageActions.tsx +0 -0
- /package/src/tools/Chat/{components → messages}/Sources.tsx +0 -0
- /package/src/tools/Chat/{components → messages}/StreamingIndicator.tsx +0 -0
- /package/src/tools/Chat/{components → messages}/ToolCalls.tsx +0 -0
- /package/src/tools/Chat/{components → shell}/ErrorBanner.tsx +0 -0
|
@@ -2,33 +2,156 @@
|
|
|
2
2
|
* Helper utilities for Mermaid diagram rendering
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
/**
|
|
6
|
+
* Read a semantic color token from CSS custom properties.
|
|
7
|
+
*
|
|
8
|
+
* Theme tokens (`light.css` / `dark.css`) ship fully-wrapped CSS colors
|
|
9
|
+
* (`hsl(0 0% 94%)`) — see `ui-core/src/styles/theme/tokens.css`. Older
|
|
10
|
+
* tokens stored bare HSL components (`0 0% 94%`), so this helper handles
|
|
11
|
+
* both: complete colors pass through untouched, bare components get
|
|
12
|
+
* wrapped once. Never double-wrap an already-complete color.
|
|
13
|
+
*
|
|
14
|
+
* @param variable CSS custom property name, e.g. `--foreground`.
|
|
15
|
+
* @param fallback Returned when the variable is empty / unavailable.
|
|
16
|
+
*/
|
|
17
|
+
export const getThemeColor = (variable: string, fallback = ''): string => {
|
|
18
|
+
if (typeof document === 'undefined') return fallback;
|
|
19
|
+
const value = getComputedStyle(document.documentElement)
|
|
20
|
+
.getPropertyValue(variable)
|
|
21
|
+
.trim();
|
|
22
|
+
if (!value) return fallback;
|
|
23
|
+
// Already a complete color (hex / rgb / hsl / oklch / named).
|
|
24
|
+
if (
|
|
25
|
+
value.startsWith('#') ||
|
|
26
|
+
value.startsWith('rgb') ||
|
|
27
|
+
value.startsWith('hsl(') ||
|
|
28
|
+
value.startsWith('oklch') ||
|
|
29
|
+
value.startsWith('oklab') ||
|
|
30
|
+
value.startsWith('color(') ||
|
|
31
|
+
value.startsWith('var(')
|
|
32
|
+
) {
|
|
33
|
+
return value;
|
|
34
|
+
}
|
|
35
|
+
// Bare HSL components — wrap once.
|
|
36
|
+
return `hsl(${value})`;
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
/** Resolve the diagram text color for the given theme. */
|
|
40
|
+
export const getTextColor = (theme: string): string =>
|
|
41
|
+
theme === 'dark'
|
|
42
|
+
? getThemeColor('--foreground', 'hsl(0 0% 98%)')
|
|
43
|
+
: getThemeColor('--foreground', 'hsl(222.2 84% 4.9%)');
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Diagram types whose labels sit on per-section colored backgrounds
|
|
47
|
+
* (timeline sections, journey tasks, pie slices, mindmap nodes, gitgraph
|
|
48
|
+
* commits). For these, Mermaid already picks a contrasting label color
|
|
49
|
+
* from the `cScaleLabel*` / `pie*` theme variables — re-asserting a single
|
|
50
|
+
* `--foreground` would put dark text on dark boxes (and vice versa).
|
|
51
|
+
*
|
|
52
|
+
* We detect them via the wrapper class Mermaid puts on the root `<g>` /
|
|
53
|
+
* `<svg>` and skip the blanket text override for those SVGs.
|
|
54
|
+
*/
|
|
55
|
+
const SECTION_COLORED_SELECTORS = [
|
|
56
|
+
'.timeline',
|
|
57
|
+
'.mindmap',
|
|
58
|
+
'[id^="mermaid"][aria-roledescription="timeline"]',
|
|
59
|
+
'[id^="mermaid"][aria-roledescription="journey"]',
|
|
60
|
+
'[id^="mermaid"][aria-roledescription="mindmap"]',
|
|
61
|
+
'[id^="mermaid"][aria-roledescription="pie"]',
|
|
62
|
+
];
|
|
63
|
+
|
|
64
|
+
/** True when the SVG renders a diagram type that colors its own labels. */
|
|
65
|
+
const usesSectionColors = (svg: SVGSVGElement): boolean => {
|
|
66
|
+
const role = svg.getAttribute('aria-roledescription') ?? '';
|
|
67
|
+
if (['timeline', 'journey', 'mindmap', 'pie'].includes(role)) return true;
|
|
68
|
+
return SECTION_COLORED_SELECTORS.some((sel) => svg.querySelector(sel) !== null);
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Apply theme text colors to a rendered Mermaid SVG.
|
|
73
|
+
*
|
|
74
|
+
* Mermaid's `base` theme bakes `themeVariables` at render time, but text
|
|
75
|
+
* fill on `<text>` nodes and `color` on foreignObject labels can drift
|
|
76
|
+
* from our tokens — this re-asserts them after render.
|
|
77
|
+
*
|
|
78
|
+
* Diagrams that color their own labels per section (timeline, journey,
|
|
79
|
+
* mindmap, pie) are skipped: their `themeVariables` already encode
|
|
80
|
+
* contrasting label colors, so a blanket override would re-introduce the
|
|
81
|
+
* dark-text-on-dark-box problem.
|
|
82
|
+
*/
|
|
83
|
+
export const applyMermaidTextColors = (container: HTMLElement, textColor: string): void => {
|
|
7
84
|
const svgElement = container.querySelector('svg');
|
|
8
|
-
if (svgElement)
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
85
|
+
if (!svgElement) return;
|
|
86
|
+
if (usesSectionColors(svgElement)) return;
|
|
87
|
+
// SVG text elements use 'fill'.
|
|
88
|
+
svgElement.querySelectorAll('text').forEach((el) => {
|
|
89
|
+
(el as SVGElement).style.fill = textColor;
|
|
90
|
+
});
|
|
91
|
+
// HTML elements inside foreignObject use 'color'.
|
|
92
|
+
svgElement.querySelectorAll('.nodeLabel, .edgeLabel').forEach((el) => {
|
|
93
|
+
(el as HTMLElement).style.color = textColor;
|
|
94
|
+
});
|
|
95
|
+
};
|
|
13
96
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
97
|
+
/**
|
|
98
|
+
* Re-color ER diagram attribute-row backgrounds.
|
|
99
|
+
*
|
|
100
|
+
* Mermaid derives the zebra-stripe fills for `.row-rect-odd` /
|
|
101
|
+
* `.row-rect-even` by lightening `mainBkg` — in dark mode the odd stripe
|
|
102
|
+
* lands on a light gray (`hsl(0 0% 83%)`) while the attribute text stays
|
|
103
|
+
* white, so it vanishes. `themeVariables` has no hook for these, so we
|
|
104
|
+
* re-assert themed fills after render.
|
|
105
|
+
*
|
|
106
|
+
* @param container Host element holding the rendered SVG.
|
|
107
|
+
* @param oddFill Background for odd attribute rows.
|
|
108
|
+
* @param evenFill Background for even attribute rows.
|
|
109
|
+
*/
|
|
110
|
+
export const applyMermaidErRowColors = (
|
|
111
|
+
container: HTMLElement,
|
|
112
|
+
oddFill: string,
|
|
113
|
+
evenFill: string,
|
|
114
|
+
): void => {
|
|
115
|
+
const svgElement = container.querySelector('svg');
|
|
116
|
+
if (!svgElement) return;
|
|
117
|
+
svgElement.querySelectorAll('.row-rect-odd path').forEach((el) => {
|
|
118
|
+
const fill = (el as SVGElement).getAttribute('fill');
|
|
119
|
+
if (fill && fill !== 'none') (el as SVGElement).setAttribute('fill', oddFill);
|
|
120
|
+
});
|
|
121
|
+
svgElement.querySelectorAll('.row-rect-even path').forEach((el) => {
|
|
122
|
+
const fill = (el as SVGElement).getAttribute('fill');
|
|
123
|
+
if (fill && fill !== 'none') (el as SVGElement).setAttribute('fill', evenFill);
|
|
124
|
+
});
|
|
19
125
|
};
|
|
20
126
|
|
|
21
|
-
|
|
127
|
+
/**
|
|
128
|
+
* Detect whether a diagram is vertical (tall and narrow).
|
|
129
|
+
*
|
|
130
|
+
* Used to pick a sensible fullscreen fit. Prefers the `viewBox` (stable,
|
|
131
|
+
* available immediately) and falls back to `getBBox()` only when needed.
|
|
132
|
+
*/
|
|
22
133
|
export const isVerticalDiagram = (svgElement: SVGSVGElement): boolean => {
|
|
23
134
|
const viewBox = svgElement.getAttribute('viewBox');
|
|
24
135
|
if (viewBox) {
|
|
25
|
-
const [, , width, height] = viewBox.split(
|
|
26
|
-
if (
|
|
136
|
+
const [, , width, height] = viewBox.split(/\s+/).map(Number);
|
|
137
|
+
if (
|
|
138
|
+
width === undefined ||
|
|
139
|
+
height === undefined ||
|
|
140
|
+
!Number.isFinite(width) ||
|
|
141
|
+
!Number.isFinite(height) ||
|
|
142
|
+
width <= 0
|
|
143
|
+
) {
|
|
144
|
+
return false;
|
|
145
|
+
}
|
|
27
146
|
return height > width * 1.5;
|
|
28
147
|
}
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
148
|
+
try {
|
|
149
|
+
const bbox = svgElement.getBBox?.();
|
|
150
|
+
if (bbox && bbox.width > 0) {
|
|
151
|
+
return bbox.height > bbox.width * 1.5;
|
|
152
|
+
}
|
|
153
|
+
} catch {
|
|
154
|
+
// getBBox throws if the SVG is not attached / not rendered.
|
|
32
155
|
}
|
|
33
156
|
return false;
|
|
34
157
|
};
|
|
@@ -35,6 +35,8 @@ export function FieldRow({ field, depth, showTreeLine = true }: FieldRowProps) {
|
|
|
35
35
|
// at smaller text sizes.
|
|
36
36
|
const padLeft = showTreeLine ? depth * 14 : 0;
|
|
37
37
|
|
|
38
|
+
const toggle = () => isExpandable && setOpen((v) => !v);
|
|
39
|
+
|
|
38
40
|
return (
|
|
39
41
|
<div className="bg-background">
|
|
40
42
|
<div
|
|
@@ -43,8 +45,16 @@ export function FieldRow({ field, depth, showTreeLine = true }: FieldRowProps) {
|
|
|
43
45
|
isExpandable && 'cursor-pointer hover:bg-muted/30',
|
|
44
46
|
)}
|
|
45
47
|
style={{ paddingLeft: 12 + padLeft }}
|
|
46
|
-
onClick={
|
|
48
|
+
onClick={toggle}
|
|
49
|
+
onKeyDown={(e) => {
|
|
50
|
+
if (!isExpandable) return;
|
|
51
|
+
if (e.key === 'Enter' || e.key === ' ') {
|
|
52
|
+
e.preventDefault();
|
|
53
|
+
toggle();
|
|
54
|
+
}
|
|
55
|
+
}}
|
|
47
56
|
role={isExpandable ? 'button' : undefined}
|
|
57
|
+
tabIndex={isExpandable ? 0 : undefined}
|
|
48
58
|
aria-expanded={isExpandable ? open : undefined}
|
|
49
59
|
>
|
|
50
60
|
<ChevronRight
|
|
@@ -3,11 +3,12 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Handles the OpenAPI 3.x subset we actually see: objects with
|
|
5
5
|
* ``properties`` + ``required``, arrays with ``items``, enums,
|
|
6
|
-
* primitives with ``format``. ``allOf``
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
6
|
+
* primitives with ``format``. ``allOf`` is merged shallowly.
|
|
7
|
+
* ``oneOf`` / ``anyOf`` object branches are merged so every variant's
|
|
8
|
+
* fields are visible (union of properties); ``required`` is intersected
|
|
9
|
+
* so a field optional in one variant is not shown as globally required.
|
|
10
|
+
* Non-object unions fall back to the first branch. Upstream
|
|
11
|
+
* dereferencing (``dereferenceSchema``) resolves ``$ref`` before this runs.
|
|
11
12
|
*/
|
|
12
13
|
|
|
13
14
|
import type { FieldKind, FieldNode } from './types';
|
|
@@ -29,19 +30,40 @@ type JsonSchemaNode = Record<string, unknown> & {
|
|
|
29
30
|
* call stack. Anything this deep is unreadable in a docs view anyway. */
|
|
30
31
|
const MAX_DEPTH = 5;
|
|
31
32
|
|
|
32
|
-
/** Merge
|
|
33
|
-
*
|
|
34
|
-
*
|
|
35
|
-
|
|
33
|
+
/** Merge object branches into a single pseudo-object. Shallow merge is
|
|
34
|
+
* enough for display — deep merge would need semantic JSON Schema
|
|
35
|
+
* reasoning we don't want in a read-only viewer.
|
|
36
|
+
*
|
|
37
|
+
* ``intersectRequired`` controls how ``required`` combines: ``allOf`` is
|
|
38
|
+
* a conjunction so every branch's required fields apply (union); for
|
|
39
|
+
* ``oneOf`` / ``anyOf`` a field is only truly required when *every*
|
|
40
|
+
* object branch requires it (intersection). */
|
|
41
|
+
function mergeObjectBranches(
|
|
42
|
+
branches: JsonSchemaNode[],
|
|
43
|
+
intersectRequired: boolean,
|
|
44
|
+
): JsonSchemaNode {
|
|
36
45
|
const properties: Record<string, JsonSchemaNode> = {};
|
|
37
|
-
const
|
|
38
|
-
|
|
46
|
+
const objectBranches = branches.filter((b) => b.properties);
|
|
47
|
+
const requiredSets = objectBranches.map((b) => new Set(b.required ?? []));
|
|
48
|
+
for (const b of objectBranches) {
|
|
39
49
|
if (b.properties) Object.assign(properties, b.properties);
|
|
40
|
-
|
|
50
|
+
}
|
|
51
|
+
let required: string[];
|
|
52
|
+
if (intersectRequired && requiredSets.length > 0) {
|
|
53
|
+
const [first, ...rest] = requiredSets;
|
|
54
|
+
required = [...first!].filter((k) => rest.every((s) => s.has(k)));
|
|
55
|
+
} else {
|
|
56
|
+
required = [...new Set(requiredSets.flatMap((s) => [...s]))];
|
|
41
57
|
}
|
|
42
58
|
return { type: 'object', properties, required };
|
|
43
59
|
}
|
|
44
60
|
|
|
61
|
+
/** True when every branch is (or describes) an object — i.e. the union
|
|
62
|
+
* can be presented as a merged property table. */
|
|
63
|
+
function allObjectBranches(branches: JsonSchemaNode[]): boolean {
|
|
64
|
+
return branches.every((b) => b.type === 'object' || Boolean(b.properties));
|
|
65
|
+
}
|
|
66
|
+
|
|
45
67
|
function describeType(node: JsonSchemaNode): { label: string; kind: FieldKind } {
|
|
46
68
|
if (node.type === 'array') {
|
|
47
69
|
const itemLabel = node.items ? describeType(node.items).label : 'any';
|
|
@@ -61,16 +83,23 @@ function describeType(node: JsonSchemaNode): { label: string; kind: FieldKind }
|
|
|
61
83
|
}
|
|
62
84
|
|
|
63
85
|
function resolveCombinators(node: JsonSchemaNode): JsonSchemaNode {
|
|
64
|
-
// ``allOf`` → merge
|
|
65
|
-
// branch (see module docstring rationale).
|
|
86
|
+
// ``allOf`` → conjunction merge (required is a union).
|
|
66
87
|
if (Array.isArray(node.allOf) && node.allOf.length > 0) {
|
|
67
|
-
return { ...
|
|
68
|
-
}
|
|
69
|
-
if (Array.isArray(node.oneOf) && node.oneOf.length > 0) {
|
|
70
|
-
return { ...node.oneOf[0]!, description: node.description ?? node.oneOf[0]!.description };
|
|
88
|
+
return { ...mergeObjectBranches(node.allOf, false), description: node.description };
|
|
71
89
|
}
|
|
72
|
-
|
|
73
|
-
|
|
90
|
+
// ``oneOf`` / ``anyOf`` of objects → merge so every variant's fields
|
|
91
|
+
// are visible; required intersected. Non-object unions → first branch.
|
|
92
|
+
for (const key of ['oneOf', 'anyOf'] as const) {
|
|
93
|
+
const branches = node[key];
|
|
94
|
+
if (Array.isArray(branches) && branches.length > 0) {
|
|
95
|
+
if (branches.length > 1 && allObjectBranches(branches)) {
|
|
96
|
+
return { ...mergeObjectBranches(branches, true), description: node.description };
|
|
97
|
+
}
|
|
98
|
+
return {
|
|
99
|
+
...branches[0]!,
|
|
100
|
+
description: node.description ?? branches[0]!.description,
|
|
101
|
+
};
|
|
102
|
+
}
|
|
74
103
|
}
|
|
75
104
|
return node;
|
|
76
105
|
}
|
|
@@ -53,6 +53,13 @@ export function EndpointDoc({ endpoint, isLoadedInPlayground, onTryIt, schemaId
|
|
|
53
53
|
data-endpoint-anchor={anchor}
|
|
54
54
|
data-schema-id={scopedSchemaId ?? ''}
|
|
55
55
|
className="scroll-mt-24 py-10 first:pt-0"
|
|
56
|
+
// ``content-visibility: auto`` lets the browser skip layout
|
|
57
|
+
// + paint for endpoint cards that are off-screen — a big
|
|
58
|
+
// win for large specs (hundreds of endpoints) with no
|
|
59
|
+
// behavioural change. ``contain-intrinsic-size`` supplies a
|
|
60
|
+
// height estimate so the scrollbar stays stable before a
|
|
61
|
+
// card is first rendered.
|
|
62
|
+
style={{ contentVisibility: 'auto', containIntrinsicSize: 'auto 480px' }}
|
|
56
63
|
>
|
|
57
64
|
<EndpointHeader
|
|
58
65
|
endpoint={endpoint}
|
|
@@ -30,10 +30,13 @@ export type SchemaSection = {
|
|
|
30
30
|
|
|
31
31
|
const METHOD_ORDER: Record<string, number> = {
|
|
32
32
|
GET: 0,
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
33
|
+
HEAD: 1,
|
|
34
|
+
POST: 2,
|
|
35
|
+
PUT: 3,
|
|
36
|
+
PATCH: 4,
|
|
37
|
+
DELETE: 5,
|
|
38
|
+
OPTIONS: 6,
|
|
39
|
+
TRACE: 7,
|
|
37
40
|
};
|
|
38
41
|
|
|
39
42
|
const methodRank = (ep: ApiEndpoint) => METHOD_ORDER[ep.method] ?? 99;
|
|
@@ -10,8 +10,10 @@ import { joinUrl, resolveBaseUrl } from '../utils/url';
|
|
|
10
10
|
|
|
11
11
|
type JsonSchemaNode = Record<string, unknown>;
|
|
12
12
|
|
|
13
|
-
// HTTP methods to extract from OpenAPI schema
|
|
14
|
-
|
|
13
|
+
// HTTP methods to extract from OpenAPI schema. Covers the full set of
|
|
14
|
+
// OpenAPI Operation Object keys — ``head`` / ``options`` / ``trace`` are
|
|
15
|
+
// rarer but valid, and silently dropping them hid real endpoints.
|
|
16
|
+
const HTTP_METHODS = ['get', 'post', 'put', 'patch', 'delete', 'head', 'options', 'trace'] as const;
|
|
15
17
|
|
|
16
18
|
// Extract endpoints from OpenAPI schema (all methods). ``baseUrl`` is
|
|
17
19
|
// resolved by the caller via ``resolveBaseUrl`` — we just paste it onto
|
|
@@ -79,14 +81,19 @@ const extractEndpoints = (
|
|
|
79
81
|
const chosen = chosenContentType ? respContent?.[chosenContentType] : undefined;
|
|
80
82
|
const respSchema = chosen?.schema as JsonSchemaNode | undefined;
|
|
81
83
|
|
|
84
|
+
// Hand-written example wins over a synthesised one.
|
|
85
|
+
const explicit = stringifyExample(explicitMediaExample(chosen));
|
|
86
|
+
|
|
82
87
|
responses.push({
|
|
83
88
|
code,
|
|
84
89
|
description: (response as any).description || `Response ${code}`,
|
|
85
90
|
contentType: chosenContentType,
|
|
86
91
|
schema: respSchema,
|
|
87
|
-
example:
|
|
88
|
-
|
|
89
|
-
|
|
92
|
+
example:
|
|
93
|
+
explicit ??
|
|
94
|
+
(respSchema
|
|
95
|
+
? sampleSchemaJson(respSchema, { skipWriteOnly: true }, specRoot)
|
|
96
|
+
: undefined),
|
|
90
97
|
});
|
|
91
98
|
}
|
|
92
99
|
}
|
|
@@ -102,13 +109,17 @@ const extractEndpoints = (
|
|
|
102
109
|
const content = op.requestBody.content;
|
|
103
110
|
const mediaType = content?.['application/json'] || content?.[Object.keys(content || {})[0]];
|
|
104
111
|
const rawSchema = mediaType?.schema as JsonSchemaNode | undefined;
|
|
112
|
+
// Hand-written example wins over a synthesised one.
|
|
113
|
+
const explicit = stringifyExample(explicitMediaExample(mediaType));
|
|
105
114
|
requestBody = {
|
|
106
115
|
type: (rawSchema?.type as string | undefined) || 'object',
|
|
107
116
|
description: op.requestBody.description,
|
|
108
117
|
schema: rawSchema,
|
|
109
|
-
example:
|
|
110
|
-
|
|
111
|
-
|
|
118
|
+
example:
|
|
119
|
+
explicit ??
|
|
120
|
+
(rawSchema
|
|
121
|
+
? sampleSchemaJson(rawSchema, { skipReadOnly: true }, specRoot)
|
|
122
|
+
: undefined),
|
|
112
123
|
};
|
|
113
124
|
}
|
|
114
125
|
|
|
@@ -132,6 +143,34 @@ const extractEndpoints = (
|
|
|
132
143
|
return endpoints;
|
|
133
144
|
};
|
|
134
145
|
|
|
146
|
+
// Pull a hand-written example off a media-type object, if present.
|
|
147
|
+
// OpenAPI lets authors attach a canonical example two ways:
|
|
148
|
+
// - ``example`` — a single inline value (3.0 + 3.1).
|
|
149
|
+
// - ``examples`` — a named map of Example Objects (3.0 + 3.1); we use
|
|
150
|
+
// the first entry's ``value``.
|
|
151
|
+
// A hand-written example is more trustworthy than a synthesised one, so
|
|
152
|
+
// callers prefer this over ``openapi-sampler`` output.
|
|
153
|
+
const explicitMediaExample = (media: Record<string, unknown> | undefined): unknown => {
|
|
154
|
+
if (!media) return undefined;
|
|
155
|
+
if (media.example !== undefined) return media.example;
|
|
156
|
+
const examples = media.examples as Record<string, { value?: unknown }> | undefined;
|
|
157
|
+
if (examples && typeof examples === 'object') {
|
|
158
|
+
for (const ex of Object.values(examples)) {
|
|
159
|
+
if (ex && typeof ex === 'object' && 'value' in ex) return ex.value;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
return undefined;
|
|
163
|
+
};
|
|
164
|
+
|
|
165
|
+
const stringifyExample = (value: unknown): string | undefined => {
|
|
166
|
+
if (value === undefined) return undefined;
|
|
167
|
+
try {
|
|
168
|
+
return JSON.stringify(value, null, 2);
|
|
169
|
+
} catch {
|
|
170
|
+
return undefined;
|
|
171
|
+
}
|
|
172
|
+
};
|
|
173
|
+
|
|
135
174
|
// Get unique categories from endpoints
|
|
136
175
|
const getCategories = (endpoints: ApiEndpoint[]): string[] => {
|
|
137
176
|
const categories = new Set<string>();
|
|
@@ -139,7 +178,9 @@ const getCategories = (endpoints: ApiEndpoint[]): string[] => {
|
|
|
139
178
|
return Array.from(categories).sort();
|
|
140
179
|
};
|
|
141
180
|
|
|
142
|
-
// Fetch schema from URL
|
|
181
|
+
// Fetch schema from URL. Parses JSON explicitly (rather than
|
|
182
|
+
// ``response.json()``) so a server that returns HTML or YAML yields a
|
|
183
|
+
// readable error instead of an opaque ``SyntaxError: Unexpected token``.
|
|
143
184
|
const fetchSchema = async (url: string): Promise<OpenApiSchema> => {
|
|
144
185
|
const response = await fetch(url, {
|
|
145
186
|
headers: {
|
|
@@ -147,9 +188,19 @@ const fetchSchema = async (url: string): Promise<OpenApiSchema> => {
|
|
|
147
188
|
},
|
|
148
189
|
});
|
|
149
190
|
if (!response.ok) {
|
|
150
|
-
throw new Error(`Failed to fetch schema
|
|
191
|
+
throw new Error(`Failed to fetch schema (HTTP ${response.status} ${response.statusText})`);
|
|
192
|
+
}
|
|
193
|
+
const text = await response.text();
|
|
194
|
+
try {
|
|
195
|
+
return JSON.parse(text) as OpenApiSchema;
|
|
196
|
+
} catch {
|
|
197
|
+
const looksYaml = /\.ya?ml($|\?)/i.test(url) || /^\s*openapi\s*:/m.test(text);
|
|
198
|
+
throw new Error(
|
|
199
|
+
looksYaml
|
|
200
|
+
? 'Schema appears to be YAML — only JSON OpenAPI documents are supported.'
|
|
201
|
+
: 'Schema response is not valid JSON.',
|
|
202
|
+
);
|
|
151
203
|
}
|
|
152
|
-
return response.json();
|
|
153
204
|
};
|
|
154
205
|
|
|
155
206
|
interface UseOpenApiSchemaProps {
|
|
@@ -269,17 +320,28 @@ export default function useOpenApiSchema({
|
|
|
269
320
|
return;
|
|
270
321
|
}
|
|
271
322
|
|
|
323
|
+
// Guard against a stale fetch resolving after the user switched
|
|
324
|
+
// schemas — without it a slow earlier request could flip ``loading``
|
|
325
|
+
// off (or surface an error) for a schema that is no longer current.
|
|
326
|
+
let cancelled = false;
|
|
327
|
+
|
|
272
328
|
fetchSchema(currentSchema.url)
|
|
273
329
|
.then((schema) => {
|
|
330
|
+
if (cancelled) return;
|
|
274
331
|
setLoadedSchemas((prev) => new Map(prev).set(currentSchema.id, schema));
|
|
275
332
|
consola.success(`Schema loaded: ${currentSchema.name}`);
|
|
276
333
|
setLoading(false);
|
|
277
334
|
})
|
|
278
335
|
.catch((err) => {
|
|
336
|
+
if (cancelled) return;
|
|
279
337
|
consola.error(`Error loading schema from ${currentSchema.url}:`, err);
|
|
280
338
|
setError(err instanceof Error ? err.message : 'Failed to load schema');
|
|
281
339
|
setLoading(false);
|
|
282
340
|
});
|
|
341
|
+
|
|
342
|
+
return () => {
|
|
343
|
+
cancelled = true;
|
|
344
|
+
};
|
|
283
345
|
}, [currentSchema, loadedSchemas, preloadAll]);
|
|
284
346
|
|
|
285
347
|
// Preload every schema (sections-grouping mode). Each schema is fetched
|
|
@@ -23,17 +23,28 @@ import { relativePath, resolveBaseUrl } from './url';
|
|
|
23
23
|
|
|
24
24
|
/**
|
|
25
25
|
* Walk a JSON object and replace every ``$ref`` with the resolved value.
|
|
26
|
-
*
|
|
27
|
-
*
|
|
26
|
+
*
|
|
27
|
+
* Cycle handling: each ``$ref`` currently being expanded is tracked on an
|
|
28
|
+
* active set. If a ref points back into a ref already on the stack, the
|
|
29
|
+
* ``$ref`` node is left intact instead of being inlined — downstream
|
|
30
|
+
* consumers (``openapi-sampler``) resolve it against the raw spec, so a
|
|
31
|
+
* left-over local ref is harmless and far cheaper than inlining a
|
|
32
|
+
* recursive type ``maxDepth`` levels deep. ``maxDepth`` is still a hard
|
|
33
|
+
* backstop for pathologically wide graphs.
|
|
28
34
|
*/
|
|
29
|
-
export function dereferenceSchema(schema: OpenApiSchema, maxDepth =
|
|
35
|
+
export function dereferenceSchema(schema: OpenApiSchema, maxDepth = 50): OpenApiSchema {
|
|
30
36
|
const root = schema as unknown as Record<string, unknown>;
|
|
31
37
|
|
|
32
38
|
function resolveRef(ref: string): unknown {
|
|
33
39
|
// Only handle local refs (#/components/schemas/Foo). External refs are
|
|
34
40
|
// out of scope — the schema we have in memory is already a single file.
|
|
35
41
|
if (!ref.startsWith('#/')) return null;
|
|
36
|
-
|
|
42
|
+
// JSON Pointer un-escaping: ``~1`` → ``/``, ``~0`` → ``~`` (then
|
|
43
|
+
// percent-decoding) so component names with slashes resolve.
|
|
44
|
+
const parts = ref
|
|
45
|
+
.slice(2)
|
|
46
|
+
.split('/')
|
|
47
|
+
.map((p) => decodeURIComponent(p.replace(/~1/g, '/').replace(/~0/g, '~')));
|
|
37
48
|
let node: unknown = root;
|
|
38
49
|
for (const part of parts) {
|
|
39
50
|
if (node && typeof node === 'object' && part in (node as Record<string, unknown>)) {
|
|
@@ -45,15 +56,24 @@ export function dereferenceSchema(schema: OpenApiSchema, maxDepth = 10): OpenApi
|
|
|
45
56
|
return node;
|
|
46
57
|
}
|
|
47
58
|
|
|
59
|
+
// Refs currently mid-expansion — detects ``A → B → A`` cycles.
|
|
60
|
+
const active = new Set<string>();
|
|
61
|
+
|
|
48
62
|
function walk(value: unknown, depth: number): unknown {
|
|
49
63
|
if (depth > maxDepth) return value;
|
|
50
64
|
if (Array.isArray(value)) return value.map((v) => walk(v, depth + 1));
|
|
51
65
|
if (value && typeof value === 'object') {
|
|
52
66
|
const obj = value as Record<string, unknown>;
|
|
53
67
|
if (typeof obj.$ref === 'string') {
|
|
54
|
-
const
|
|
68
|
+
const ref = obj.$ref;
|
|
69
|
+
// Cycle — keep the ref node so the sampler can resolve it.
|
|
70
|
+
if (active.has(ref)) return obj;
|
|
71
|
+
const resolved = resolveRef(ref);
|
|
55
72
|
if (resolved === null) return obj;
|
|
56
|
-
|
|
73
|
+
active.add(ref);
|
|
74
|
+
const out = walk(resolved, depth + 1);
|
|
75
|
+
active.delete(ref);
|
|
76
|
+
return out;
|
|
57
77
|
}
|
|
58
78
|
const out: Record<string, unknown> = {};
|
|
59
79
|
for (const [k, v] of Object.entries(obj)) out[k] = walk(v, depth + 1);
|
|
@@ -103,7 +103,11 @@ const PrettyCode = ({ data, language, className, mode = 'dark', inline = false,
|
|
|
103
103
|
style={customBg ? undefined : surfaceStyle}
|
|
104
104
|
>
|
|
105
105
|
<div className="h-full overflow-auto p-4">
|
|
106
|
-
<p
|
|
106
|
+
<p
|
|
107
|
+
className="text-sm italic"
|
|
108
|
+
role="status"
|
|
109
|
+
style={{ color: isDarkMode ? '#9ca3af' : undefined }}
|
|
110
|
+
>
|
|
107
111
|
{labels.noContent}
|
|
108
112
|
</p>
|
|
109
113
|
</div>
|
|
@@ -270,9 +274,9 @@ const PrettyCode = ({ data, language, className, mode = 'dark', inline = false,
|
|
|
270
274
|
const inlineBgClass = customBg || (isDarkMode ? '' : 'bg-muted');
|
|
271
275
|
return (
|
|
272
276
|
<Highlight theme={prismTheme} code={contentJson} language={normalizedLanguage as Language}>
|
|
273
|
-
{({ className, style, tokens, getTokenProps }) => (
|
|
277
|
+
{({ className: prismClassName, style, tokens, getTokenProps }) => (
|
|
274
278
|
<code
|
|
275
|
-
className={`${
|
|
279
|
+
className={`${prismClassName} ${inlineBgClass} px-2 py-1 rounded ${isCompact ? 'text-xs' : 'text-sm'} font-mono inline-block ${className || ''}`}
|
|
276
280
|
style={{
|
|
277
281
|
...style,
|
|
278
282
|
...(customBg ? undefined : inlineSurfaceStyle),
|
|
@@ -280,11 +284,11 @@ const PrettyCode = ({ data, language, className, mode = 'dark', inline = false,
|
|
|
280
284
|
fontFamily: 'monospace',
|
|
281
285
|
}}
|
|
282
286
|
>
|
|
283
|
-
{tokens.map((line) =>
|
|
287
|
+
{tokens.map((line, i) =>
|
|
284
288
|
line.map((token, key) => (
|
|
285
|
-
<span key={key} {...getTokenProps({ token })} />
|
|
286
|
-
))
|
|
287
|
-
)
|
|
289
|
+
<span key={`${i}-${key}`} {...getTokenProps({ token })} />
|
|
290
|
+
)),
|
|
291
|
+
)}
|
|
288
292
|
</code>
|
|
289
293
|
)}
|
|
290
294
|
</Highlight>
|
|
@@ -299,12 +303,7 @@ const PrettyCode = ({ data, language, className, mode = 'dark', inline = false,
|
|
|
299
303
|
<div
|
|
300
304
|
ref={containerRef}
|
|
301
305
|
className={`group relative ${bgClass} rounded-lg border ${isDarkMode ? '' : 'border-border'} ${className || ''}`}
|
|
302
|
-
style={
|
|
303
|
-
...(customBg ? undefined : surfaceStyle),
|
|
304
|
-
// maxHeight caps growth at ``maxLines`` rows; without maxLines we
|
|
305
|
-
// let the block grow to fit its content (no scroll).
|
|
306
|
-
...(maxHeightPx ? { maxHeight: `${maxHeightPx}px` } : null),
|
|
307
|
-
}}
|
|
306
|
+
style={customBg ? undefined : surfaceStyle}
|
|
308
307
|
>
|
|
309
308
|
{/* Toolbar: hidden by default, appears on hover. Absolute overlay so it doesn't shift layout.
|
|
310
309
|
scrollIsolation is force-disabled when content fits without scrolling —
|
|
@@ -326,14 +325,22 @@ const PrettyCode = ({ data, language, className, mode = 'dark', inline = false,
|
|
|
326
325
|
</div>
|
|
327
326
|
</div>
|
|
328
327
|
|
|
329
|
-
<div
|
|
328
|
+
<div
|
|
329
|
+
className={shouldScroll ? 'h-full overflow-auto' : ''}
|
|
330
|
+
style={shouldScroll ? { maxHeight: maxHeightPx } : undefined}
|
|
331
|
+
// When the viewer scrolls internally, make it keyboard-reachable
|
|
332
|
+
// so users without a wheel/trackpad can scroll with arrow keys.
|
|
333
|
+
{...(shouldScroll
|
|
334
|
+
? { tabIndex: 0, role: 'region', 'aria-label': displayLanguage }
|
|
335
|
+
: {})}
|
|
336
|
+
>
|
|
330
337
|
<Highlight theme={prismTheme} code={contentJson} language={normalizedLanguage as Language}>
|
|
331
|
-
{({ className, style, tokens, getLineProps, getTokenProps }) => {
|
|
338
|
+
{({ className: prismClassName, style, tokens, getLineProps, getTokenProps }) => {
|
|
332
339
|
// Remove background from Prism theme - we use our own via CSS
|
|
333
340
|
const { backgroundColor: _bg, ...restStyle } = style;
|
|
334
341
|
return (
|
|
335
342
|
<pre
|
|
336
|
-
className={`${
|
|
343
|
+
className={`${prismClassName} rounded-lg`}
|
|
337
344
|
style={{
|
|
338
345
|
...restStyle,
|
|
339
346
|
margin: 0,
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
* import PrettyCode from '@djangocfg/ui-tools/pretty-code'
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
|
-
import { createLazyComponent
|
|
13
|
+
import { createLazyComponent } from '../../components';
|
|
14
14
|
import type { Language } from 'prism-react-renderer';
|
|
15
15
|
|
|
16
16
|
// ============================================================================
|