@djangocfg/ui-tools 2.1.418 → 2.1.419
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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/package.json +93 -28
- 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/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/hooks/useEditorTheme.ts +13 -73
- package/src/tools/forms/CodeEditor/lazy.tsx +1 -1
- 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/lazy.tsx +1 -1
- package/src/tools/forms/NotionEditor/README.md +237 -0
- package/src/tools/forms/NotionEditor/lazy.tsx +1 -1
- 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/ImageViewer/components/ImageToolbar.tsx +58 -47
- package/src/tools/media/ImageViewer/components/ImageViewer.tsx +27 -19
- package/src/tools/media/ImageViewer/lazy.tsx +1 -1
- package/src/tools/media/LottiePlayer/lazy.tsx +1 -1
- package/src/tools/media/VideoPlayer/VideoPlayer.tsx +28 -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/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,33 @@
|
|
|
1
|
+
# DiffViewer
|
|
2
|
+
|
|
3
|
+
Line-by-line diff viewer with unified or split layout. Reuses the `prism-react-renderer` instance from `PrettyCode` for optional syntax highlighting — no second highlighter is loaded.
|
|
4
|
+
|
|
5
|
+
Pass either `oldCode` + `newCode` (computed diff) or a pre-built `patch` string (unified format).
|
|
6
|
+
|
|
7
|
+
```tsx
|
|
8
|
+
import { DiffViewer } from '@djangocfg/ui-tools/diff-viewer';
|
|
9
|
+
|
|
10
|
+
<DiffViewer
|
|
11
|
+
oldCode="const x = 1;"
|
|
12
|
+
newCode="const x = 2;\nconst y = 3;"
|
|
13
|
+
language="ts"
|
|
14
|
+
layout="unified"
|
|
15
|
+
/>
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## Props
|
|
19
|
+
|
|
20
|
+
| Prop | Type | Default | Description |
|
|
21
|
+
|---|---|---|---|
|
|
22
|
+
| `oldCode` / `newCode` | `string` | — | Source pair to diff. Mutually exclusive with `patch`. |
|
|
23
|
+
| `patch` | `string` | — | Pre-computed unified diff. Mutually exclusive with `oldCode`/`newCode`. |
|
|
24
|
+
| `layout` | `'unified' \| 'split'` | `'unified'` | Single column vs side-by-side. |
|
|
25
|
+
| `language` | `Language \| string` | — | Prism language key for highlighting. Omit for plain monospace. |
|
|
26
|
+
| `oldTitle` | `string` | — | LHS file header label. |
|
|
27
|
+
| `newTitle` | `string` | — | RHS file header label. |
|
|
28
|
+
|
|
29
|
+
Storybook: `apps/storybook/stories/ui-tools/dev/DiffViewer.stories.tsx`
|
|
30
|
+
|
|
31
|
+
---
|
|
32
|
+
|
|
33
|
+
Adapted from jalcoui (MIT).
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
// Adapted from jalcoui (MIT) — github.com/jal-co/ui
|
|
2
|
+
|
|
3
|
+
'use client';
|
|
4
|
+
|
|
5
|
+
import * as React from 'react';
|
|
6
|
+
import { Check, Copy } from 'lucide-react';
|
|
7
|
+
import { cn } from '@djangocfg/ui-core/lib';
|
|
8
|
+
|
|
9
|
+
interface CopyButtonProps extends Omit<React.ComponentProps<'button'>, 'value'> {
|
|
10
|
+
value: string;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Tiny copy-to-clipboard button used in the DiffViewer header. Local
|
|
15
|
+
* component (not a hoist of `CopyAction` from `FloatingToolbar`) so the
|
|
16
|
+
* viewer can render without any provider context.
|
|
17
|
+
*
|
|
18
|
+
* Uses semantic tokens — the diff viewer adapts to the host theme,
|
|
19
|
+
* unlike `PrettyCode` which forces dark.
|
|
20
|
+
*/
|
|
21
|
+
export function CopyButton({ value, className, ...props }: CopyButtonProps) {
|
|
22
|
+
const [copied, setCopied] = React.useState(false);
|
|
23
|
+
|
|
24
|
+
const handleCopy = React.useCallback(() => {
|
|
25
|
+
void navigator.clipboard.writeText(value).then(() => {
|
|
26
|
+
setCopied(true);
|
|
27
|
+
window.setTimeout(() => setCopied(false), 1500);
|
|
28
|
+
});
|
|
29
|
+
}, [value]);
|
|
30
|
+
|
|
31
|
+
return (
|
|
32
|
+
<button
|
|
33
|
+
type="button"
|
|
34
|
+
onClick={handleCopy}
|
|
35
|
+
data-slot="diff-viewer-copy-button"
|
|
36
|
+
className={cn(
|
|
37
|
+
'inline-flex items-center gap-1.5 rounded-md px-2 py-1 text-xs transition-colors',
|
|
38
|
+
'text-muted-foreground hover:text-foreground hover:bg-muted',
|
|
39
|
+
'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring/50',
|
|
40
|
+
className,
|
|
41
|
+
)}
|
|
42
|
+
aria-label={copied ? 'Copied' : 'Copy code'}
|
|
43
|
+
{...props}
|
|
44
|
+
>
|
|
45
|
+
{copied ? <Check className="size-3.5" /> : <Copy className="size-3.5" />}
|
|
46
|
+
{copied ? 'Copied' : 'Copy'}
|
|
47
|
+
</button>
|
|
48
|
+
);
|
|
49
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
// Adapted from jalcoui (MIT) — github.com/jal-co/ui
|
|
2
|
+
|
|
3
|
+
'use client';
|
|
4
|
+
|
|
5
|
+
import * as React from 'react';
|
|
6
|
+
import { Highlight, type Language, type PrismTheme } from 'prism-react-renderer';
|
|
7
|
+
|
|
8
|
+
interface DiffLineContentProps {
|
|
9
|
+
content: string;
|
|
10
|
+
language: Language | null;
|
|
11
|
+
/**
|
|
12
|
+
* Prism palette resolved by the parent `DiffViewer` against the host
|
|
13
|
+
* theme. Forwarded here so we don't run a theme-detection hook on
|
|
14
|
+
* every line (a big diff can be thousands of rows).
|
|
15
|
+
*/
|
|
16
|
+
prismTheme: PrismTheme;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Single-line content cell. When `language` is non-null the line is
|
|
21
|
+
* tokenized by `prism-react-renderer` (the same instance PrettyCode
|
|
22
|
+
* uses). When null we render plain text — cheap path for `language`-less
|
|
23
|
+
* diffs and very large lists.
|
|
24
|
+
*
|
|
25
|
+
* Each line tokenizes independently. For diffs > a few thousand lines
|
|
26
|
+
* this can get warm; in practice the LCS already drops to context lines
|
|
27
|
+
* before we get there.
|
|
28
|
+
*/
|
|
29
|
+
export const DiffLineContent = React.memo(function DiffLineContent({
|
|
30
|
+
content,
|
|
31
|
+
language,
|
|
32
|
+
prismTheme,
|
|
33
|
+
}: DiffLineContentProps) {
|
|
34
|
+
if (!language) {
|
|
35
|
+
return <>{content || ' '}</>;
|
|
36
|
+
}
|
|
37
|
+
return (
|
|
38
|
+
<Highlight theme={prismTheme} code={content || ' '} language={language}>
|
|
39
|
+
{({ tokens, getTokenProps }) => (
|
|
40
|
+
<>
|
|
41
|
+
{tokens[0]?.map((token, key) => (
|
|
42
|
+
<span key={key} {...getTokenProps({ token })} />
|
|
43
|
+
))}
|
|
44
|
+
</>
|
|
45
|
+
)}
|
|
46
|
+
</Highlight>
|
|
47
|
+
);
|
|
48
|
+
});
|
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
// Adapted from jalcoui (MIT) — github.com/jal-co/ui
|
|
2
|
+
|
|
3
|
+
'use client';
|
|
4
|
+
|
|
5
|
+
import * as React from 'react';
|
|
6
|
+
import type { Language, PrismTheme } from 'prism-react-renderer';
|
|
7
|
+
import { Virtuoso } from 'react-virtuoso';
|
|
8
|
+
import { cn } from '@djangocfg/ui-core/lib';
|
|
9
|
+
import { DIFF_TONE_CLASSES, type DiffLine } from '../types';
|
|
10
|
+
import { DiffLineContent } from './DiffLineContent';
|
|
11
|
+
|
|
12
|
+
interface SplitViewProps {
|
|
13
|
+
lines: DiffLine[];
|
|
14
|
+
numWidth: number;
|
|
15
|
+
language: Language | null;
|
|
16
|
+
prismTheme: PrismTheme;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// See UnifiedView for context — same threshold + viewport size keeps both
|
|
20
|
+
// layouts behaviorally aligned.
|
|
21
|
+
const VIRTUALIZE_THRESHOLD = 100;
|
|
22
|
+
const VIRTUAL_VIEWPORT_HEIGHT = 480;
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Align consecutive `removed` / `added` runs into a pair of columns.
|
|
26
|
+
* Pure-add rows leave the left column empty; pure-remove rows leave the
|
|
27
|
+
* right column empty. Context rows duplicate on both sides.
|
|
28
|
+
*
|
|
29
|
+
* The pairing is the same shape jalcoui ships — we keep it so downstream
|
|
30
|
+
* consumers' visual baseline doesn't move.
|
|
31
|
+
*/
|
|
32
|
+
function pairLines(lines: DiffLine[]): Array<{
|
|
33
|
+
left: DiffLine | null;
|
|
34
|
+
right: DiffLine | null;
|
|
35
|
+
}> {
|
|
36
|
+
const pairs: Array<{ left: DiffLine | null; right: DiffLine | null }> = [];
|
|
37
|
+
|
|
38
|
+
let i = 0;
|
|
39
|
+
while (i < lines.length) {
|
|
40
|
+
const line = lines[i];
|
|
41
|
+
if (line.type === 'context') {
|
|
42
|
+
pairs.push({ left: line, right: line });
|
|
43
|
+
i++;
|
|
44
|
+
continue;
|
|
45
|
+
}
|
|
46
|
+
if (line.type === 'removed') {
|
|
47
|
+
const removed: DiffLine[] = [];
|
|
48
|
+
while (i < lines.length && lines[i].type === 'removed') {
|
|
49
|
+
removed.push(lines[i]);
|
|
50
|
+
i++;
|
|
51
|
+
}
|
|
52
|
+
const added: DiffLine[] = [];
|
|
53
|
+
while (i < lines.length && lines[i].type === 'added') {
|
|
54
|
+
added.push(lines[i]);
|
|
55
|
+
i++;
|
|
56
|
+
}
|
|
57
|
+
const maxLen = Math.max(removed.length, added.length);
|
|
58
|
+
for (let j = 0; j < maxLen; j++) {
|
|
59
|
+
pairs.push({
|
|
60
|
+
left: j < removed.length ? removed[j] : null,
|
|
61
|
+
right: j < added.length ? added[j] : null,
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
continue;
|
|
65
|
+
}
|
|
66
|
+
// line.type === 'added' with no preceding removed run
|
|
67
|
+
pairs.push({ left: null, right: line });
|
|
68
|
+
i++;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
return pairs;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function sidePrefix(side: 'old' | 'new', type: DiffLine['type'] | undefined) {
|
|
75
|
+
if (!type || type === 'context') return ' ';
|
|
76
|
+
if (side === 'old' && type === 'removed') return '-';
|
|
77
|
+
if (side === 'new' && type === 'added') return '+';
|
|
78
|
+
return ' ';
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
interface HalfProps {
|
|
82
|
+
line: DiffLine | null;
|
|
83
|
+
side: 'old' | 'new';
|
|
84
|
+
numWidth: number;
|
|
85
|
+
language: Language | null;
|
|
86
|
+
prismTheme: PrismTheme;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function Half({ line, side, numWidth, language, prismTheme }: HalfProps) {
|
|
90
|
+
const tone = line ? DIFF_TONE_CLASSES[line.type] : DIFF_TONE_CLASSES.context;
|
|
91
|
+
const gutterCh = `calc(${numWidth}ch + 1rem)`;
|
|
92
|
+
return (
|
|
93
|
+
<div
|
|
94
|
+
className={cn('grid items-start min-w-0', tone.row)}
|
|
95
|
+
style={{
|
|
96
|
+
gridTemplateColumns: `${gutterCh} 1.5rem minmax(0, 1fr)`,
|
|
97
|
+
}}
|
|
98
|
+
>
|
|
99
|
+
<div
|
|
100
|
+
className={cn(
|
|
101
|
+
'select-none px-2 text-right tabular-nums whitespace-nowrap',
|
|
102
|
+
tone.num,
|
|
103
|
+
)}
|
|
104
|
+
>
|
|
105
|
+
{(side === 'old' ? line?.oldNumber : line?.newNumber) ?? ''}
|
|
106
|
+
</div>
|
|
107
|
+
<div
|
|
108
|
+
className={cn(
|
|
109
|
+
'select-none text-center whitespace-nowrap',
|
|
110
|
+
tone.num,
|
|
111
|
+
)}
|
|
112
|
+
>
|
|
113
|
+
{sidePrefix(side, line?.type)}
|
|
114
|
+
</div>
|
|
115
|
+
<div
|
|
116
|
+
className={cn(
|
|
117
|
+
'whitespace-pre px-3 overflow-x-auto',
|
|
118
|
+
line ? tone.text : 'text-muted-foreground/40',
|
|
119
|
+
)}
|
|
120
|
+
>
|
|
121
|
+
{line ? (
|
|
122
|
+
<DiffLineContent
|
|
123
|
+
content={line.content}
|
|
124
|
+
language={language}
|
|
125
|
+
prismTheme={prismTheme}
|
|
126
|
+
/>
|
|
127
|
+
) : (
|
|
128
|
+
' '
|
|
129
|
+
)}
|
|
130
|
+
</div>
|
|
131
|
+
</div>
|
|
132
|
+
);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
interface PairRowProps {
|
|
136
|
+
pair: { left: DiffLine | null; right: DiffLine | null };
|
|
137
|
+
numWidth: number;
|
|
138
|
+
language: Language | null;
|
|
139
|
+
prismTheme: PrismTheme;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
function PairRow({ pair, numWidth, language, prismTheme }: PairRowProps) {
|
|
143
|
+
return (
|
|
144
|
+
<div className="grid grid-cols-2 font-mono text-[13px] leading-relaxed">
|
|
145
|
+
<div className="border-r border-border/40 min-w-0">
|
|
146
|
+
<Half
|
|
147
|
+
line={pair.left}
|
|
148
|
+
side="old"
|
|
149
|
+
numWidth={numWidth}
|
|
150
|
+
language={language}
|
|
151
|
+
prismTheme={prismTheme}
|
|
152
|
+
/>
|
|
153
|
+
</div>
|
|
154
|
+
<div className="min-w-0">
|
|
155
|
+
<Half
|
|
156
|
+
line={pair.right}
|
|
157
|
+
side="new"
|
|
158
|
+
numWidth={numWidth}
|
|
159
|
+
language={language}
|
|
160
|
+
prismTheme={prismTheme}
|
|
161
|
+
/>
|
|
162
|
+
</div>
|
|
163
|
+
</div>
|
|
164
|
+
);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Side-by-side diff. Left half = old, right half = new. Each row is a
|
|
169
|
+
* single grid wrapping two `<Half>` halves so a `<Virtuoso>` list can
|
|
170
|
+
* virtualize the whole stack with naturally synchronized scrolling.
|
|
171
|
+
*
|
|
172
|
+
* Both halves receive the SAME globally-computed `numWidth` so the two
|
|
173
|
+
* gutter columns are pixel-identical regardless of which side has the
|
|
174
|
+
* longer line numbers.
|
|
175
|
+
*/
|
|
176
|
+
export function SplitView({
|
|
177
|
+
lines,
|
|
178
|
+
numWidth,
|
|
179
|
+
language,
|
|
180
|
+
prismTheme,
|
|
181
|
+
}: SplitViewProps) {
|
|
182
|
+
const pairs = React.useMemo(() => pairLines(lines), [lines]);
|
|
183
|
+
|
|
184
|
+
if (pairs.length > VIRTUALIZE_THRESHOLD) {
|
|
185
|
+
return (
|
|
186
|
+
<div data-slot="diff-viewer-split">
|
|
187
|
+
<Virtuoso
|
|
188
|
+
data={pairs}
|
|
189
|
+
style={{ height: VIRTUAL_VIEWPORT_HEIGHT }}
|
|
190
|
+
// See UnifiedView for why `initialItemCount` is required —
|
|
191
|
+
// without it, embedded iframes start with a zero-height list.
|
|
192
|
+
initialItemCount={Math.min(pairs.length, 30)}
|
|
193
|
+
itemContent={(_, pair) => (
|
|
194
|
+
<PairRow
|
|
195
|
+
pair={pair}
|
|
196
|
+
numWidth={numWidth}
|
|
197
|
+
language={language}
|
|
198
|
+
prismTheme={prismTheme}
|
|
199
|
+
/>
|
|
200
|
+
)}
|
|
201
|
+
increaseViewportBy={{ top: 200, bottom: 400 }}
|
|
202
|
+
/>
|
|
203
|
+
</div>
|
|
204
|
+
);
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
return (
|
|
208
|
+
<div data-slot="diff-viewer-split">
|
|
209
|
+
{pairs.map((pair, idx) => (
|
|
210
|
+
<PairRow
|
|
211
|
+
key={idx}
|
|
212
|
+
pair={pair}
|
|
213
|
+
numWidth={numWidth}
|
|
214
|
+
language={language}
|
|
215
|
+
prismTheme={prismTheme}
|
|
216
|
+
/>
|
|
217
|
+
))}
|
|
218
|
+
</div>
|
|
219
|
+
);
|
|
220
|
+
}
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
// Adapted from jalcoui (MIT) — github.com/jal-co/ui
|
|
2
|
+
|
|
3
|
+
'use client';
|
|
4
|
+
|
|
5
|
+
import * as React from 'react';
|
|
6
|
+
import type { Language, PrismTheme } from 'prism-react-renderer';
|
|
7
|
+
import { Virtuoso } from 'react-virtuoso';
|
|
8
|
+
import { cn } from '@djangocfg/ui-core/lib';
|
|
9
|
+
import { DIFF_TONE_CLASSES, type DiffLine } from '../types';
|
|
10
|
+
import { DiffLineContent } from './DiffLineContent';
|
|
11
|
+
|
|
12
|
+
interface UnifiedViewProps {
|
|
13
|
+
lines: DiffLine[];
|
|
14
|
+
numWidth: number;
|
|
15
|
+
language: Language | null;
|
|
16
|
+
prismTheme: PrismTheme;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function linePrefix(type: DiffLine['type']) {
|
|
20
|
+
if (type === 'added') return '+';
|
|
21
|
+
if (type === 'removed') return '-';
|
|
22
|
+
return ' ';
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// Threshold above which we switch from a plain DOM table to a virtualized
|
|
26
|
+
// list. Mirrors LogViewer — under this size the DOM stays debuggable;
|
|
27
|
+
// above it tokenization + layout of thousands of rows starts to dominate
|
|
28
|
+
// scroll.
|
|
29
|
+
const VIRTUALIZE_THRESHOLD = 100;
|
|
30
|
+
// Height of the scrolling viewport when virtualized. Kept here (not a prop)
|
|
31
|
+
// because every existing consumer expects the viewer to expand to its
|
|
32
|
+
// content; only the >100-line path needs a fixed window to virtualize.
|
|
33
|
+
const VIRTUAL_VIEWPORT_HEIGHT = 480;
|
|
34
|
+
|
|
35
|
+
interface RowProps {
|
|
36
|
+
line: DiffLine;
|
|
37
|
+
numWidth: number;
|
|
38
|
+
language: Language | null;
|
|
39
|
+
prismTheme: PrismTheme;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* One unified-diff row. CSS grid (not a table cell) so a `<Virtuoso>`
|
|
44
|
+
* list can virtualize identical rows without a host `<table>` (the
|
|
45
|
+
* `display: table-row-group` virtual scrollers force on `<tbody>` doesn't
|
|
46
|
+
* play with arbitrary row heights).
|
|
47
|
+
*/
|
|
48
|
+
function Row({ line, numWidth, language, prismTheme }: RowProps) {
|
|
49
|
+
const tone = DIFF_TONE_CLASSES[line.type];
|
|
50
|
+
const gutterCh = `calc(${numWidth}ch + 1rem)`;
|
|
51
|
+
return (
|
|
52
|
+
<div
|
|
53
|
+
className={cn(
|
|
54
|
+
'grid items-start font-mono text-[13px] leading-relaxed',
|
|
55
|
+
tone.row,
|
|
56
|
+
)}
|
|
57
|
+
style={{
|
|
58
|
+
gridTemplateColumns: `${gutterCh} ${gutterCh} 1.5rem 1fr`,
|
|
59
|
+
}}
|
|
60
|
+
>
|
|
61
|
+
<div
|
|
62
|
+
className={cn(
|
|
63
|
+
'select-none px-2 text-right tabular-nums whitespace-nowrap',
|
|
64
|
+
tone.num,
|
|
65
|
+
)}
|
|
66
|
+
>
|
|
67
|
+
{line.oldNumber ?? ''}
|
|
68
|
+
</div>
|
|
69
|
+
<div
|
|
70
|
+
className={cn(
|
|
71
|
+
'select-none px-2 text-right tabular-nums whitespace-nowrap',
|
|
72
|
+
tone.num,
|
|
73
|
+
)}
|
|
74
|
+
>
|
|
75
|
+
{line.newNumber ?? ''}
|
|
76
|
+
</div>
|
|
77
|
+
<div
|
|
78
|
+
className={cn(
|
|
79
|
+
'select-none text-center whitespace-nowrap',
|
|
80
|
+
tone.num,
|
|
81
|
+
)}
|
|
82
|
+
>
|
|
83
|
+
{linePrefix(line.type)}
|
|
84
|
+
</div>
|
|
85
|
+
<div className={cn('whitespace-pre px-3', tone.text)}>
|
|
86
|
+
<DiffLineContent
|
|
87
|
+
content={line.content}
|
|
88
|
+
language={language}
|
|
89
|
+
prismTheme={prismTheme}
|
|
90
|
+
/>
|
|
91
|
+
</div>
|
|
92
|
+
</div>
|
|
93
|
+
);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Single-column unified diff. Two gutters on the left carry the old / new
|
|
98
|
+
* line numbers, the third (narrow) column is the +/-/space prefix, then
|
|
99
|
+
* the content. Background tint per row signals the change kind.
|
|
100
|
+
*
|
|
101
|
+
* The gutter widths come from the globally-computed `numWidth` (passed
|
|
102
|
+
* down from the viewer root). Using CSS grid with the same explicit
|
|
103
|
+
* `gridTemplateColumns` on every row guarantees pixel-identical gutters
|
|
104
|
+
* across all rows, even when content sizes vary.
|
|
105
|
+
*
|
|
106
|
+
* Diffs above {@link VIRTUALIZE_THRESHOLD} lines render through
|
|
107
|
+
* `<Virtuoso>` so 1k–10k-line diffs don't lock the main thread.
|
|
108
|
+
*/
|
|
109
|
+
export function UnifiedView({
|
|
110
|
+
lines,
|
|
111
|
+
numWidth,
|
|
112
|
+
language,
|
|
113
|
+
prismTheme,
|
|
114
|
+
}: UnifiedViewProps) {
|
|
115
|
+
if (lines.length > VIRTUALIZE_THRESHOLD) {
|
|
116
|
+
return (
|
|
117
|
+
<div data-slot="diff-viewer-unified">
|
|
118
|
+
<Virtuoso
|
|
119
|
+
data={lines}
|
|
120
|
+
style={{ height: VIRTUAL_VIEWPORT_HEIGHT }}
|
|
121
|
+
// `initialItemCount` forces SSR-style initial render so rows
|
|
122
|
+
// appear immediately on first paint instead of waiting for
|
|
123
|
+
// ResizeObserver — without it, embedded iframes (Storybook,
|
|
124
|
+
// docs sites) sometimes start at zero height and the list
|
|
125
|
+
// stays empty until a window resize.
|
|
126
|
+
initialItemCount={Math.min(lines.length, 30)}
|
|
127
|
+
itemContent={(_, line) => (
|
|
128
|
+
<Row
|
|
129
|
+
line={line}
|
|
130
|
+
numWidth={numWidth}
|
|
131
|
+
language={language}
|
|
132
|
+
prismTheme={prismTheme}
|
|
133
|
+
/>
|
|
134
|
+
)}
|
|
135
|
+
increaseViewportBy={{ top: 200, bottom: 400 }}
|
|
136
|
+
/>
|
|
137
|
+
</div>
|
|
138
|
+
);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
return (
|
|
142
|
+
<div data-slot="diff-viewer-unified">
|
|
143
|
+
{lines.map((line, i) => (
|
|
144
|
+
<Row
|
|
145
|
+
key={i}
|
|
146
|
+
line={line}
|
|
147
|
+
numWidth={numWidth}
|
|
148
|
+
language={language}
|
|
149
|
+
prismTheme={prismTheme}
|
|
150
|
+
/>
|
|
151
|
+
))}
|
|
152
|
+
</div>
|
|
153
|
+
);
|
|
154
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
// Adapted from jalcoui (MIT) — github.com/jal-co/ui
|
|
2
|
+
|
|
3
|
+
import { useMemo } from 'react';
|
|
4
|
+
import {
|
|
5
|
+
computeStats,
|
|
6
|
+
diffLines,
|
|
7
|
+
lineNumberWidth,
|
|
8
|
+
parseUnifiedPatch,
|
|
9
|
+
} from '../utils/computeDiff';
|
|
10
|
+
import type { DiffInput, DiffLine, DiffStats } from '../types';
|
|
11
|
+
|
|
12
|
+
interface UseDiffOptions {
|
|
13
|
+
input: DiffInput;
|
|
14
|
+
/** Context lines around each change-hunk. @default 3 */
|
|
15
|
+
context?: number;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
interface UseDiffResult {
|
|
19
|
+
lines: DiffLine[];
|
|
20
|
+
numWidth: number;
|
|
21
|
+
stats: DiffStats;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Memoize the diff computation so a re-render of the viewer (e.g. tab
|
|
26
|
+
* switch, header copy click) doesn't re-run the LCS. The dependency
|
|
27
|
+
* surface intentionally narrows to the two input strings / patch — the
|
|
28
|
+
* `context` knob too, since a different window emits a different line
|
|
29
|
+
* list.
|
|
30
|
+
*/
|
|
31
|
+
export function useDiff({ input, context = 3 }: UseDiffOptions): UseDiffResult {
|
|
32
|
+
const isPatch = 'patch' in input && typeof input.patch === 'string';
|
|
33
|
+
const oldCode = isPatch ? '' : input.oldCode;
|
|
34
|
+
const newCode = isPatch ? '' : input.newCode;
|
|
35
|
+
const patch = isPatch ? (input as { patch: string }).patch : '';
|
|
36
|
+
|
|
37
|
+
return useMemo(() => {
|
|
38
|
+
const lines = isPatch
|
|
39
|
+
? parseUnifiedPatch(patch)
|
|
40
|
+
: diffLines(oldCode, newCode, context);
|
|
41
|
+
return {
|
|
42
|
+
lines,
|
|
43
|
+
numWidth: lineNumberWidth(lines),
|
|
44
|
+
stats: computeStats(lines),
|
|
45
|
+
};
|
|
46
|
+
}, [isPatch, patch, oldCode, newCode, context]);
|
|
47
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
// Adapted from jalcoui (MIT) — github.com/jal-co/ui
|
|
2
|
+
//
|
|
3
|
+
// Shared tokenizer for DiffViewer. Reuses the same `prism-react-renderer`
|
|
4
|
+
// bundle that `PrettyCode` ships — no second highlighter is loaded. We
|
|
5
|
+
// also re-import `PrettyCode`'s grammar-registration hook so `bash`,
|
|
6
|
+
// `ruby`, `java`, `php` work in diffs without duplicating the dynamic
|
|
7
|
+
// imports.
|
|
8
|
+
|
|
9
|
+
import { useMemo } from 'react';
|
|
10
|
+
import type { Language } from 'prism-react-renderer';
|
|
11
|
+
import { useEnsurePrismLanguages } from '../../PrettyCode/registerPrismLanguages';
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Normalize a free-form language string into a Prism language key. Falls
|
|
15
|
+
* back to `text` when the key isn't one Prism knows about.
|
|
16
|
+
*/
|
|
17
|
+
export function normalizeLanguage(language: string | undefined): Language {
|
|
18
|
+
if (!language) return 'text' as Language;
|
|
19
|
+
const lang = language.toLowerCase();
|
|
20
|
+
switch (lang) {
|
|
21
|
+
case 'js':
|
|
22
|
+
return 'javascript' as Language;
|
|
23
|
+
case 'ts':
|
|
24
|
+
return 'typescript' as Language;
|
|
25
|
+
case 'py':
|
|
26
|
+
return 'python' as Language;
|
|
27
|
+
case 'rb':
|
|
28
|
+
return 'ruby' as Language;
|
|
29
|
+
case 'sh':
|
|
30
|
+
case 'shell':
|
|
31
|
+
return 'bash' as Language;
|
|
32
|
+
case 'html':
|
|
33
|
+
case 'xml':
|
|
34
|
+
return 'markup' as Language;
|
|
35
|
+
case 'md':
|
|
36
|
+
return 'markdown' as Language;
|
|
37
|
+
case 'yml':
|
|
38
|
+
return 'yaml' as Language;
|
|
39
|
+
case 'golang':
|
|
40
|
+
return 'go' as Language;
|
|
41
|
+
default:
|
|
42
|
+
return lang as Language;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Subscribe to PrettyCode's extra-grammar loader and return the resolved
|
|
48
|
+
* Prism language key. The component re-renders once `bash`/`ruby`/… are
|
|
49
|
+
* registered so the first paint isn't stuck on plain text.
|
|
50
|
+
*/
|
|
51
|
+
export function useDiffLanguage(language: string | undefined): Language {
|
|
52
|
+
useEnsurePrismLanguages();
|
|
53
|
+
return useMemo(() => normalizeLanguage(language), [language]);
|
|
54
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
// Adapted from jalcoui (MIT) — github.com/jal-co/ui
|
|
2
|
+
|
|
3
|
+
export { DiffViewer } from './DiffViewer';
|
|
4
|
+
export { DIFF_TONE_CLASSES } from './types';
|
|
5
|
+
export {
|
|
6
|
+
diffLines,
|
|
7
|
+
parseUnifiedPatch,
|
|
8
|
+
computeStats as computeDiffStats,
|
|
9
|
+
lineNumberWidth as diffLineNumberWidth,
|
|
10
|
+
} from './utils/computeDiff';
|
|
11
|
+
export { useDiff } from './hooks/useDiff';
|
|
12
|
+
export { normalizeLanguage as normalizeDiffLanguage } from './hooks/useHighlighter';
|
|
13
|
+
|
|
14
|
+
export type {
|
|
15
|
+
DiffLayout,
|
|
16
|
+
DiffLine,
|
|
17
|
+
DiffLineType,
|
|
18
|
+
DiffStats,
|
|
19
|
+
DiffInput,
|
|
20
|
+
DiffToneClasses,
|
|
21
|
+
DiffViewerProps,
|
|
22
|
+
} from './types';
|
|
@@ -0,0 +1,22 @@
|
|
|
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 { DiffViewerProps } from './types';
|
|
7
|
+
|
|
8
|
+
export const LazyDiffViewer = createLazyComponent<DiffViewerProps>(
|
|
9
|
+
() =>
|
|
10
|
+
import('./DiffViewer').then((mod) => ({ default: mod.DiffViewer })),
|
|
11
|
+
{
|
|
12
|
+
displayName: 'LazyDiffViewer',
|
|
13
|
+
fallback: (
|
|
14
|
+
<div
|
|
15
|
+
data-slot="diff-viewer-skeleton"
|
|
16
|
+
className="h-48 w-full animate-pulse rounded-xl border border-border/60 bg-muted"
|
|
17
|
+
/>
|
|
18
|
+
),
|
|
19
|
+
},
|
|
20
|
+
);
|
|
21
|
+
|
|
22
|
+
export type { DiffViewerProps } from './types';
|