@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,195 @@
|
|
|
1
|
+
// Adapted from jalcoui (MIT) — github.com/jal-co/ui
|
|
2
|
+
//
|
|
3
|
+
// GitHub-style activity heatmap that visualizes daily counts as a
|
|
4
|
+
// color-intensity grid. Auto-sizes blocks to fit the container by default.
|
|
5
|
+
//
|
|
6
|
+
// Port notes:
|
|
7
|
+
// - Cell colors resolve through `useThemeColor('primary')` + `alpha(...)`,
|
|
8
|
+
// not raw `bg-emerald-*` (CONTRACT §2, AUDIT §1). The five emerald
|
|
9
|
+
// stops in the original collapse to five opacity stops on the active
|
|
10
|
+
// theme's `primary` token, which means the heatmap follows the theme
|
|
11
|
+
// preset automatically.
|
|
12
|
+
// - Intensity bucketing routed through `getIntensity` from
|
|
13
|
+
// `@djangocfg/ui-core/lib` (CONTRACT shared util added in Phase 0).
|
|
14
|
+
// - Container sizing routed through `useResizeObserver` from
|
|
15
|
+
// `@djangocfg/ui-core/hooks` (shared RO from Phase 0), replacing the
|
|
16
|
+
// local `ResizeObserver` instance in the source.
|
|
17
|
+
// - Tooltips: the original wrapped every cell in its own Radix
|
|
18
|
+
// `Tooltip.Provider`. Mounting a provider per cell violates CONTRACT
|
|
19
|
+
// §5 ("no nested providers") and explodes to 365 portals on a default
|
|
20
|
+
// 52-week graph. The port uses the native `title` attribute on each
|
|
21
|
+
// cell instead — accessible, no provider dependency, no portal.
|
|
22
|
+
|
|
23
|
+
'use client';
|
|
24
|
+
|
|
25
|
+
import * as React from 'react';
|
|
26
|
+
import { cn } from '@djangocfg/ui-core/lib';
|
|
27
|
+
import { getIntensity } from '@djangocfg/ui-core/lib';
|
|
28
|
+
import { useResizeObserver } from '@djangocfg/ui-core/hooks';
|
|
29
|
+
import { useThemeColor, alpha } from '@djangocfg/ui-core/styles/palette';
|
|
30
|
+
|
|
31
|
+
import {
|
|
32
|
+
DAY_LABEL_INDICES,
|
|
33
|
+
DAY_LABEL_WIDTH,
|
|
34
|
+
DAY_LABELS,
|
|
35
|
+
DEFAULT_INTENSITY_OPACITY,
|
|
36
|
+
DEFAULT_INTENSITY_THRESHOLDS,
|
|
37
|
+
GAP,
|
|
38
|
+
MONTH_LABEL_HEIGHT,
|
|
39
|
+
type ActivityGraphProps,
|
|
40
|
+
} from './types';
|
|
41
|
+
import { buildWeeks, formatDate, getMonthLabels } from './utils';
|
|
42
|
+
|
|
43
|
+
export function ActivityGraph({
|
|
44
|
+
data,
|
|
45
|
+
intensityOpacity = DEFAULT_INTENSITY_OPACITY,
|
|
46
|
+
blockSize: fixedBlockSize,
|
|
47
|
+
blockRadius = 2,
|
|
48
|
+
weeks: weekCount = 52,
|
|
49
|
+
className,
|
|
50
|
+
...props
|
|
51
|
+
}: ActivityGraphProps) {
|
|
52
|
+
const containerRef = React.useRef<HTMLDivElement>(null);
|
|
53
|
+
const { width: containerWidth } = useResizeObserver(containerRef);
|
|
54
|
+
|
|
55
|
+
const isAutoFit = fixedBlockSize == null;
|
|
56
|
+
|
|
57
|
+
// Auto-fit cell size based on container width. Falls back to a small
|
|
58
|
+
// default until the first measurement lands (consistent with original).
|
|
59
|
+
const autoSize = React.useMemo(() => {
|
|
60
|
+
if (!isAutoFit || containerWidth <= 0) return null;
|
|
61
|
+
const available = containerWidth - DAY_LABEL_WIDTH;
|
|
62
|
+
const size = (available - GAP * (weekCount - 1)) / weekCount;
|
|
63
|
+
return Math.max(4, Math.floor(size));
|
|
64
|
+
}, [isAutoFit, containerWidth, weekCount]);
|
|
65
|
+
|
|
66
|
+
const blockSize = fixedBlockSize ?? autoSize ?? 10;
|
|
67
|
+
const showGraph = !isAutoFit || autoSize != null;
|
|
68
|
+
|
|
69
|
+
// Resolve the theme color once; map each intensity bucket to an
|
|
70
|
+
// opacity stop applied to that color (see CONTRACT §3 — never raw
|
|
71
|
+
// Tailwind class for parametric colors).
|
|
72
|
+
const primary = useThemeColor('primary');
|
|
73
|
+
const intensityColors = React.useMemo(
|
|
74
|
+
() => intensityOpacity.map((op) => alpha(primary, op)),
|
|
75
|
+
[primary, intensityOpacity],
|
|
76
|
+
);
|
|
77
|
+
|
|
78
|
+
const weeks = React.useMemo(() => buildWeeks(data, weekCount), [data, weekCount]);
|
|
79
|
+
const maxCount = React.useMemo(
|
|
80
|
+
() => data.reduce((m, d) => (d.count > m ? d.count : m), 0),
|
|
81
|
+
[data],
|
|
82
|
+
);
|
|
83
|
+
const monthLabels = React.useMemo(
|
|
84
|
+
() => getMonthLabels(weeks, blockSize),
|
|
85
|
+
[weeks, blockSize],
|
|
86
|
+
);
|
|
87
|
+
|
|
88
|
+
const gridWidth = weeks.length * (blockSize + GAP) - GAP;
|
|
89
|
+
const gridHeight = 7 * (blockSize + GAP) - GAP;
|
|
90
|
+
const totalWidth = DAY_LABEL_WIDTH + gridWidth;
|
|
91
|
+
|
|
92
|
+
return (
|
|
93
|
+
<div
|
|
94
|
+
ref={containerRef}
|
|
95
|
+
className={cn(isAutoFit ? 'w-full' : 'overflow-x-auto', className)}
|
|
96
|
+
role="img"
|
|
97
|
+
aria-label="Activity graph"
|
|
98
|
+
data-slot="activity-graph"
|
|
99
|
+
{...props}
|
|
100
|
+
>
|
|
101
|
+
{showGraph && (
|
|
102
|
+
<div
|
|
103
|
+
className="flex flex-col gap-2"
|
|
104
|
+
style={{ minWidth: isAutoFit ? undefined : totalWidth }}
|
|
105
|
+
>
|
|
106
|
+
<div
|
|
107
|
+
className="relative"
|
|
108
|
+
style={{
|
|
109
|
+
width: isAutoFit ? '100%' : totalWidth,
|
|
110
|
+
height: MONTH_LABEL_HEIGHT + gridHeight,
|
|
111
|
+
}}
|
|
112
|
+
>
|
|
113
|
+
{monthLabels.map((m, i) => (
|
|
114
|
+
<span
|
|
115
|
+
key={`${m.label}-${i}`}
|
|
116
|
+
className="absolute text-[10px] leading-none text-muted-foreground"
|
|
117
|
+
style={{ left: DAY_LABEL_WIDTH + m.offset, top: 0 }}
|
|
118
|
+
>
|
|
119
|
+
{m.label}
|
|
120
|
+
</span>
|
|
121
|
+
))}
|
|
122
|
+
|
|
123
|
+
{DAY_LABELS.map((label, i) => (
|
|
124
|
+
<span
|
|
125
|
+
key={label}
|
|
126
|
+
className="absolute text-[10px] leading-none text-muted-foreground"
|
|
127
|
+
style={{
|
|
128
|
+
left: 0,
|
|
129
|
+
top:
|
|
130
|
+
MONTH_LABEL_HEIGHT +
|
|
131
|
+
DAY_LABEL_INDICES[i] * (blockSize + GAP) +
|
|
132
|
+
blockSize / 2 -
|
|
133
|
+
4,
|
|
134
|
+
}}
|
|
135
|
+
>
|
|
136
|
+
{label}
|
|
137
|
+
</span>
|
|
138
|
+
))}
|
|
139
|
+
|
|
140
|
+
<div
|
|
141
|
+
className="absolute"
|
|
142
|
+
style={{
|
|
143
|
+
left: DAY_LABEL_WIDTH,
|
|
144
|
+
top: MONTH_LABEL_HEIGHT,
|
|
145
|
+
display: 'flex',
|
|
146
|
+
gap: GAP,
|
|
147
|
+
}}
|
|
148
|
+
>
|
|
149
|
+
{weeks.map((week, wi) => (
|
|
150
|
+
<div key={wi} style={{ display: 'flex', flexDirection: 'column', gap: GAP }}>
|
|
151
|
+
{week.map((day, di) => {
|
|
152
|
+
const ratio = maxCount > 0 ? day.count / maxCount : 0;
|
|
153
|
+
const intensity = getIntensity(ratio, [...DEFAULT_INTENSITY_THRESHOLDS]);
|
|
154
|
+
const title = `${day.count} contribution${day.count === 1 ? '' : 's'} — ${formatDate(day.date)}`;
|
|
155
|
+
return (
|
|
156
|
+
<div
|
|
157
|
+
key={di}
|
|
158
|
+
data-slot="activity-graph-cell"
|
|
159
|
+
data-intensity={intensity}
|
|
160
|
+
className="transition-colors"
|
|
161
|
+
title={title}
|
|
162
|
+
style={{
|
|
163
|
+
width: blockSize,
|
|
164
|
+
height: blockSize,
|
|
165
|
+
borderRadius: blockRadius,
|
|
166
|
+
backgroundColor: intensityColors[intensity],
|
|
167
|
+
}}
|
|
168
|
+
/>
|
|
169
|
+
);
|
|
170
|
+
})}
|
|
171
|
+
</div>
|
|
172
|
+
))}
|
|
173
|
+
</div>
|
|
174
|
+
</div>
|
|
175
|
+
|
|
176
|
+
<div className="flex items-center gap-1.5 self-end text-[10px] text-muted-foreground">
|
|
177
|
+
<span>Less</span>
|
|
178
|
+
{intensityColors.map((color, i) => (
|
|
179
|
+
<div
|
|
180
|
+
key={i}
|
|
181
|
+
style={{
|
|
182
|
+
width: blockSize,
|
|
183
|
+
height: blockSize,
|
|
184
|
+
borderRadius: blockRadius,
|
|
185
|
+
backgroundColor: color,
|
|
186
|
+
}}
|
|
187
|
+
/>
|
|
188
|
+
))}
|
|
189
|
+
<span>More</span>
|
|
190
|
+
</div>
|
|
191
|
+
</div>
|
|
192
|
+
)}
|
|
193
|
+
</div>
|
|
194
|
+
);
|
|
195
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# ActivityGraph
|
|
2
|
+
|
|
3
|
+
GitHub-style contribution heatmap. Maps daily counts to opacity stops of the active theme `primary` color via `useThemeColor` + `useResizeObserver`.
|
|
4
|
+
|
|
5
|
+
```tsx
|
|
6
|
+
import { ActivityGraph } from '@djangocfg/ui-tools/activity-graph';
|
|
7
|
+
|
|
8
|
+
<ActivityGraph
|
|
9
|
+
data={[{ date: '2025-01-12', count: 4 }, { date: '2025-01-13', count: 0 }]}
|
|
10
|
+
weeks={52}
|
|
11
|
+
/>
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
## Props
|
|
15
|
+
|
|
16
|
+
| Prop | Type | Default | Description |
|
|
17
|
+
|---|---|---|---|
|
|
18
|
+
| `data` | `ActivityEntry[]` | — | Daily entries `{ date, count }`. Duplicate dates are summed. |
|
|
19
|
+
| `weeks` | `number` | `52` | Trailing week count. |
|
|
20
|
+
| `blockSize` | `number` | auto | Cell size in px. When omitted, auto-fits the container. |
|
|
21
|
+
| `blockRadius` | `number` | `2` | Cell border-radius in px. |
|
|
22
|
+
| `intensityOpacity` | `[number,number,number,number,number]` | `[0.08, 0.18, 0.35, 0.6, 0.95]` | Opacity stops applied to the theme `primary` color (intensity 0–4). |
|
|
23
|
+
|
|
24
|
+
Storybook: `apps/storybook/stories/ui-tools/visual/ActivityGraph.stories.tsx`
|
|
25
|
+
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
Adapted from jalcoui (MIT).
|
|
@@ -0,0 +1,21 @@
|
|
|
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 { ActivityGraphProps } from './types';
|
|
7
|
+
|
|
8
|
+
export const LazyActivityGraph = createLazyComponent<ActivityGraphProps>(
|
|
9
|
+
() => import('./ActivityGraph').then((mod) => ({ default: mod.ActivityGraph })),
|
|
10
|
+
{
|
|
11
|
+
displayName: 'LazyActivityGraph',
|
|
12
|
+
fallback: (
|
|
13
|
+
<div
|
|
14
|
+
data-slot="activity-graph-skeleton"
|
|
15
|
+
className="h-[120px] w-full animate-pulse rounded-md bg-muted"
|
|
16
|
+
/>
|
|
17
|
+
),
|
|
18
|
+
},
|
|
19
|
+
);
|
|
20
|
+
|
|
21
|
+
export type { ActivityGraphProps, ActivityEntry } from './types';
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
// Adapted from jalcoui (MIT) — github.com/jal-co/ui
|
|
2
|
+
|
|
3
|
+
import type * as React from 'react';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Single day entry in the contribution heatmap.
|
|
7
|
+
*/
|
|
8
|
+
export interface ActivityEntry {
|
|
9
|
+
/** ISO date string (YYYY-MM-DD). */
|
|
10
|
+
date: string;
|
|
11
|
+
/** Activity count for this date. */
|
|
12
|
+
count: number;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Five opacity stops applied to the resolved theme `primary` color for
|
|
17
|
+
* intensity levels 0–4. Level 0 is the empty / no-activity surface and is
|
|
18
|
+
* intentionally low (almost equal to `bg-muted`).
|
|
19
|
+
*
|
|
20
|
+
* Audit notes (AUDIT.md §1) collapse the full emerald scale to
|
|
21
|
+
* `alpha(primary, …)`, so the same five stops cover light and dark themes.
|
|
22
|
+
*/
|
|
23
|
+
export const DEFAULT_INTENSITY_OPACITY: readonly [number, number, number, number, number] = [
|
|
24
|
+
0.08, 0.18, 0.35, 0.6, 0.95,
|
|
25
|
+
] as const;
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Thresholds (ratios of `value / max`) that map a normalized count into
|
|
29
|
+
* one of four non-zero intensity buckets. Combined with the empty bucket
|
|
30
|
+
* for `count === 0` they yield the 5 levels expected by the heatmap.
|
|
31
|
+
*
|
|
32
|
+
* Mirrors the original jalcoui `getIntensity` (`<=0.25 / 0.5 / 0.75`).
|
|
33
|
+
*/
|
|
34
|
+
export const DEFAULT_INTENSITY_THRESHOLDS: readonly [number, number, number] = [0.25, 0.5, 0.75];
|
|
35
|
+
|
|
36
|
+
export interface ActivityGraphProps extends Omit<React.ComponentProps<'div'>, 'children'> {
|
|
37
|
+
/** Activity data entries. Duplicates with the same `date` are summed. */
|
|
38
|
+
data: ActivityEntry[];
|
|
39
|
+
/**
|
|
40
|
+
* Override the 5 opacity stops applied to the active theme `primary`
|
|
41
|
+
* color. Index 0 is the empty surface; index 4 is the peak.
|
|
42
|
+
*/
|
|
43
|
+
intensityOpacity?: readonly [number, number, number, number, number];
|
|
44
|
+
/**
|
|
45
|
+
* Fixed cell size in pixels. When omitted the component auto-sizes
|
|
46
|
+
* blocks to fill the available container width via `useResizeObserver`.
|
|
47
|
+
*/
|
|
48
|
+
blockSize?: number;
|
|
49
|
+
/** Cell border radius in pixels. @default 2 */
|
|
50
|
+
blockRadius?: number;
|
|
51
|
+
/** Number of trailing weeks to display. @default 52 */
|
|
52
|
+
weeks?: number;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export const GAP = 2;
|
|
56
|
+
export const DAY_LABEL_WIDTH = 28;
|
|
57
|
+
export const MONTH_LABEL_HEIGHT = 16;
|
|
58
|
+
export const DAY_LABELS = ['Mon', 'Wed', 'Fri'] as const;
|
|
59
|
+
export const DAY_LABEL_INDICES = [1, 3, 5] as const;
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
// Adapted from jalcoui (MIT) — github.com/jal-co/ui
|
|
2
|
+
|
|
3
|
+
import type { ActivityEntry } from './types';
|
|
4
|
+
import { GAP } from './types';
|
|
5
|
+
|
|
6
|
+
export interface WeekDay {
|
|
7
|
+
date: Date;
|
|
8
|
+
count: number;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Bucket the supplied entries into a fixed-size grid that ends on the
|
|
13
|
+
* current week and spans `weekCount` weeks. Missing dates fill with 0.
|
|
14
|
+
*/
|
|
15
|
+
export function buildWeeks(data: ActivityEntry[], weekCount: number): WeekDay[][] {
|
|
16
|
+
const countMap = new Map<string, number>();
|
|
17
|
+
for (const entry of data) {
|
|
18
|
+
countMap.set(entry.date, (countMap.get(entry.date) ?? 0) + entry.count);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const today = new Date();
|
|
22
|
+
today.setHours(0, 0, 0, 0);
|
|
23
|
+
const todayDay = today.getDay();
|
|
24
|
+
|
|
25
|
+
const endOfWeek = new Date(today);
|
|
26
|
+
endOfWeek.setDate(today.getDate() + (6 - todayDay));
|
|
27
|
+
|
|
28
|
+
const totalDays = weekCount * 7;
|
|
29
|
+
const startDate = new Date(endOfWeek);
|
|
30
|
+
startDate.setDate(endOfWeek.getDate() - totalDays + 1);
|
|
31
|
+
|
|
32
|
+
const weeks: WeekDay[][] = [];
|
|
33
|
+
let currentWeek: WeekDay[] = [];
|
|
34
|
+
|
|
35
|
+
for (let i = 0; i < totalDays; i++) {
|
|
36
|
+
const d = new Date(startDate);
|
|
37
|
+
d.setDate(startDate.getDate() + i);
|
|
38
|
+
const key = d.toISOString().slice(0, 10);
|
|
39
|
+
currentWeek.push({ date: d, count: countMap.get(key) ?? 0 });
|
|
40
|
+
|
|
41
|
+
if (currentWeek.length === 7) {
|
|
42
|
+
weeks.push(currentWeek);
|
|
43
|
+
currentWeek = [];
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
if (currentWeek.length > 0) {
|
|
48
|
+
weeks.push(currentWeek);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return weeks;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Find the column offset for the first week of every new month. Used to
|
|
56
|
+
* lay month labels along the top of the grid.
|
|
57
|
+
*/
|
|
58
|
+
export function getMonthLabels(
|
|
59
|
+
weeks: WeekDay[][],
|
|
60
|
+
blockSize: number,
|
|
61
|
+
): { label: string; offset: number }[] {
|
|
62
|
+
const months: { label: string; offset: number }[] = [];
|
|
63
|
+
let lastKey = '';
|
|
64
|
+
|
|
65
|
+
for (let w = 0; w < weeks.length; w++) {
|
|
66
|
+
const firstDay = weeks[w][0];
|
|
67
|
+
const key = `${firstDay.date.getFullYear()}-${firstDay.date.getMonth()}`;
|
|
68
|
+
|
|
69
|
+
if (key !== lastKey) {
|
|
70
|
+
months.push({
|
|
71
|
+
label: firstDay.date.toLocaleString('en-US', { month: 'short' }),
|
|
72
|
+
offset: w * (blockSize + GAP),
|
|
73
|
+
});
|
|
74
|
+
lastKey = key;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
return months;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export function formatDate(date: Date): string {
|
|
82
|
+
return date.toLocaleDateString('en-US', {
|
|
83
|
+
weekday: 'short',
|
|
84
|
+
month: 'short',
|
|
85
|
+
day: 'numeric',
|
|
86
|
+
year: 'numeric',
|
|
87
|
+
});
|
|
88
|
+
}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
// Adapted from jalcoui (MIT) — github.com/jal-co/ui
|
|
2
|
+
//
|
|
3
|
+
// Topological git graph with rail lines showing branch forks, merges, and
|
|
4
|
+
// commit ancestry. Renders the same visual as GitKraken / Fork / Tower.
|
|
5
|
+
//
|
|
6
|
+
// Composer only — heavy lifting lives in:
|
|
7
|
+
// - hooks/useGraphLayout.ts topology pass
|
|
8
|
+
// - hooks/useLaneColors.ts theme-aware categorical palette
|
|
9
|
+
// - components/Rails.tsx SVG rails + dot
|
|
10
|
+
// - components/CommitRow.tsx row body (refs, message, meta)
|
|
11
|
+
// - components/CommitDetail.tsx popover detail
|
|
12
|
+
//
|
|
13
|
+
// Project rules exercised here: semantic tokens for chrome (no raw scales),
|
|
14
|
+
// theme-derived hex colors for SVG strokes (no Tailwind classes inside
|
|
15
|
+
// `stroke=`), no nested overlay providers, attribution header, lazy entry.
|
|
16
|
+
|
|
17
|
+
'use client';
|
|
18
|
+
|
|
19
|
+
import * as React from 'react';
|
|
20
|
+
import { cn } from '@djangocfg/ui-core/lib';
|
|
21
|
+
import type { CommitGraphProps } from './types';
|
|
22
|
+
import { useGraphLayout } from './hooks/useGraphLayout';
|
|
23
|
+
import { useLaneColors, pickLaneColor } from './hooks/useLaneColors';
|
|
24
|
+
import { CommitRow } from './components/CommitRow';
|
|
25
|
+
|
|
26
|
+
function CommitGraph({
|
|
27
|
+
commits,
|
|
28
|
+
truncateHash = 7,
|
|
29
|
+
railWidth = 24,
|
|
30
|
+
className,
|
|
31
|
+
...props
|
|
32
|
+
}: CommitGraphProps) {
|
|
33
|
+
const laneColors = useLaneColors();
|
|
34
|
+
const { rows, maxRails } = useGraphLayout(commits, laneColors);
|
|
35
|
+
const svgWidth = maxRails * railWidth;
|
|
36
|
+
|
|
37
|
+
if (rows.length === 0) {
|
|
38
|
+
return (
|
|
39
|
+
<div
|
|
40
|
+
data-slot="commit-graph"
|
|
41
|
+
className={cn(
|
|
42
|
+
'flex items-center justify-center rounded-xl border border-border/60 bg-card py-10 text-sm text-muted-foreground shadow-sm',
|
|
43
|
+
className,
|
|
44
|
+
)}
|
|
45
|
+
{...props}
|
|
46
|
+
>
|
|
47
|
+
No commits.
|
|
48
|
+
</div>
|
|
49
|
+
);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
return (
|
|
53
|
+
<div
|
|
54
|
+
data-slot="commit-graph"
|
|
55
|
+
className={cn(
|
|
56
|
+
'overflow-hidden rounded-xl border border-border/60 bg-card shadow-sm',
|
|
57
|
+
className,
|
|
58
|
+
)}
|
|
59
|
+
style={{ ['--commit-graph-rail-width' as string]: `${svgWidth}px` }}
|
|
60
|
+
{...props}
|
|
61
|
+
>
|
|
62
|
+
<div className="overflow-x-auto">
|
|
63
|
+
{rows.map((row, i) => (
|
|
64
|
+
<CommitRow
|
|
65
|
+
key={`${row.commit.hash}-${i}`}
|
|
66
|
+
row={row}
|
|
67
|
+
prevRow={i > 0 ? rows[i - 1] : null}
|
|
68
|
+
railWidth={railWidth}
|
|
69
|
+
maxRails={maxRails}
|
|
70
|
+
truncateHash={truncateHash}
|
|
71
|
+
laneColors={laneColors}
|
|
72
|
+
railColor={pickLaneColor(laneColors, row.rail)}
|
|
73
|
+
/>
|
|
74
|
+
))}
|
|
75
|
+
</div>
|
|
76
|
+
</div>
|
|
77
|
+
);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export { CommitGraph };
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# CommitGraph
|
|
2
|
+
|
|
3
|
+
Git-style commit rail visualisation. Computes rail topology from `parents` and renders SVG rails + edges. Lane colors come from `useStylePresets()` so every theme preset re-skins consistently.
|
|
4
|
+
|
|
5
|
+
```tsx
|
|
6
|
+
import { CommitGraph } from '@djangocfg/ui-tools/commit-graph';
|
|
7
|
+
|
|
8
|
+
<CommitGraph
|
|
9
|
+
commits={[
|
|
10
|
+
{ hash: 'a1b2c3d', message: 'init', author: { name: 'Mark' }, date: '2025-01-01', parents: [] },
|
|
11
|
+
{ hash: 'd4e5f6g', message: 'feat: auth', author: { name: 'Mark' }, date: '2025-01-02', parents: ['a1b2c3d'] },
|
|
12
|
+
]}
|
|
13
|
+
/>
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
## Props
|
|
17
|
+
|
|
18
|
+
| Prop | Type | Default | Description |
|
|
19
|
+
|---|---|---|---|
|
|
20
|
+
| `commits` | `Commit[]` | — | Commits in topological order (newest first). Merge = two+ `parents`. |
|
|
21
|
+
| `truncateHash` | `number` | `7` | Number of hash characters to display. |
|
|
22
|
+
| `railWidth` | `number` | `24` | Pixel width per rail column. |
|
|
23
|
+
|
|
24
|
+
Storybook: `apps/storybook/stories/ui-tools/visual/CommitGraph.stories.tsx`
|
|
25
|
+
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
Adapted from jalcoui (MIT).
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
// Adapted from jalcoui (MIT) — github.com/jal-co/ui
|
|
2
|
+
|
|
3
|
+
'use client';
|
|
4
|
+
|
|
5
|
+
/* eslint-disable @next/next/no-img-element */
|
|
6
|
+
|
|
7
|
+
import * as React from 'react';
|
|
8
|
+
import { Popover, PopoverContent, PopoverTrigger } from '@djangocfg/ui-core';
|
|
9
|
+
import { alpha } from '@djangocfg/ui-core/styles/palette';
|
|
10
|
+
import type { Commit } from '../types';
|
|
11
|
+
import { formatFullDate, initials } from '../utils';
|
|
12
|
+
|
|
13
|
+
interface CommitDetailProps {
|
|
14
|
+
commit: Commit;
|
|
15
|
+
hashLength: number;
|
|
16
|
+
railColor: string;
|
|
17
|
+
children: React.ReactNode;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Popover with the full commit message, refs, tag and parent hashes. The
|
|
22
|
+
* trigger is the row button — the parent component owns the row markup and
|
|
23
|
+
* passes it through `children`.
|
|
24
|
+
*
|
|
25
|
+
* Requires the host app to mount `<UiProviders>` at the root (we do not
|
|
26
|
+
* remount a Popover provider here — see CONTRACT §5).
|
|
27
|
+
*/
|
|
28
|
+
export function CommitDetail({
|
|
29
|
+
commit,
|
|
30
|
+
hashLength,
|
|
31
|
+
railColor,
|
|
32
|
+
children,
|
|
33
|
+
}: CommitDetailProps) {
|
|
34
|
+
// Pre-compute style strings outside JSX (per project rule: hoist derived
|
|
35
|
+
// values into named consts before `return (...)`).
|
|
36
|
+
const tagStyle = {
|
|
37
|
+
backgroundColor: alpha(railColor, 0.125),
|
|
38
|
+
color: railColor,
|
|
39
|
+
};
|
|
40
|
+
const initialsLabel = initials(commit.author.name);
|
|
41
|
+
|
|
42
|
+
return (
|
|
43
|
+
<Popover>
|
|
44
|
+
<PopoverTrigger asChild>{children}</PopoverTrigger>
|
|
45
|
+
<PopoverContent side="right" sideOffset={8} className="w-80 p-3">
|
|
46
|
+
<div className="flex flex-col gap-2">
|
|
47
|
+
<p className="text-sm font-medium leading-snug">{commit.message}</p>
|
|
48
|
+
|
|
49
|
+
<div className="flex flex-wrap items-center gap-2 text-xs text-muted-foreground">
|
|
50
|
+
<span className="inline-flex items-center gap-1.5">
|
|
51
|
+
{commit.author.avatarUrl ? (
|
|
52
|
+
<img
|
|
53
|
+
src={commit.author.avatarUrl}
|
|
54
|
+
alt=""
|
|
55
|
+
width={14}
|
|
56
|
+
height={14}
|
|
57
|
+
className="size-3.5 rounded-full border border-border/60 bg-muted"
|
|
58
|
+
/>
|
|
59
|
+
) : (
|
|
60
|
+
<span className="flex size-3.5 items-center justify-center rounded-full bg-muted text-[7px] font-bold">
|
|
61
|
+
{initialsLabel}
|
|
62
|
+
</span>
|
|
63
|
+
)}
|
|
64
|
+
{commit.author.name}
|
|
65
|
+
</span>
|
|
66
|
+
<span className="text-border">·</span>
|
|
67
|
+
<code className="rounded bg-muted px-1 py-0.5 font-mono text-[10px]">
|
|
68
|
+
{commit.hash.slice(0, hashLength)}
|
|
69
|
+
</code>
|
|
70
|
+
</div>
|
|
71
|
+
|
|
72
|
+
<div className="text-[11px] text-muted-foreground">
|
|
73
|
+
{formatFullDate(commit.date)}
|
|
74
|
+
</div>
|
|
75
|
+
|
|
76
|
+
{(commit.refs || commit.tag) && (
|
|
77
|
+
<div className="flex flex-wrap gap-1">
|
|
78
|
+
{commit.refs?.map((ref) => (
|
|
79
|
+
<span
|
|
80
|
+
key={ref}
|
|
81
|
+
className="inline-flex items-center gap-1 rounded-md border border-border/60 bg-muted/50 px-1.5 py-0.5 text-[10px] font-medium text-muted-foreground"
|
|
82
|
+
>
|
|
83
|
+
{ref}
|
|
84
|
+
</span>
|
|
85
|
+
))}
|
|
86
|
+
{commit.tag && (
|
|
87
|
+
<span
|
|
88
|
+
className="inline-flex items-center rounded-md px-1.5 py-0.5 text-[10px] font-medium"
|
|
89
|
+
style={tagStyle}
|
|
90
|
+
>
|
|
91
|
+
{commit.tag}
|
|
92
|
+
</span>
|
|
93
|
+
)}
|
|
94
|
+
</div>
|
|
95
|
+
)}
|
|
96
|
+
|
|
97
|
+
{commit.parents.length > 0 && (
|
|
98
|
+
<div className="text-[10px] text-muted-foreground/60">
|
|
99
|
+
{commit.parents.length === 1 ? 'Parent' : 'Parents'}:{' '}
|
|
100
|
+
{commit.parents.map((p) => p.slice(0, hashLength)).join(', ')}
|
|
101
|
+
</div>
|
|
102
|
+
)}
|
|
103
|
+
</div>
|
|
104
|
+
</PopoverContent>
|
|
105
|
+
</Popover>
|
|
106
|
+
);
|
|
107
|
+
}
|