@djangocfg/ui-tools 2.1.417 → 2.1.419
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/audio-player/index.cjs +1 -2
- package/dist/audio-player/index.cjs.map +1 -1
- package/dist/audio-player/index.d.cts +3 -11
- package/dist/audio-player/index.d.ts +3 -11
- package/dist/audio-player/index.mjs +1 -2
- package/dist/audio-player/index.mjs.map +1 -1
- package/dist/file-icon/index.cjs +3 -3
- package/dist/file-icon/index.cjs.map +1 -1
- package/dist/file-icon/index.mjs +3 -3
- package/dist/file-icon/index.mjs.map +1 -1
- package/dist/tree/index.cjs +0 -3
- package/dist/tree/index.cjs.map +1 -1
- package/dist/tree/index.mjs +0 -3
- package/dist/tree/index.mjs.map +1 -1
- package/package.json +117 -36
- package/src/common/FloatingToolbar/actions/CopyAction.tsx +31 -0
- package/src/{components → common}/FloatingToolbar/actions/DownloadAction.tsx +15 -10
- package/src/common/FloatingToolbar/actions/ExpandAction.tsx +33 -0
- package/src/common/FloatingToolbar/actions/FullscreenAction.tsx +38 -0
- package/src/{components → common}/FloatingToolbar/index.tsx +39 -0
- package/src/lib/http.ts +64 -0
- package/src/tools/chat/index.ts +1 -1
- package/src/tools/chat/launcher/ChatFAB.tsx +66 -74
- package/src/tools/chat/launcher/header/ChatHeaderActionButton.tsx +2 -3
- package/src/tools/chat/lazy.tsx +1 -1
- package/src/tools/chat/messages/MessageBubble.tsx +1 -1
- package/src/tools/chat/messages/blocks/builtin.tsx +1 -1
- package/src/tools/chat/messages/blocks/renderers/CodeBlock.tsx +2 -2
- package/src/tools/chat/messages/blocks/renderers/JsonBlock.tsx +12 -1
- package/src/tools/data/DataGrid/lazy.tsx +1 -1
- package/src/tools/data/DataTable/lazy.tsx +1 -1
- package/src/tools/data/JsonTree/JsonViewer.tsx +720 -0
- package/src/tools/data/JsonTree/README.md +126 -73
- package/src/tools/data/JsonTree/index.tsx +3 -95
- package/src/tools/data/JsonTree/lazy.tsx +10 -50
- package/src/tools/data/JsonTree/types.ts +82 -63
- package/src/tools/data/Kanban/lazy.tsx +1 -1
- package/src/tools/data/Listbox/lazy.tsx +1 -1
- package/src/tools/data/Masonry/lazy.tsx +1 -1
- package/src/tools/data/Timeline/lazy.tsx +1 -1
- package/src/tools/data/Tree/components/TreeRow.tsx +0 -11
- package/src/tools/data/Tree/lazy.tsx +1 -1
- package/src/tools/dev/Map/lazy.tsx +1 -1
- package/src/tools/dev/Mermaid/Mermaid.client.tsx +2 -2
- package/src/tools/dev/Mermaid/lazy.tsx +1 -1
- package/src/tools/dev/api/ApiRefTable/ApiRefTable.tsx +65 -0
- package/src/tools/dev/api/ApiRefTable/README.md +31 -0
- package/src/tools/dev/api/ApiRefTable/components/Row.tsx +96 -0
- package/src/tools/dev/api/ApiRefTable/components/TypeDisplay.tsx +44 -0
- package/src/tools/dev/api/ApiRefTable/index.ts +6 -0
- package/src/tools/dev/api/ApiRefTable/lazy.tsx +21 -0
- package/src/tools/dev/api/ApiRefTable/types.ts +82 -0
- package/src/tools/dev/api/ApiRefTable/utils.ts +42 -0
- package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/ApiIntroSection.tsx +1 -1
- package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/EndpointDoc/CodeSamples/index.tsx +1 -1
- package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/EndpointDoc/Header/index.tsx +1 -1
- package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/EndpointDoc/Responses/ResponseBody.tsx +7 -21
- package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/shared/RequestPanel.tsx +1 -1
- package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/shared/ResponsePanel/PrettyView.tsx +13 -19
- package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/shared/ResponsePanel/types.ts +1 -1
- package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/lazy.tsx +1 -1
- package/src/tools/dev/api/RequestViewer/README.md +33 -0
- package/src/tools/dev/api/RequestViewer/RequestViewer.tsx +121 -0
- package/src/tools/dev/api/RequestViewer/components/BodyTab.tsx +44 -0
- package/src/tools/dev/api/RequestViewer/components/EmptyState.tsx +13 -0
- package/src/tools/dev/api/RequestViewer/components/HeadersTab.tsx +78 -0
- package/src/tools/dev/api/RequestViewer/components/TimingTab.tsx +113 -0
- package/src/tools/dev/api/RequestViewer/components/utils.ts +31 -0
- package/src/tools/dev/api/RequestViewer/index.ts +16 -0
- package/src/tools/dev/api/RequestViewer/lazy.tsx +30 -0
- package/src/tools/dev/api/RequestViewer/types.ts +81 -0
- package/src/tools/dev/code/DiffViewer/DiffViewer.tsx +144 -0
- package/src/tools/dev/code/DiffViewer/README.md +33 -0
- package/src/tools/dev/code/DiffViewer/components/CopyButton.tsx +49 -0
- package/src/tools/dev/code/DiffViewer/components/DiffLineContent.tsx +48 -0
- package/src/tools/dev/code/DiffViewer/components/SplitView.tsx +220 -0
- package/src/tools/dev/code/DiffViewer/components/UnifiedView.tsx +154 -0
- package/src/tools/dev/code/DiffViewer/hooks/useDiff.ts +47 -0
- package/src/tools/dev/code/DiffViewer/hooks/useHighlighter.ts +54 -0
- package/src/tools/dev/code/DiffViewer/index.ts +22 -0
- package/src/tools/dev/code/DiffViewer/lazy.tsx +22 -0
- package/src/tools/dev/code/DiffViewer/types.ts +109 -0
- package/src/tools/dev/code/DiffViewer/utils/computeDiff.ts +159 -0
- package/src/tools/dev/{MarkdownMessage → code/MarkdownMessage}/CollapseToggle.tsx +1 -1
- package/src/tools/dev/{MarkdownMessage → code/MarkdownMessage}/MarkdownMessage.tsx +1 -1
- package/src/tools/dev/{MarkdownMessage → code/MarkdownMessage}/components.tsx +2 -2
- package/src/tools/dev/{PrettyCode → code/PrettyCode}/PrettyCode.client.tsx +2 -2
- package/src/tools/dev/{PrettyCode → code/PrettyCode}/lazy.tsx +1 -1
- package/src/tools/dev/ops/EnvTable/EnvTable.tsx +228 -0
- package/src/tools/dev/ops/EnvTable/README.md +29 -0
- package/src/tools/dev/ops/EnvTable/hooks/useEnvMask.ts +121 -0
- package/src/tools/dev/ops/EnvTable/index.ts +12 -0
- package/src/tools/dev/ops/EnvTable/lazy.tsx +21 -0
- package/src/tools/dev/ops/EnvTable/types.ts +76 -0
- package/src/tools/dev/ops/LogViewer/LogViewer.tsx +194 -0
- package/src/tools/dev/ops/LogViewer/README.md +30 -0
- package/src/tools/dev/ops/LogViewer/components/LogRow.tsx +151 -0
- package/src/tools/dev/ops/LogViewer/components/Toolbar.tsx +199 -0
- package/src/tools/dev/ops/LogViewer/hooks/useAutoScroll.ts +68 -0
- package/src/tools/dev/ops/LogViewer/hooks/useLogFilter.ts +58 -0
- package/src/tools/dev/ops/LogViewer/index.ts +18 -0
- package/src/tools/dev/ops/LogViewer/lazy.tsx +25 -0
- package/src/tools/dev/ops/LogViewer/types.ts +142 -0
- package/src/tools/dev/ops/LogViewer/utils/ansi.ts +231 -0
- package/src/tools/forms/CodeEditor/components/Editor.tsx +19 -0
- package/src/tools/forms/CodeEditor/hooks/useEditorTheme.ts +13 -73
- package/src/tools/forms/CodeEditor/lazy.tsx +1 -1
- package/src/tools/forms/CodeEditor/types/index.ts +7 -0
- package/src/tools/forms/FileUpload/lazy.tsx +1 -1
- package/src/tools/forms/JsonEditor/JsonEditor.tsx +115 -0
- package/src/tools/forms/JsonEditor/index.ts +1 -0
- package/src/tools/forms/JsonEditor/lazy.tsx +24 -0
- package/src/tools/forms/JsonForm/index.ts +1 -1
- package/src/tools/forms/JsonForm/lazy.tsx +1 -1
- package/src/tools/forms/MarkdownEditor/MarkdownEditor.tsx +40 -0
- package/src/tools/forms/MarkdownEditor/lazy.tsx +1 -1
- package/src/tools/forms/MarkdownEditor/styles.css +174 -21
- package/src/tools/forms/NotionEditor/CustomKeymap.ts +48 -0
- package/src/tools/forms/NotionEditor/LinkDialog.tsx +133 -0
- package/src/tools/forms/NotionEditor/NotionEditor.tsx +304 -0
- package/src/tools/forms/NotionEditor/README.md +237 -0
- package/src/tools/forms/NotionEditor/SlashExtension.ts +32 -0
- package/src/tools/forms/NotionEditor/SlashList.tsx +136 -0
- package/src/tools/forms/NotionEditor/TaskItemView.tsx +41 -0
- package/src/tools/forms/NotionEditor/createSlashSuggestion.ts +121 -0
- package/src/tools/forms/NotionEditor/extensions.ts +105 -0
- package/src/tools/forms/NotionEditor/index.ts +1 -0
- package/src/tools/forms/NotionEditor/lazy.tsx +44 -0
- package/src/tools/forms/NotionEditor/slashItems.ts +159 -0
- package/src/tools/forms/NotionEditor/styles.css +478 -0
- package/src/tools/forms/NotionEditor/types.ts +28 -0
- package/src/tools/index.ts +153 -13
- package/src/tools/input/Combobox/lazy.tsx +1 -1
- package/src/tools/input/CronScheduler/components/CronPreview.README.md +28 -0
- package/src/tools/input/CronScheduler/components/CronPreview.tsx +136 -0
- package/src/tools/input/CronScheduler/components/index.ts +3 -0
- package/src/tools/input/CronScheduler/index.tsx +5 -1
- package/src/tools/input/CronScheduler/lazy.tsx +5 -1
- package/src/tools/input/CronScheduler/utils/cron-next-runs.ts +122 -0
- package/src/tools/input/CronScheduler/utils/index.ts +1 -0
- package/src/tools/input/Scroller/lazy.tsx +1 -1
- package/src/tools/input/Sortable/lazy.tsx +1 -1
- package/src/tools/input/SpeechRecognition/lazy.tsx +1 -1
- package/src/tools/input/SpeechRecognition/widgets/VoiceComposerSlot.tsx +41 -36
- package/src/tools/media/AudioPlayer/PlayerShell.tsx +3 -11
- package/src/tools/media/AudioPlayer/types.ts +4 -11
- package/src/tools/media/ImageViewer/components/ImageToolbar.tsx +58 -47
- package/src/tools/media/ImageViewer/components/ImageViewer.tsx +35 -19
- package/src/tools/media/ImageViewer/lazy.tsx +1 -1
- package/src/tools/media/ImageViewer/types.ts +4 -0
- package/src/tools/media/LottiePlayer/lazy.tsx +1 -1
- package/src/tools/media/VideoPlayer/VideoPlayer.tsx +47 -1
- package/src/tools/media/VideoPlayer/parts/fullscreen.tsx +21 -4
- package/src/tools/media/VideoPlayer/parts/pip.tsx +21 -4
- package/src/tools/media/VideoPlayer/parts/play-button.tsx +21 -4
- package/src/tools/media/VideoPlayer/parts/playback-rate.tsx +19 -3
- package/src/tools/media/VideoPlayer/parts/volume.tsx +237 -18
- package/src/tools/media/VideoPlayer/styles/video-player.css +87 -7
- package/src/tools/media/VideoPlayer/types.ts +4 -0
- package/src/tools/overlay/ResponsiveDialog/lazy.tsx +1 -1
- package/src/tools/overlay/ScrollSpy/lazy.tsx +1 -1
- package/src/tools/overlay/SelectionToolbar/lazy.tsx +1 -1
- package/src/tools/overlay/Tour/lazy.tsx +1 -1
- package/src/tools/visual/Marquee/lazy.tsx +1 -1
- package/src/tools/visual/QRCode/lazy.tsx +1 -1
- package/src/tools/visual/charts/ActivityGraph/ActivityGraph.tsx +195 -0
- package/src/tools/visual/charts/ActivityGraph/README.md +28 -0
- package/src/tools/visual/charts/ActivityGraph/index.ts +8 -0
- package/src/tools/visual/charts/ActivityGraph/lazy.tsx +21 -0
- package/src/tools/visual/charts/ActivityGraph/types.ts +59 -0
- package/src/tools/visual/charts/ActivityGraph/utils.ts +88 -0
- package/src/tools/visual/charts/CommitGraph/CommitGraph.tsx +80 -0
- package/src/tools/visual/charts/CommitGraph/README.md +28 -0
- package/src/tools/visual/charts/CommitGraph/components/CommitDetail.tsx +107 -0
- package/src/tools/visual/charts/CommitGraph/components/CommitRow.tsx +122 -0
- package/src/tools/visual/charts/CommitGraph/components/Rails.tsx +171 -0
- package/src/tools/visual/charts/CommitGraph/hooks/useGraphLayout.ts +169 -0
- package/src/tools/visual/charts/CommitGraph/hooks/useLaneColors.ts +45 -0
- package/src/tools/visual/charts/CommitGraph/index.ts +14 -0
- package/src/tools/visual/charts/CommitGraph/lazy.tsx +25 -0
- package/src/tools/visual/charts/CommitGraph/types.ts +85 -0
- package/src/tools/visual/charts/CommitGraph/utils.ts +53 -0
- package/src/tools/visual/{Gauge → charts/Gauge}/lazy.tsx +1 -1
- package/src/tools/visual/charts/Sparkline/README.md +29 -0
- package/src/tools/visual/charts/Sparkline/Sparkline.tsx +217 -0
- package/src/tools/visual/charts/Sparkline/index.ts +9 -0
- package/src/tools/visual/charts/Sparkline/lazy.tsx +26 -0
- package/src/tools/visual/charts/Sparkline/types.ts +58 -0
- package/src/tools/visual/design/ColorPalette/ColorPalette.tsx +129 -0
- package/src/tools/visual/design/ColorPalette/README.md +34 -0
- package/src/tools/visual/design/ColorPalette/components/Swatch.tsx +102 -0
- package/src/tools/visual/design/ColorPalette/hooks/useCopyToClipboard.ts +41 -0
- package/src/tools/visual/design/ColorPalette/index.ts +12 -0
- package/src/tools/visual/design/ColorPalette/lazy.tsx +21 -0
- package/src/tools/visual/design/ColorPalette/types.ts +63 -0
- package/src/tools/visual/design/ColorPalette/utils.ts +83 -0
- package/src/tools/visual/{ColorPicker → design/ColorPicker}/lazy.tsx +1 -1
- package/src/tools/visual/{FileIcon → design/FileIcon}/treeAdapter.tsx +1 -1
- package/src/tools/visual/{Fps → indicators/Fps}/lazy.tsx +1 -1
- package/src/tools/visual/{Rating → indicators/Rating}/lazy.tsx +1 -1
- package/src/tools/visual/indicators/StatusIndicator/README.md +28 -0
- package/src/tools/visual/indicators/StatusIndicator/StatusIndicator.tsx +83 -0
- package/src/tools/visual/indicators/StatusIndicator/index.ts +14 -0
- package/src/tools/visual/indicators/StatusIndicator/lazy.tsx +21 -0
- package/src/tools/visual/indicators/StatusIndicator/types.ts +133 -0
- package/src/components/FloatingToolbar/actions/CopyAction.tsx +0 -22
- package/src/components/FloatingToolbar/actions/ExpandAction.tsx +0 -25
- package/src/components/FloatingToolbar/actions/FullscreenAction.tsx +0 -30
- package/src/tools/data/JsonTree/components/JsonContent.tsx +0 -197
- package/src/tools/data/JsonTree/hooks/useJsonExpand.ts +0 -50
- /package/src/{components → common}/FloatingToolbar/FloatingToolbar.css +0 -0
- /package/src/{components → common}/FloatingToolbar/actions/index.ts +0 -0
- /package/src/{components → common}/FloatingToolbar/hooks/useElementCorner.ts +0 -0
- /package/src/{components → common}/FloatingToolbar/hooks/useScrollIsolation.ts +0 -0
- /package/src/{components → common}/index.ts +0 -0
- /package/src/{components → common}/lazy-wrapper.tsx +0 -0
- /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/README.md +0 -0
- /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/DocsView.tsx +0 -0
- /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/EndpointDoc/CodeSamples/LanguageTabs.tsx +0 -0
- /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/EndpointDoc/CodeSamples/useCodeSnippet.ts +0 -0
- /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/EndpointDoc/Header/MetaActions.tsx +0 -0
- /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/EndpointDoc/Header/MethodBadge.tsx +0 -0
- /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/EndpointDoc/Header/PathDisplay.tsx +0 -0
- /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/EndpointDoc/Parameters/ParamGroup.tsx +0 -0
- /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/EndpointDoc/Parameters/ParamRow.tsx +0 -0
- /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/EndpointDoc/Parameters/index.tsx +0 -0
- /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/EndpointDoc/RequestBody/index.tsx +0 -0
- /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/EndpointDoc/Responses/ResponseRow.tsx +0 -0
- /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/EndpointDoc/Responses/StatusTag.tsx +0 -0
- /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/EndpointDoc/Responses/index.tsx +0 -0
- /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/EndpointDoc/SchemaFields/FieldRow.tsx +0 -0
- /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/EndpointDoc/SchemaFields/buildTree.ts +0 -0
- /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/EndpointDoc/SchemaFields/index.tsx +0 -0
- /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/EndpointDoc/SchemaFields/types.ts +0 -0
- /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/EndpointDoc/Section/SectionHeader.tsx +0 -0
- /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/EndpointDoc/Section/defaults.ts +0 -0
- /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/EndpointDoc/Section/index.tsx +0 -0
- /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/EndpointDoc/context.tsx +0 -0
- /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/EndpointDoc/hooks/useSectionHash.ts +0 -0
- /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/EndpointDoc/index.tsx +0 -0
- /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/EndpointDoc/store/index.ts +0 -0
- /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/EndpointDoc/store/selectors.ts +0 -0
- /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/EndpointDoc/types.ts +0 -0
- /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/SchemaCopyMenu.tsx +0 -0
- /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/Sidebar/BrandHeader.tsx +0 -0
- /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/Sidebar/CategoryBlock.tsx +0 -0
- /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/Sidebar/EndpointRow.tsx +0 -0
- /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/Sidebar/SchemaSection.tsx +0 -0
- /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/Sidebar/SearchInput.tsx +0 -0
- /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/Sidebar/SidebarBody.tsx +0 -0
- /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/Sidebar/Toolbar.tsx +0 -0
- /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/Sidebar/buildVM.ts +0 -0
- /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/Sidebar/index.tsx +0 -0
- /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/Sidebar/types.ts +0 -0
- /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/Sidebar/useDebouncedValue.ts +0 -0
- /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/SlideInPlayground.tsx +0 -0
- /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/TryItSheet.tsx +0 -0
- /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/anchor.ts +0 -0
- /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/grouping.ts +0 -0
- /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/index.tsx +0 -0
- /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/sidebarLabel.ts +0 -0
- /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/index.ts +0 -0
- /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/shared/BodyFormEditor.tsx +0 -0
- /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/shared/EndpointDraftSync.tsx +0 -0
- /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/shared/EndpointResetButton.tsx +0 -0
- /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/shared/ResponsePanel/PreviewView.tsx +0 -0
- /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/shared/ResponsePanel/RawView.tsx +0 -0
- /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/shared/ResponsePanel/StatusBar.tsx +0 -0
- /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/shared/ResponsePanel/ViewTabs.tsx +0 -0
- /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/shared/ResponsePanel/detectContent.ts +0 -0
- /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/shared/ResponsePanel/index.tsx +0 -0
- /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/shared/ResponsePanel/useResponseView.ts +0 -0
- /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/shared/SendButton.tsx +0 -0
- /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/shared/ui.tsx +0 -0
- /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/constants.ts +0 -0
- /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/context/PlaygroundContext.tsx +0 -0
- /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/hooks/index.ts +0 -0
- /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/hooks/useDocsUrlSync.ts +0 -0
- /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/hooks/useEndpointDraft.ts +0 -0
- /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/hooks/useMobile.ts +0 -0
- /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/hooks/useOpenApiSchema.ts +0 -0
- /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/index.tsx +0 -0
- /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/types.ts +0 -0
- /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/utils/apiKeyManager.ts +0 -0
- /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/utils/codeSamples.ts +0 -0
- /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/utils/formatters.ts +0 -0
- /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/utils/index.ts +0 -0
- /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/utils/operationToHar.ts +0 -0
- /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/utils/sampler.ts +0 -0
- /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/utils/schemaExport.ts +0 -0
- /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/utils/scrollParent.ts +0 -0
- /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/utils/url.ts +0 -0
- /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/utils/versionManager.ts +0 -0
- /package/src/tools/dev/{MarkdownMessage → code/MarkdownMessage}/ActionRow.tsx +0 -0
- /package/src/tools/dev/{MarkdownMessage → code/MarkdownMessage}/ChatMessageRow.tsx +0 -0
- /package/src/tools/dev/{MarkdownMessage → code/MarkdownMessage}/CodeBlock.tsx +0 -0
- /package/src/tools/dev/{MarkdownMessage → code/MarkdownMessage}/README.md +0 -0
- /package/src/tools/dev/{MarkdownMessage → code/MarkdownMessage}/index.ts +0 -0
- /package/src/tools/dev/{MarkdownMessage → code/MarkdownMessage}/linkRules.ts +0 -0
- /package/src/tools/dev/{MarkdownMessage → code/MarkdownMessage}/plainText.ts +0 -0
- /package/src/tools/dev/{MarkdownMessage → code/MarkdownMessage}/sanitize.ts +0 -0
- /package/src/tools/dev/{MarkdownMessage → code/MarkdownMessage}/types.ts +0 -0
- /package/src/tools/dev/{PrettyCode → code/PrettyCode}/README.md +0 -0
- /package/src/tools/dev/{PrettyCode → code/PrettyCode}/index.tsx +0 -0
- /package/src/tools/dev/{PrettyCode → code/PrettyCode}/registerPrismLanguages.ts +0 -0
- /package/src/tools/visual/{Gauge → charts/Gauge}/Gauge.tsx +0 -0
- /package/src/tools/visual/{Gauge → charts/Gauge}/index.ts +0 -0
- /package/src/tools/visual/{Gauge → charts/Gauge}/types.ts +0 -0
- /package/src/tools/visual/{ColorPicker → design/ColorPicker}/ColorPicker.tsx +0 -0
- /package/src/tools/visual/{ColorPicker → design/ColorPicker}/context/ColorPickerContext.tsx +0 -0
- /package/src/tools/visual/{ColorPicker → design/ColorPicker}/context/ColorPickerStore.tsx +0 -0
- /package/src/tools/visual/{ColorPicker → design/ColorPicker}/context/index.ts +0 -0
- /package/src/tools/visual/{ColorPicker → design/ColorPicker}/index.ts +0 -0
- /package/src/tools/visual/{ColorPicker → design/ColorPicker}/lib/color-utils.ts +0 -0
- /package/src/tools/visual/{ColorPicker → design/ColorPicker}/parts/ColorPickerAlphaSlider.tsx +0 -0
- /package/src/tools/visual/{ColorPicker → design/ColorPicker}/parts/ColorPickerArea.tsx +0 -0
- /package/src/tools/visual/{ColorPicker → design/ColorPicker}/parts/ColorPickerEyeDropper.tsx +0 -0
- /package/src/tools/visual/{ColorPicker → design/ColorPicker}/parts/ColorPickerFormatSelect.tsx +0 -0
- /package/src/tools/visual/{ColorPicker → design/ColorPicker}/parts/ColorPickerHueSlider.tsx +0 -0
- /package/src/tools/visual/{ColorPicker → design/ColorPicker}/parts/ColorPickerInput.tsx +0 -0
- /package/src/tools/visual/{ColorPicker → design/ColorPicker}/parts/ColorPickerSwatch.tsx +0 -0
- /package/src/tools/visual/{ColorPicker → design/ColorPicker}/parts/index.ts +0 -0
- /package/src/tools/visual/{ColorPicker → design/ColorPicker}/types.ts +0 -0
- /package/src/tools/visual/{FileIcon → design/FileIcon}/FileIcon.tsx +0 -0
- /package/src/tools/visual/{FileIcon → design/FileIcon}/get-file-icon.ts +0 -0
- /package/src/tools/visual/{FileIcon → design/FileIcon}/icons/icon-data.ts +0 -0
- /package/src/tools/visual/{FileIcon → design/FileIcon}/index.ts +0 -0
- /package/src/tools/visual/{FileIcon → design/FileIcon}/loader.ts +0 -0
- /package/src/tools/visual/{FileIcon → design/FileIcon}/specialFolders.ts +0 -0
- /package/src/tools/visual/{Fps → indicators/Fps}/Fps.tsx +0 -0
- /package/src/tools/visual/{Fps → indicators/Fps}/index.ts +0 -0
- /package/src/tools/visual/{Fps → indicators/Fps}/types.ts +0 -0
- /package/src/tools/visual/{Rating → indicators/Rating}/Rating.tsx +0 -0
- /package/src/tools/visual/{Rating → indicators/Rating}/index.ts +0 -0
- /package/src/tools/visual/{Rating → indicators/Rating}/types.ts +0 -0
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
// Adapted from jalcoui (MIT) — github.com/jal-co/ui
|
|
2
|
+
//
|
|
3
|
+
// Lightweight HTTP inspector — headers / body / timing in tabs. Renders an
|
|
4
|
+
// already-executed request/response object; never fetches. For an
|
|
5
|
+
// interactive request playground, use OpenapiViewer instead.
|
|
6
|
+
//
|
|
7
|
+
// Requires <UiProviders> mounted at the host root (Tabs use Radix primitives).
|
|
8
|
+
|
|
9
|
+
'use client';
|
|
10
|
+
|
|
11
|
+
import * as React from 'react';
|
|
12
|
+
import { Clock, FileText, Globe, Activity } from 'lucide-react';
|
|
13
|
+
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@djangocfg/ui-core';
|
|
14
|
+
import { cn } from '@djangocfg/ui-core/lib';
|
|
15
|
+
import { methodColor, statusColor } from '../../../../lib/http';
|
|
16
|
+
import {
|
|
17
|
+
METHOD_TONE_TEXT,
|
|
18
|
+
STATUS_TONE_PILL,
|
|
19
|
+
type RequestViewerProps,
|
|
20
|
+
type RequestViewerTab,
|
|
21
|
+
} from './types';
|
|
22
|
+
import { HeadersTab } from './components/HeadersTab';
|
|
23
|
+
import { BodyTab } from './components/BodyTab';
|
|
24
|
+
import { TimingTab } from './components/TimingTab';
|
|
25
|
+
import { formatDuration } from './components/utils';
|
|
26
|
+
|
|
27
|
+
export function RequestViewer({
|
|
28
|
+
request,
|
|
29
|
+
defaultTab = 'headers',
|
|
30
|
+
className,
|
|
31
|
+
...props
|
|
32
|
+
}: RequestViewerProps) {
|
|
33
|
+
const headerCount =
|
|
34
|
+
(request.requestHeaders?.length ?? 0) +
|
|
35
|
+
(request.responseHeaders?.length ?? 0);
|
|
36
|
+
const timingCount = request.timing?.length ?? 0;
|
|
37
|
+
|
|
38
|
+
const methodToneCls = METHOD_TONE_TEXT[methodColor(request.method)];
|
|
39
|
+
const statusToneCls = STATUS_TONE_PILL[statusColor(request.status)];
|
|
40
|
+
|
|
41
|
+
return (
|
|
42
|
+
<div
|
|
43
|
+
data-slot="request-viewer"
|
|
44
|
+
className={cn(
|
|
45
|
+
'overflow-hidden rounded-xl border border-border/60 bg-card shadow-sm',
|
|
46
|
+
className,
|
|
47
|
+
)}
|
|
48
|
+
{...props}
|
|
49
|
+
>
|
|
50
|
+
{/* Request summary */}
|
|
51
|
+
<div className="flex flex-wrap items-center gap-3 border-b border-border/40 px-4 py-3">
|
|
52
|
+
<span
|
|
53
|
+
className={cn(
|
|
54
|
+
'font-mono text-sm font-bold uppercase',
|
|
55
|
+
methodToneCls,
|
|
56
|
+
)}
|
|
57
|
+
>
|
|
58
|
+
{request.method}
|
|
59
|
+
</span>
|
|
60
|
+
<span className="min-w-0 flex-1 truncate font-mono text-sm text-muted-foreground">
|
|
61
|
+
{request.url}
|
|
62
|
+
</span>
|
|
63
|
+
<span
|
|
64
|
+
className={cn(
|
|
65
|
+
'inline-flex items-center rounded-md px-2.5 py-0.5 font-mono text-xs font-semibold',
|
|
66
|
+
statusToneCls,
|
|
67
|
+
)}
|
|
68
|
+
>
|
|
69
|
+
{request.status}
|
|
70
|
+
{request.statusText ? ` ${request.statusText}` : null}
|
|
71
|
+
</span>
|
|
72
|
+
{request.duration !== undefined && (
|
|
73
|
+
<span className="inline-flex items-center gap-1 text-xs text-muted-foreground">
|
|
74
|
+
<Clock className="size-3" />
|
|
75
|
+
{formatDuration(request.duration)}
|
|
76
|
+
</span>
|
|
77
|
+
)}
|
|
78
|
+
</div>
|
|
79
|
+
|
|
80
|
+
{/* Tabs */}
|
|
81
|
+
<Tabs defaultValue={defaultTab satisfies RequestViewerTab}>
|
|
82
|
+
<div className="border-b border-border/40 bg-muted/20 px-2 pt-2">
|
|
83
|
+
<TabsList variant="underline">
|
|
84
|
+
<TabsTrigger value="headers">
|
|
85
|
+
<FileText className="size-3.5 mr-1.5" />
|
|
86
|
+
Headers
|
|
87
|
+
{headerCount > 0 && (
|
|
88
|
+
<span className="ml-1.5 rounded-full bg-muted px-1.5 py-0.5 text-[10px] font-semibold leading-none">
|
|
89
|
+
{headerCount}
|
|
90
|
+
</span>
|
|
91
|
+
)}
|
|
92
|
+
</TabsTrigger>
|
|
93
|
+
<TabsTrigger value="body">
|
|
94
|
+
<Globe className="size-3.5 mr-1.5" />
|
|
95
|
+
Body
|
|
96
|
+
</TabsTrigger>
|
|
97
|
+
<TabsTrigger value="timing">
|
|
98
|
+
<Activity className="size-3.5 mr-1.5" />
|
|
99
|
+
Timing
|
|
100
|
+
{timingCount > 0 && (
|
|
101
|
+
<span className="ml-1.5 rounded-full bg-muted px-1.5 py-0.5 text-[10px] font-semibold leading-none">
|
|
102
|
+
{timingCount}
|
|
103
|
+
</span>
|
|
104
|
+
)}
|
|
105
|
+
</TabsTrigger>
|
|
106
|
+
</TabsList>
|
|
107
|
+
</div>
|
|
108
|
+
|
|
109
|
+
<TabsContent value="headers" className="mt-0">
|
|
110
|
+
<HeadersTab request={request} />
|
|
111
|
+
</TabsContent>
|
|
112
|
+
<TabsContent value="body" className="mt-0">
|
|
113
|
+
<BodyTab request={request} />
|
|
114
|
+
</TabsContent>
|
|
115
|
+
<TabsContent value="timing" className="mt-0">
|
|
116
|
+
<TimingTab request={request} />
|
|
117
|
+
</TabsContent>
|
|
118
|
+
</Tabs>
|
|
119
|
+
</div>
|
|
120
|
+
);
|
|
121
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
// Adapted from jalcoui (MIT) — github.com/jal-co/ui
|
|
2
|
+
|
|
3
|
+
'use client';
|
|
4
|
+
|
|
5
|
+
import * as React from 'react';
|
|
6
|
+
import { JsonTree } from '../../../../data/JsonTree';
|
|
7
|
+
import type { NetworkRequest } from '../types';
|
|
8
|
+
import { byteSize, looksLikeJson, tryParseJson } from './utils';
|
|
9
|
+
import { EmptyState } from './EmptyState';
|
|
10
|
+
|
|
11
|
+
export function BodyTab({ request }: { request: NetworkRequest }) {
|
|
12
|
+
if (!request.responseBody) {
|
|
13
|
+
return <EmptyState message="No response body available." />;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const declaredJson = request.contentType?.toLowerCase().includes('json');
|
|
17
|
+
const isJson = declaredJson || looksLikeJson(request.responseBody);
|
|
18
|
+
const parsed = isJson ? tryParseJson(request.responseBody) : null;
|
|
19
|
+
|
|
20
|
+
return (
|
|
21
|
+
<div className="flex flex-col gap-2 p-4">
|
|
22
|
+
{request.contentType && (
|
|
23
|
+
<div className="flex items-center gap-2">
|
|
24
|
+
<span className="rounded-md bg-muted px-2 py-0.5 font-mono text-[10px] font-medium text-muted-foreground">
|
|
25
|
+
{request.contentType}
|
|
26
|
+
</span>
|
|
27
|
+
<span className="text-[10px] text-muted-foreground">
|
|
28
|
+
{byteSize(request.responseBody)}
|
|
29
|
+
</span>
|
|
30
|
+
</div>
|
|
31
|
+
)}
|
|
32
|
+
{parsed !== null ? (
|
|
33
|
+
// JsonTree provides collapsible tree + search/copy for structured bodies.
|
|
34
|
+
<JsonTree data={parsed} bordered toolbar="always" compactHeader />
|
|
35
|
+
) : (
|
|
36
|
+
<div className="overflow-auto rounded-lg border border-border/60 bg-muted/30">
|
|
37
|
+
<pre className="p-4 text-xs leading-relaxed text-foreground">
|
|
38
|
+
<code>{request.responseBody}</code>
|
|
39
|
+
</pre>
|
|
40
|
+
</div>
|
|
41
|
+
)}
|
|
42
|
+
</div>
|
|
43
|
+
);
|
|
44
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
// Adapted from jalcoui (MIT) — github.com/jal-co/ui
|
|
2
|
+
|
|
3
|
+
'use client';
|
|
4
|
+
|
|
5
|
+
import * as React from 'react';
|
|
6
|
+
|
|
7
|
+
export function EmptyState({ message }: { message: string }) {
|
|
8
|
+
return (
|
|
9
|
+
<div className="flex items-center justify-center py-10 text-sm text-muted-foreground">
|
|
10
|
+
{message}
|
|
11
|
+
</div>
|
|
12
|
+
);
|
|
13
|
+
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
// Adapted from jalcoui (MIT) — github.com/jal-co/ui
|
|
2
|
+
|
|
3
|
+
'use client';
|
|
4
|
+
|
|
5
|
+
import * as React from 'react';
|
|
6
|
+
import { ArrowDown, ArrowUp, type LucideIcon } from 'lucide-react';
|
|
7
|
+
import { cn } from '@djangocfg/ui-core/lib';
|
|
8
|
+
import type { HeaderEntry, NetworkRequest } from '../types';
|
|
9
|
+
import { EmptyState } from './EmptyState';
|
|
10
|
+
|
|
11
|
+
function HeadersTable({
|
|
12
|
+
title,
|
|
13
|
+
headers,
|
|
14
|
+
icon: Icon,
|
|
15
|
+
}: {
|
|
16
|
+
title: string;
|
|
17
|
+
headers: HeaderEntry[];
|
|
18
|
+
icon: LucideIcon;
|
|
19
|
+
}) {
|
|
20
|
+
if (headers.length === 0) return null;
|
|
21
|
+
|
|
22
|
+
return (
|
|
23
|
+
<div className="flex flex-col gap-2">
|
|
24
|
+
<div className="flex items-center gap-1.5">
|
|
25
|
+
<Icon className="size-3.5 text-muted-foreground" />
|
|
26
|
+
<h4 className="text-xs font-semibold uppercase tracking-wide text-muted-foreground">
|
|
27
|
+
{title}
|
|
28
|
+
</h4>
|
|
29
|
+
</div>
|
|
30
|
+
<div className="overflow-hidden rounded-lg border border-border/60">
|
|
31
|
+
{headers.map((header, i) => (
|
|
32
|
+
<div
|
|
33
|
+
key={`${header.name}-${i}`}
|
|
34
|
+
className={cn(
|
|
35
|
+
'flex gap-4 px-3 py-2 text-sm',
|
|
36
|
+
i !== headers.length - 1 && 'border-b border-border/40',
|
|
37
|
+
)}
|
|
38
|
+
>
|
|
39
|
+
<span className="w-[200px] shrink-0 truncate font-mono text-xs font-medium text-foreground">
|
|
40
|
+
{header.name}
|
|
41
|
+
</span>
|
|
42
|
+
<span className="min-w-0 flex-1 break-all font-mono text-xs text-muted-foreground">
|
|
43
|
+
{header.value}
|
|
44
|
+
</span>
|
|
45
|
+
</div>
|
|
46
|
+
))}
|
|
47
|
+
</div>
|
|
48
|
+
</div>
|
|
49
|
+
);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export function HeadersTab({ request }: { request: NetworkRequest }) {
|
|
53
|
+
const hasRequest = (request.requestHeaders?.length ?? 0) > 0;
|
|
54
|
+
const hasResponse = (request.responseHeaders?.length ?? 0) > 0;
|
|
55
|
+
|
|
56
|
+
if (!hasRequest && !hasResponse) {
|
|
57
|
+
return <EmptyState message="No headers available." />;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return (
|
|
61
|
+
<div className="flex flex-col gap-5 p-4">
|
|
62
|
+
{hasRequest && (
|
|
63
|
+
<HeadersTable
|
|
64
|
+
title="Request Headers"
|
|
65
|
+
headers={request.requestHeaders!}
|
|
66
|
+
icon={ArrowUp}
|
|
67
|
+
/>
|
|
68
|
+
)}
|
|
69
|
+
{hasResponse && (
|
|
70
|
+
<HeadersTable
|
|
71
|
+
title="Response Headers"
|
|
72
|
+
headers={request.responseHeaders!}
|
|
73
|
+
icon={ArrowDown}
|
|
74
|
+
/>
|
|
75
|
+
)}
|
|
76
|
+
</div>
|
|
77
|
+
);
|
|
78
|
+
}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
// Adapted from jalcoui (MIT) — github.com/jal-co/ui
|
|
2
|
+
|
|
3
|
+
'use client';
|
|
4
|
+
|
|
5
|
+
import * as React from 'react';
|
|
6
|
+
import { useStylePresets } from '@djangocfg/ui-core/styles/palette';
|
|
7
|
+
import { cn } from '@djangocfg/ui-core/lib';
|
|
8
|
+
import type { NetworkRequest } from '../types';
|
|
9
|
+
import { EmptyState } from './EmptyState';
|
|
10
|
+
import { formatDuration } from './utils';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Categorical palette for timing phases (DNS, TCP, TLS, …). Stages are not
|
|
14
|
+
* statuses, so we route through `useStylePresets()` — 8 distinguishable hex
|
|
15
|
+
* colors that respect the active theme. Indices wrap modulo length.
|
|
16
|
+
*/
|
|
17
|
+
function useStagePalette(): string[] {
|
|
18
|
+
const presets = useStylePresets();
|
|
19
|
+
return React.useMemo(
|
|
20
|
+
() => [
|
|
21
|
+
presets.primary.fill,
|
|
22
|
+
presets.success.fill,
|
|
23
|
+
presets.warning.fill,
|
|
24
|
+
presets.danger.fill,
|
|
25
|
+
presets.info.fill,
|
|
26
|
+
presets.chart3.fill,
|
|
27
|
+
presets.chart4.fill,
|
|
28
|
+
presets.chart5.fill,
|
|
29
|
+
],
|
|
30
|
+
[presets],
|
|
31
|
+
);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export function TimingTab({ request }: { request: NetworkRequest }) {
|
|
35
|
+
const stagePalette = useStagePalette();
|
|
36
|
+
|
|
37
|
+
if (!request.timing || request.timing.length === 0) {
|
|
38
|
+
return <EmptyState message="No timing data available." />;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const timing = request.timing;
|
|
42
|
+
const maxDuration = Math.max(...timing.map((t) => t.duration));
|
|
43
|
+
const totalDuration =
|
|
44
|
+
request.duration ?? timing.reduce((sum, t) => sum + t.duration, 0);
|
|
45
|
+
|
|
46
|
+
return (
|
|
47
|
+
<div className="flex flex-col gap-4 p-4">
|
|
48
|
+
{/* Total */}
|
|
49
|
+
<div className="flex items-baseline justify-between">
|
|
50
|
+
<span className="text-xs font-semibold uppercase tracking-wide text-muted-foreground">
|
|
51
|
+
Total Duration
|
|
52
|
+
</span>
|
|
53
|
+
<span className="font-mono text-sm font-semibold text-foreground">
|
|
54
|
+
{formatDuration(totalDuration)}
|
|
55
|
+
</span>
|
|
56
|
+
</div>
|
|
57
|
+
|
|
58
|
+
{/* Waterfall */}
|
|
59
|
+
<div className="flex flex-col gap-2.5">
|
|
60
|
+
{timing.map((entry, i) => {
|
|
61
|
+
const percentage =
|
|
62
|
+
maxDuration > 0 ? (entry.duration / maxDuration) * 100 : 0;
|
|
63
|
+
const color = stagePalette[i % stagePalette.length];
|
|
64
|
+
|
|
65
|
+
return (
|
|
66
|
+
<div
|
|
67
|
+
key={`${entry.label}-${i}`}
|
|
68
|
+
className="flex items-center gap-3"
|
|
69
|
+
>
|
|
70
|
+
<span className="w-[120px] shrink-0 truncate text-xs text-muted-foreground">
|
|
71
|
+
{entry.label}
|
|
72
|
+
</span>
|
|
73
|
+
<div className="relative h-5 flex-1 overflow-hidden rounded-md bg-muted/50">
|
|
74
|
+
<div
|
|
75
|
+
className={cn('h-full rounded-md transition-all')}
|
|
76
|
+
style={{
|
|
77
|
+
width: `${Math.max(percentage, 2)}%`,
|
|
78
|
+
opacity: percentage === 0 ? 0.3 : 1,
|
|
79
|
+
backgroundColor: color,
|
|
80
|
+
}}
|
|
81
|
+
/>
|
|
82
|
+
</div>
|
|
83
|
+
<span className="w-[64px] shrink-0 text-right font-mono text-xs text-muted-foreground">
|
|
84
|
+
{formatDuration(entry.duration)}
|
|
85
|
+
</span>
|
|
86
|
+
</div>
|
|
87
|
+
);
|
|
88
|
+
})}
|
|
89
|
+
</div>
|
|
90
|
+
|
|
91
|
+
{/* Legend */}
|
|
92
|
+
<div className="flex flex-wrap gap-3 border-t border-border/40 pt-3">
|
|
93
|
+
{timing.map((entry, i) => {
|
|
94
|
+
const color = stagePalette[i % stagePalette.length];
|
|
95
|
+
return (
|
|
96
|
+
<div
|
|
97
|
+
key={`legend-${entry.label}-${i}`}
|
|
98
|
+
className="flex items-center gap-1.5"
|
|
99
|
+
>
|
|
100
|
+
<span
|
|
101
|
+
className="size-2.5 rounded-full"
|
|
102
|
+
style={{ backgroundColor: color }}
|
|
103
|
+
/>
|
|
104
|
+
<span className="text-[10px] text-muted-foreground">
|
|
105
|
+
{entry.label}
|
|
106
|
+
</span>
|
|
107
|
+
</div>
|
|
108
|
+
);
|
|
109
|
+
})}
|
|
110
|
+
</div>
|
|
111
|
+
</div>
|
|
112
|
+
);
|
|
113
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
// Adapted from jalcoui (MIT) — github.com/jal-co/ui
|
|
2
|
+
|
|
3
|
+
/** Format milliseconds for human reading: µs / ms / s. */
|
|
4
|
+
export function formatDuration(ms: number): string {
|
|
5
|
+
if (ms < 1) return `${(ms * 1000).toFixed(0)}µs`;
|
|
6
|
+
if (ms < 1000) return `${ms.toFixed(ms < 10 ? 1 : 0)}ms`;
|
|
7
|
+
return `${(ms / 1000).toFixed(2)}s`;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export function byteSize(str: string): string {
|
|
11
|
+
const bytes = new Blob([str]).size;
|
|
12
|
+
if (bytes < 1024) return `${bytes} B`;
|
|
13
|
+
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
|
|
14
|
+
return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function looksLikeJson(str: string): boolean {
|
|
18
|
+
const trimmed = str.trim();
|
|
19
|
+
return (
|
|
20
|
+
(trimmed.startsWith('{') && trimmed.endsWith('}')) ||
|
|
21
|
+
(trimmed.startsWith('[') && trimmed.endsWith(']'))
|
|
22
|
+
);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function tryParseJson(str: string): unknown | null {
|
|
26
|
+
try {
|
|
27
|
+
return JSON.parse(str);
|
|
28
|
+
} catch {
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
// Adapted from jalcoui (MIT) — github.com/jal-co/ui
|
|
2
|
+
|
|
3
|
+
export { RequestViewer } from './RequestViewer';
|
|
4
|
+
export {
|
|
5
|
+
STATUS_TONE_PILL,
|
|
6
|
+
METHOD_TONE_TEXT,
|
|
7
|
+
} from './types';
|
|
8
|
+
export type {
|
|
9
|
+
RequestViewerProps,
|
|
10
|
+
RequestViewerTab,
|
|
11
|
+
NetworkRequest,
|
|
12
|
+
HeaderEntry,
|
|
13
|
+
TimingEntry,
|
|
14
|
+
HttpMethodTone,
|
|
15
|
+
HttpStatusTone,
|
|
16
|
+
} from './types';
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
// Adapted from jalcoui (MIT) — github.com/jal-co/ui
|
|
2
|
+
|
|
3
|
+
'use client';
|
|
4
|
+
|
|
5
|
+
import { createLazyComponent } from '../../../../common/lazy-wrapper';
|
|
6
|
+
import type { RequestViewerProps } from './types';
|
|
7
|
+
|
|
8
|
+
export const LazyRequestViewer = createLazyComponent<RequestViewerProps>(
|
|
9
|
+
() =>
|
|
10
|
+
import('./RequestViewer').then((mod) => ({
|
|
11
|
+
default: mod.RequestViewer,
|
|
12
|
+
})),
|
|
13
|
+
{
|
|
14
|
+
displayName: 'LazyRequestViewer',
|
|
15
|
+
fallback: (
|
|
16
|
+
<div
|
|
17
|
+
data-slot="request-viewer-skeleton"
|
|
18
|
+
className="h-64 w-full animate-pulse rounded-xl bg-muted"
|
|
19
|
+
/>
|
|
20
|
+
),
|
|
21
|
+
},
|
|
22
|
+
);
|
|
23
|
+
|
|
24
|
+
export type {
|
|
25
|
+
RequestViewerProps,
|
|
26
|
+
RequestViewerTab,
|
|
27
|
+
NetworkRequest,
|
|
28
|
+
HeaderEntry,
|
|
29
|
+
TimingEntry,
|
|
30
|
+
} from './types';
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
// Adapted from jalcoui (MIT) — github.com/jal-co/ui
|
|
2
|
+
|
|
3
|
+
import type * as React from 'react';
|
|
4
|
+
import type { HttpMethodTone, HttpStatusTone } from '../../../../lib/http';
|
|
5
|
+
|
|
6
|
+
/** Single key/value header pair. */
|
|
7
|
+
export interface HeaderEntry {
|
|
8
|
+
name: string;
|
|
9
|
+
value: string;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/** A timing phase for the request lifecycle (DNS, TCP, TLS, …). */
|
|
13
|
+
export interface TimingEntry {
|
|
14
|
+
/** Label for this timing phase (e.g. "DNS Lookup", "TLS Handshake"). */
|
|
15
|
+
label: string;
|
|
16
|
+
/** Duration in milliseconds. */
|
|
17
|
+
duration: number;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* A captured network request — already-executed. The viewer never fetches.
|
|
22
|
+
*/
|
|
23
|
+
export interface NetworkRequest {
|
|
24
|
+
/** HTTP method (GET, POST, etc.). Case-insensitive. */
|
|
25
|
+
method: string;
|
|
26
|
+
/** Full request URL. */
|
|
27
|
+
url: string;
|
|
28
|
+
/** HTTP status code. */
|
|
29
|
+
status: number;
|
|
30
|
+
/** HTTP status text (e.g. "OK", "Not Found"). */
|
|
31
|
+
statusText?: string;
|
|
32
|
+
/** Request headers. */
|
|
33
|
+
requestHeaders?: HeaderEntry[];
|
|
34
|
+
/** Response headers. */
|
|
35
|
+
responseHeaders?: HeaderEntry[];
|
|
36
|
+
/** Response body as a string (raw text or JSON-encoded text). */
|
|
37
|
+
responseBody?: string;
|
|
38
|
+
/** Content type of the response (used for display hints). */
|
|
39
|
+
contentType?: string;
|
|
40
|
+
/** Timing breakdown for the request lifecycle. */
|
|
41
|
+
timing?: TimingEntry[];
|
|
42
|
+
/** Total request duration in milliseconds. */
|
|
43
|
+
duration?: number;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export type RequestViewerTab = 'headers' | 'body' | 'timing';
|
|
47
|
+
|
|
48
|
+
export interface RequestViewerProps
|
|
49
|
+
extends Omit<React.ComponentProps<'div'>, 'children'> {
|
|
50
|
+
/** Network request data to display. */
|
|
51
|
+
request: NetworkRequest;
|
|
52
|
+
/** Initial active tab. Defaults to "headers". */
|
|
53
|
+
defaultTab?: RequestViewerTab;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// Re-export the HTTP tone types for callers that need them.
|
|
57
|
+
export type { HttpMethodTone, HttpStatusTone };
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Map an HTTP status tone → background+text class pair for the status pill.
|
|
61
|
+
* Uses opacity-derived surfaces so every theme preset renders correctly
|
|
62
|
+
* even when `--{status}-background` is undefined.
|
|
63
|
+
*/
|
|
64
|
+
export const STATUS_TONE_PILL: Record<HttpStatusTone, string> = {
|
|
65
|
+
success: 'bg-success/15 text-success',
|
|
66
|
+
info: 'bg-info/15 text-info',
|
|
67
|
+
warning: 'bg-warning/15 text-warning',
|
|
68
|
+
destructive: 'bg-destructive/15 text-destructive',
|
|
69
|
+
muted: 'bg-muted text-muted-foreground',
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Map an HTTP method tone → text-only class. Method label has no background.
|
|
74
|
+
*/
|
|
75
|
+
export const METHOD_TONE_TEXT: Record<HttpMethodTone, string> = {
|
|
76
|
+
success: 'text-success',
|
|
77
|
+
info: 'text-info',
|
|
78
|
+
warning: 'text-warning',
|
|
79
|
+
destructive: 'text-destructive',
|
|
80
|
+
muted: 'text-muted-foreground',
|
|
81
|
+
};
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
// Adapted from jalcoui (MIT) — github.com/jal-co/ui
|
|
2
|
+
//
|
|
3
|
+
// Line-by-line code diff viewer. Supports unified (single column) and
|
|
4
|
+
// split (side-by-side) layouts. Syntax highlighting reuses the same
|
|
5
|
+
// `prism-react-renderer` bundle that the `PrettyCode` tool already
|
|
6
|
+
// pulls in — no second highlighter is loaded.
|
|
7
|
+
//
|
|
8
|
+
// Requires <UiProviders> at the host root (no nested providers here).
|
|
9
|
+
// See CONTRACT.md §5.
|
|
10
|
+
//
|
|
11
|
+
// Unlike `PrettyCode` (intentionally always-dark), DiffViewer adapts to
|
|
12
|
+
// the host theme. Diffs are commonly embedded in documentation pages
|
|
13
|
+
// where forcing a dark surface on a light page reads as a bug. The
|
|
14
|
+
// Prism theme is picked per host theme (`themes.vsDark` for dark,
|
|
15
|
+
// `themes.github` for light) and surface chrome uses semantic tokens.
|
|
16
|
+
|
|
17
|
+
'use client';
|
|
18
|
+
|
|
19
|
+
import * as React from 'react';
|
|
20
|
+
import { themes, type PrismTheme } from 'prism-react-renderer';
|
|
21
|
+
import { cn } from '@djangocfg/ui-core/lib';
|
|
22
|
+
import { useResolvedTheme } from '@djangocfg/ui-core/hooks';
|
|
23
|
+
import { CopyButton } from './components/CopyButton';
|
|
24
|
+
import { SplitView } from './components/SplitView';
|
|
25
|
+
import { UnifiedView } from './components/UnifiedView';
|
|
26
|
+
import { useDiff } from './hooks/useDiff';
|
|
27
|
+
import { useDiffLanguage } from './hooks/useHighlighter';
|
|
28
|
+
import {
|
|
29
|
+
DIFF_TONE_CLASSES,
|
|
30
|
+
type DiffInput,
|
|
31
|
+
type DiffViewerProps,
|
|
32
|
+
} from './types';
|
|
33
|
+
|
|
34
|
+
export function DiffViewer({
|
|
35
|
+
layout = 'unified',
|
|
36
|
+
language,
|
|
37
|
+
oldTitle,
|
|
38
|
+
newTitle,
|
|
39
|
+
copyable = true,
|
|
40
|
+
context = 3,
|
|
41
|
+
className,
|
|
42
|
+
...allProps
|
|
43
|
+
}: DiffViewerProps) {
|
|
44
|
+
// Split the diff-input keys off the DOM rest props so we don't spread
|
|
45
|
+
// `oldCode` / `newCode` / `patch` onto the wrapping <div>.
|
|
46
|
+
const { oldCode, newCode, patch, ...divProps } =
|
|
47
|
+
allProps as DiffViewerProps & Record<string, unknown>;
|
|
48
|
+
const input: DiffInput =
|
|
49
|
+
patch !== undefined
|
|
50
|
+
? ({ patch: patch as string } as DiffInput)
|
|
51
|
+
: ({
|
|
52
|
+
oldCode: (oldCode as string) ?? '',
|
|
53
|
+
newCode: (newCode as string) ?? '',
|
|
54
|
+
} as DiffInput);
|
|
55
|
+
|
|
56
|
+
const { lines, numWidth, stats } = useDiff({ input, context });
|
|
57
|
+
|
|
58
|
+
// Pass `null` when no language → cheap plain-text render path.
|
|
59
|
+
const prismLang = useDiffLanguage(language);
|
|
60
|
+
const resolvedLanguage = language ? prismLang : null;
|
|
61
|
+
|
|
62
|
+
// Pick a Prism palette to match the host theme. Resolved once at the
|
|
63
|
+
// viewer root, then forwarded to every line — keeps the hook out of
|
|
64
|
+
// per-line render paths (could be thousands of rows on big diffs).
|
|
65
|
+
const resolved = useResolvedTheme();
|
|
66
|
+
const prismTheme: PrismTheme =
|
|
67
|
+
resolved === 'dark' ? themes.vsDark : themes.github;
|
|
68
|
+
|
|
69
|
+
// Full text used for the copy button — concatenates current lines as
|
|
70
|
+
// displayed, including removed ones. Mirrors jalcoui's behavior.
|
|
71
|
+
const fullCode = React.useMemo(
|
|
72
|
+
() => lines.map((l) => l.content).join('\n'),
|
|
73
|
+
[lines],
|
|
74
|
+
);
|
|
75
|
+
|
|
76
|
+
const showHeader = Boolean(oldTitle || newTitle);
|
|
77
|
+
const addedTone = DIFF_TONE_CLASSES.added.stat;
|
|
78
|
+
const removedTone = DIFF_TONE_CLASSES.removed.stat;
|
|
79
|
+
|
|
80
|
+
return (
|
|
81
|
+
<div
|
|
82
|
+
data-slot="diff-viewer"
|
|
83
|
+
data-layout={layout}
|
|
84
|
+
className={cn(
|
|
85
|
+
'relative overflow-hidden rounded-xl border border-border/60 bg-card text-foreground shadow-sm',
|
|
86
|
+
className,
|
|
87
|
+
)}
|
|
88
|
+
{...(divProps as React.ComponentProps<'div'>)}
|
|
89
|
+
>
|
|
90
|
+
{showHeader && (
|
|
91
|
+
<div className="flex items-center justify-between gap-3 border-b border-border/60 bg-muted/40 px-4 py-2.5">
|
|
92
|
+
<div className="flex items-center gap-3 text-sm">
|
|
93
|
+
{oldTitle && (
|
|
94
|
+
<span className="text-muted-foreground">{oldTitle}</span>
|
|
95
|
+
)}
|
|
96
|
+
{oldTitle && newTitle && (
|
|
97
|
+
<span className="text-muted-foreground/60" aria-hidden="true">
|
|
98
|
+
→
|
|
99
|
+
</span>
|
|
100
|
+
)}
|
|
101
|
+
{newTitle && (
|
|
102
|
+
<span className="font-medium text-foreground">{newTitle}</span>
|
|
103
|
+
)}
|
|
104
|
+
</div>
|
|
105
|
+
<div className="flex items-center gap-3">
|
|
106
|
+
<div className="flex items-center gap-2 text-xs tabular-nums">
|
|
107
|
+
{stats.added > 0 && (
|
|
108
|
+
<span className={addedTone}>+{stats.added}</span>
|
|
109
|
+
)}
|
|
110
|
+
{stats.removed > 0 && (
|
|
111
|
+
<span className={removedTone}>-{stats.removed}</span>
|
|
112
|
+
)}
|
|
113
|
+
</div>
|
|
114
|
+
{copyable && <CopyButton value={fullCode} />}
|
|
115
|
+
</div>
|
|
116
|
+
</div>
|
|
117
|
+
)}
|
|
118
|
+
|
|
119
|
+
{!showHeader && copyable && (
|
|
120
|
+
<div className="absolute right-2 top-2 z-10">
|
|
121
|
+
<CopyButton value={fullCode} />
|
|
122
|
+
</div>
|
|
123
|
+
)}
|
|
124
|
+
|
|
125
|
+
<div className="overflow-x-auto">
|
|
126
|
+
{layout === 'split' ? (
|
|
127
|
+
<SplitView
|
|
128
|
+
lines={lines}
|
|
129
|
+
numWidth={numWidth}
|
|
130
|
+
language={resolvedLanguage}
|
|
131
|
+
prismTheme={prismTheme}
|
|
132
|
+
/>
|
|
133
|
+
) : (
|
|
134
|
+
<UnifiedView
|
|
135
|
+
lines={lines}
|
|
136
|
+
numWidth={numWidth}
|
|
137
|
+
language={resolvedLanguage}
|
|
138
|
+
prismTheme={prismTheme}
|
|
139
|
+
/>
|
|
140
|
+
)}
|
|
141
|
+
</div>
|
|
142
|
+
</div>
|
|
143
|
+
);
|
|
144
|
+
}
|