@djangocfg/ui-tools 2.1.418 → 2.1.420
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +51 -224
- 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/README.md +48 -0
- package/src/tools/data/DataGrid/lazy.tsx +1 -1
- package/src/tools/data/DataTable/README.md +42 -0
- 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/README.md +46 -0
- 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/README.md +61 -0
- 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/charts/Gauge/README.md +45 -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,136 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* CronPreview
|
|
5
|
+
*
|
|
6
|
+
* Read-only sibling of `CronScheduler`. Renders a human-readable summary
|
|
7
|
+
* for a cron expression plus an optional list of upcoming run times.
|
|
8
|
+
*
|
|
9
|
+
* Unlike `SchedulePreview` (which lives inside the editor and pulls its
|
|
10
|
+
* state from `CronSchedulerProvider`), `CronPreview` is fully standalone:
|
|
11
|
+
* pass a value, get a preview. Use it in dashboards, list rows, and
|
|
12
|
+
* read-only schedule views where the heavier editor is not needed.
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* <CronPreview value="0 9 * * 1-5" />
|
|
16
|
+
*
|
|
17
|
+
* @example
|
|
18
|
+
* <CronPreview value="*\/15 * * * *" nextRuns={5} />
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
import * as React from 'react';
|
|
22
|
+
import { Calendar } from 'lucide-react';
|
|
23
|
+
import { cn } from '@djangocfg/ui-core/lib';
|
|
24
|
+
import { humanizeCron } from '../utils/cron-humanize';
|
|
25
|
+
import { getNextRuns, formatNextRun } from '../utils/cron-next-runs';
|
|
26
|
+
import { isValidCron } from '../utils/cron-parser';
|
|
27
|
+
|
|
28
|
+
export interface CronPreviewProps extends Omit<React.ComponentPropsWithoutRef<'div'>, 'children'> {
|
|
29
|
+
/** Cron expression (standard 5-field format, e.g. `"0 9 * * 1-5"`). */
|
|
30
|
+
value: string;
|
|
31
|
+
/** Optional heading shown above the summary. */
|
|
32
|
+
title?: string;
|
|
33
|
+
/**
|
|
34
|
+
* Number of upcoming run times to display.
|
|
35
|
+
* `0` hides the list. Defaults to `5`.
|
|
36
|
+
*/
|
|
37
|
+
nextRuns?: number;
|
|
38
|
+
/** Show the raw cron expression alongside the summary. Defaults to `true`. */
|
|
39
|
+
showExpression?: boolean;
|
|
40
|
+
/** Base date for computing next runs. Defaults to "now". */
|
|
41
|
+
referenceDate?: Date;
|
|
42
|
+
/** Additional CSS classes for the outer container. */
|
|
43
|
+
className?: string;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export function CronPreview({
|
|
47
|
+
value,
|
|
48
|
+
title,
|
|
49
|
+
nextRuns = 5,
|
|
50
|
+
showExpression = true,
|
|
51
|
+
referenceDate,
|
|
52
|
+
className,
|
|
53
|
+
...rest
|
|
54
|
+
}: CronPreviewProps) {
|
|
55
|
+
const trimmed = (value ?? '').trim();
|
|
56
|
+
const valid = isValidCron(trimmed);
|
|
57
|
+
|
|
58
|
+
const summary = React.useMemo(() => humanizeCron(trimmed), [trimmed]);
|
|
59
|
+
|
|
60
|
+
const runs = React.useMemo(() => {
|
|
61
|
+
if (!valid || nextRuns <= 0) return [];
|
|
62
|
+
return getNextRuns(trimmed, nextRuns, referenceDate ?? new Date());
|
|
63
|
+
}, [trimmed, valid, nextRuns, referenceDate]);
|
|
64
|
+
|
|
65
|
+
if (!valid) {
|
|
66
|
+
return (
|
|
67
|
+
<div
|
|
68
|
+
data-slot="cron-preview"
|
|
69
|
+
className={cn(
|
|
70
|
+
'rounded-xl border border-destructive/30 bg-destructive/5 px-4 py-3 text-sm text-destructive',
|
|
71
|
+
className
|
|
72
|
+
)}
|
|
73
|
+
{...rest}
|
|
74
|
+
>
|
|
75
|
+
Invalid cron expression: <code className="font-mono">{trimmed || '(empty)'}</code>
|
|
76
|
+
</div>
|
|
77
|
+
);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
return (
|
|
81
|
+
<div
|
|
82
|
+
data-slot="cron-preview"
|
|
83
|
+
role="status"
|
|
84
|
+
aria-live="polite"
|
|
85
|
+
className={cn(
|
|
86
|
+
'overflow-hidden rounded-xl border border-border bg-card text-card-foreground shadow-sm',
|
|
87
|
+
className
|
|
88
|
+
)}
|
|
89
|
+
{...rest}
|
|
90
|
+
>
|
|
91
|
+
{/* Header */}
|
|
92
|
+
<div className="flex items-start justify-between gap-3 border-b border-border/60 px-4 py-3">
|
|
93
|
+
<div className="flex min-w-0 items-start gap-2">
|
|
94
|
+
<Calendar aria-hidden="true" className="mt-0.5 h-4 w-4 shrink-0 text-muted-foreground" />
|
|
95
|
+
<div className="flex min-w-0 flex-col gap-0.5">
|
|
96
|
+
{title && (
|
|
97
|
+
<h3 className="text-sm font-semibold text-foreground">{title}</h3>
|
|
98
|
+
)}
|
|
99
|
+
<p className="text-sm text-muted-foreground">{summary}</p>
|
|
100
|
+
</div>
|
|
101
|
+
</div>
|
|
102
|
+
{showExpression && (
|
|
103
|
+
<code className="shrink-0 rounded-md bg-muted px-2.5 py-1 font-mono text-xs text-foreground">
|
|
104
|
+
{trimmed}
|
|
105
|
+
</code>
|
|
106
|
+
)}
|
|
107
|
+
</div>
|
|
108
|
+
|
|
109
|
+
{/* Next runs */}
|
|
110
|
+
{runs.length > 0 && (
|
|
111
|
+
<div className="px-4 py-3">
|
|
112
|
+
<p className="mb-2 text-[10px] font-medium uppercase tracking-wide text-muted-foreground">
|
|
113
|
+
Next {runs.length === 1 ? 'run' : `${runs.length} runs`}
|
|
114
|
+
</p>
|
|
115
|
+
<ol className="flex flex-col gap-1">
|
|
116
|
+
{runs.map((run, i) => (
|
|
117
|
+
<li
|
|
118
|
+
key={run.toISOString()}
|
|
119
|
+
className="flex items-center gap-2 text-sm"
|
|
120
|
+
>
|
|
121
|
+
<span className="flex size-5 shrink-0 items-center justify-center rounded-full bg-muted text-[10px] font-semibold text-muted-foreground">
|
|
122
|
+
{i + 1}
|
|
123
|
+
</span>
|
|
124
|
+
<span className="font-mono text-xs text-foreground">
|
|
125
|
+
{formatNextRun(run)}
|
|
126
|
+
</span>
|
|
127
|
+
</li>
|
|
128
|
+
))}
|
|
129
|
+
</ol>
|
|
130
|
+
</div>
|
|
131
|
+
)}
|
|
132
|
+
</div>
|
|
133
|
+
);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
export default CronPreview;
|
|
@@ -22,3 +22,6 @@ export type { SchedulePreviewProps } from './SchedulePreview';
|
|
|
22
22
|
|
|
23
23
|
export { CronCheatsheet } from './CronCheatsheet';
|
|
24
24
|
export type { CronCheatsheetProps } from './CronCheatsheet';
|
|
25
|
+
|
|
26
|
+
export { CronPreview } from './CronPreview';
|
|
27
|
+
export type { CronPreviewProps } from './CronPreview';
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
*/
|
|
18
18
|
|
|
19
19
|
import React, { lazy, Suspense } from 'react';
|
|
20
|
-
import { LoadingFallback } from '../../../
|
|
20
|
+
import { LoadingFallback } from '../../../common';
|
|
21
21
|
import type { CronSchedulerProps } from './types';
|
|
22
22
|
|
|
23
23
|
// Lazy load the client component
|
|
@@ -78,6 +78,8 @@ export {
|
|
|
78
78
|
parseCron,
|
|
79
79
|
isValidCron,
|
|
80
80
|
humanizeCron,
|
|
81
|
+
getNextRuns,
|
|
82
|
+
formatNextRun,
|
|
81
83
|
} from './utils';
|
|
82
84
|
|
|
83
85
|
// Re-export components for custom compositions
|
|
@@ -88,6 +90,8 @@ export {
|
|
|
88
90
|
MonthDayGrid,
|
|
89
91
|
CustomInput,
|
|
90
92
|
SchedulePreview,
|
|
93
|
+
CronPreview,
|
|
91
94
|
} from './components';
|
|
95
|
+
export type { CronPreviewProps } from './components';
|
|
92
96
|
|
|
93
97
|
export default CronScheduler;
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
* />
|
|
16
16
|
*/
|
|
17
17
|
|
|
18
|
-
import { createLazyComponent, LoadingFallback } from '../../../
|
|
18
|
+
import { createLazyComponent, LoadingFallback } from '../../../common';
|
|
19
19
|
import type { CronSchedulerProps } from './types';
|
|
20
20
|
|
|
21
21
|
// Re-export types
|
|
@@ -28,6 +28,10 @@ export type {
|
|
|
28
28
|
CronSchedulerState,
|
|
29
29
|
} from './types';
|
|
30
30
|
|
|
31
|
+
// Re-export the read-only preview (small, no lazy needed — pure render).
|
|
32
|
+
export { CronPreview } from './components/CronPreview';
|
|
33
|
+
export type { CronPreviewProps } from './components/CronPreview';
|
|
34
|
+
|
|
31
35
|
/**
|
|
32
36
|
* LazyCronScheduler - Lazy-loaded cron expression builder
|
|
33
37
|
*
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cron Next Runs
|
|
3
|
+
*
|
|
4
|
+
* Computes upcoming run times for a standard 5-field cron expression
|
|
5
|
+
* (minute, hour, day-of-month, month, day-of-week). Supports `*`, lists
|
|
6
|
+
* (`1,2,3`), ranges (`1-5`), and step values (`*\/15`, `1-5/2`).
|
|
7
|
+
*
|
|
8
|
+
* Implementation note: this is a minute-resolution scanner — adequate for
|
|
9
|
+
* preview UIs (showing the next few runs). It does not attempt to support
|
|
10
|
+
* non-standard fields (seconds, year, `L`, `W`, `#`).
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
function parseField(field: string, min: number, max: number): number[] {
|
|
14
|
+
const values = new Set<number>();
|
|
15
|
+
|
|
16
|
+
for (const part of field.split(',')) {
|
|
17
|
+
const stepMatch = part.match(/^(.+)\/(\d+)$/);
|
|
18
|
+
const step = stepMatch ? parseInt(stepMatch[2], 10) : 1;
|
|
19
|
+
const range = stepMatch ? stepMatch[1] : part;
|
|
20
|
+
|
|
21
|
+
if (!Number.isFinite(step) || step < 1) continue;
|
|
22
|
+
|
|
23
|
+
if (range === '*') {
|
|
24
|
+
for (let i = min; i <= max; i += step) values.add(i);
|
|
25
|
+
} else if (range.includes('-')) {
|
|
26
|
+
const [start, end] = range.split('-').map(Number);
|
|
27
|
+
if (Number.isFinite(start) && Number.isFinite(end)) {
|
|
28
|
+
for (let i = start; i <= end; i += step) values.add(i);
|
|
29
|
+
}
|
|
30
|
+
} else {
|
|
31
|
+
const n = parseInt(range, 10);
|
|
32
|
+
if (Number.isFinite(n)) values.add(n);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
return Array.from(values).sort((a, b) => a - b);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Compute the next `count` run times of a cron expression starting after
|
|
41
|
+
* `from` (exclusive). Returns an empty array for invalid expressions.
|
|
42
|
+
*/
|
|
43
|
+
export function getNextRuns(cron: string, count: number, from: Date = new Date()): Date[] {
|
|
44
|
+
if (!cron || typeof cron !== 'string' || count <= 0) return [];
|
|
45
|
+
|
|
46
|
+
const fields = cron.trim().split(/\s+/);
|
|
47
|
+
if (fields.length !== 5) return [];
|
|
48
|
+
|
|
49
|
+
const minuteValues = parseField(fields[0], 0, 59);
|
|
50
|
+
const hourValues = parseField(fields[1], 0, 23);
|
|
51
|
+
const domValues = parseField(fields[2], 1, 31);
|
|
52
|
+
const monthValues = parseField(fields[3], 1, 12);
|
|
53
|
+
const dowValues = parseField(fields[4], 0, 6);
|
|
54
|
+
|
|
55
|
+
if (
|
|
56
|
+
minuteValues.length === 0 ||
|
|
57
|
+
hourValues.length === 0 ||
|
|
58
|
+
(fields[2] !== '*' && domValues.length === 0) ||
|
|
59
|
+
(fields[3] !== '*' && monthValues.length === 0) ||
|
|
60
|
+
(fields[4] !== '*' && dowValues.length === 0)
|
|
61
|
+
) {
|
|
62
|
+
return [];
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const minuteSet = new Set(minuteValues);
|
|
66
|
+
const hourSet = new Set(hourValues);
|
|
67
|
+
const domSet = fields[2] === '*' ? null : new Set(domValues);
|
|
68
|
+
const monthSet = fields[3] === '*' ? null : new Set(monthValues);
|
|
69
|
+
const dowSet = fields[4] === '*' ? null : new Set(dowValues);
|
|
70
|
+
|
|
71
|
+
const runs: Date[] = [];
|
|
72
|
+
const cursor = new Date(from);
|
|
73
|
+
cursor.setSeconds(0, 0);
|
|
74
|
+
cursor.setMinutes(cursor.getMinutes() + 1);
|
|
75
|
+
|
|
76
|
+
// cap at ~1 year of minutes — far enough for monthly schedules,
|
|
77
|
+
// tight enough that broken expressions return quickly.
|
|
78
|
+
const maxIterations = 366 * 24 * 60;
|
|
79
|
+
|
|
80
|
+
for (let i = 0; i < maxIterations && runs.length < count; i++) {
|
|
81
|
+
const m = cursor.getMinutes();
|
|
82
|
+
const h = cursor.getHours();
|
|
83
|
+
const d = cursor.getDate();
|
|
84
|
+
const mo = cursor.getMonth() + 1;
|
|
85
|
+
const w = cursor.getDay();
|
|
86
|
+
|
|
87
|
+
if (
|
|
88
|
+
minuteSet.has(m) &&
|
|
89
|
+
hourSet.has(h) &&
|
|
90
|
+
(domSet === null || domSet.has(d)) &&
|
|
91
|
+
(monthSet === null || monthSet.has(mo)) &&
|
|
92
|
+
(dowSet === null || dowSet.has(w))
|
|
93
|
+
) {
|
|
94
|
+
runs.push(new Date(cursor));
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
cursor.setMinutes(cursor.getMinutes() + 1);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
return runs;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Default human-friendly formatter for a next-run Date.
|
|
105
|
+
* Example: "Mon, Jan 5 at 9:00 AM"
|
|
106
|
+
*/
|
|
107
|
+
export function formatNextRun(date: Date): string {
|
|
108
|
+
const days = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'];
|
|
109
|
+
const months = [
|
|
110
|
+
'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
|
|
111
|
+
'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec',
|
|
112
|
+
];
|
|
113
|
+
const day = days[date.getDay()];
|
|
114
|
+
const month = months[date.getMonth()];
|
|
115
|
+
const d = date.getDate();
|
|
116
|
+
const h = date.getHours();
|
|
117
|
+
const m = date.getMinutes().toString().padStart(2, '0');
|
|
118
|
+
const period = h >= 12 ? 'PM' : 'AM';
|
|
119
|
+
const hour = h === 0 ? 12 : h > 12 ? h - 12 : h;
|
|
120
|
+
|
|
121
|
+
return `${day}, ${month} ${d} at ${hour}:${m} ${period}`;
|
|
122
|
+
}
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* Lazy-loaded Scroller Component
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
import { createLazyComponent, LoadingFallback } from '../../../
|
|
7
|
+
import { createLazyComponent, LoadingFallback } from '../../../common/lazy-wrapper';
|
|
8
8
|
import type {
|
|
9
9
|
ScrollerProps,
|
|
10
10
|
ScrollerItemProps,
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* Lazy-loaded Sortable Component
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
import { createLazyComponent, LoadingFallback } from '../../../
|
|
7
|
+
import { createLazyComponent, LoadingFallback } from '../../../common/lazy-wrapper';
|
|
8
8
|
import type {
|
|
9
9
|
SortableProps,
|
|
10
10
|
SortableItemProps,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
|
-
import { createLazyComponent } from '../../../
|
|
3
|
+
import { createLazyComponent } from '../../../common';
|
|
4
4
|
import type { DictationFieldProps } from './widgets/DictationField';
|
|
5
5
|
|
|
6
6
|
export const LazyDictationField = createLazyComponent<DictationFieldProps>(
|
|
@@ -6,6 +6,7 @@ import { AlertCircle, Loader2, Mic } from 'lucide-react';
|
|
|
6
6
|
|
|
7
7
|
import { useCountdownFromSeconds, useNotificationSounds } from '@djangocfg/ui-core/hooks';
|
|
8
8
|
import { cn } from '@djangocfg/ui-core/lib';
|
|
9
|
+
import { Tooltip, TooltipContent, TooltipTrigger } from '@djangocfg/ui-core/components';
|
|
9
10
|
import { useActiveComposer } from '@djangocfg/ui-tools/composer-registry';
|
|
10
11
|
import { useSpeechRecognition } from '../hooks/useSpeechRecognition';
|
|
11
12
|
import { useVoiceSupport } from '../hooks/useVoiceSupport';
|
|
@@ -364,44 +365,48 @@ export function VoiceComposerSlot({
|
|
|
364
365
|
Failed
|
|
365
366
|
</span>
|
|
366
367
|
) : null}
|
|
367
|
-
<
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
'relative inline-flex items-center justify-center rounded-full transition-all duration-200',
|
|
376
|
-
'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2',
|
|
377
|
-
SIZE_CLS[size],
|
|
378
|
-
slotState === 'listening' &&
|
|
379
|
-
'bg-destructive text-destructive-foreground hover:bg-destructive/90',
|
|
380
|
-
slotState === 'processing' &&
|
|
381
|
-
'bg-primary/10 text-primary hover:bg-primary/15',
|
|
382
|
-
slotState === 'error' &&
|
|
383
|
-
'bg-destructive/10 text-destructive hover:bg-destructive/15',
|
|
384
|
-
slotState === 'idle' &&
|
|
385
|
-
'text-muted-foreground hover:bg-muted hover:text-foreground',
|
|
386
|
-
className,
|
|
387
|
-
)}
|
|
388
|
-
>
|
|
389
|
-
{/* Recording feedback — pulsing circle overlay driven by the
|
|
390
|
-
live mic level. Hidden in every non-listening state. */}
|
|
391
|
-
<RecordingPulse active={slotState === 'listening'} level={rec.level} />
|
|
392
|
-
{slotState === 'processing' ? (
|
|
393
|
-
<Loader2 className="animate-spin" />
|
|
394
|
-
) : slotState === 'error' ? (
|
|
395
|
-
<AlertCircle />
|
|
396
|
-
) : (
|
|
397
|
-
<Mic
|
|
368
|
+
<Tooltip>
|
|
369
|
+
<TooltipTrigger asChild>
|
|
370
|
+
<button
|
|
371
|
+
type="button"
|
|
372
|
+
onClick={toggle}
|
|
373
|
+
aria-pressed={slotState === 'listening'}
|
|
374
|
+
aria-label={ariaLabel}
|
|
375
|
+
data-state={slotState}
|
|
398
376
|
className={cn(
|
|
399
|
-
'transition-
|
|
400
|
-
|
|
377
|
+
'relative inline-flex items-center justify-center rounded-full transition-all duration-200',
|
|
378
|
+
'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2',
|
|
379
|
+
SIZE_CLS[size],
|
|
380
|
+
slotState === 'listening' &&
|
|
381
|
+
'bg-destructive text-destructive-foreground hover:bg-destructive/90',
|
|
382
|
+
slotState === 'processing' &&
|
|
383
|
+
'bg-primary/10 text-primary hover:bg-primary/15',
|
|
384
|
+
slotState === 'error' &&
|
|
385
|
+
'bg-destructive/10 text-destructive hover:bg-destructive/15',
|
|
386
|
+
slotState === 'idle' &&
|
|
387
|
+
'text-muted-foreground hover:bg-muted hover:text-foreground',
|
|
388
|
+
className,
|
|
401
389
|
)}
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
390
|
+
>
|
|
391
|
+
{/* Recording feedback — pulsing circle overlay driven by the
|
|
392
|
+
live mic level. Hidden in every non-listening state. */}
|
|
393
|
+
<RecordingPulse active={slotState === 'listening'} level={rec.level} />
|
|
394
|
+
{slotState === 'processing' ? (
|
|
395
|
+
<Loader2 className="animate-spin" />
|
|
396
|
+
) : slotState === 'error' ? (
|
|
397
|
+
<AlertCircle />
|
|
398
|
+
) : (
|
|
399
|
+
<Mic
|
|
400
|
+
className={cn(
|
|
401
|
+
'transition-transform duration-200',
|
|
402
|
+
slotState === 'listening' && 'scale-110',
|
|
403
|
+
)}
|
|
404
|
+
/>
|
|
405
|
+
)}
|
|
406
|
+
</button>
|
|
407
|
+
</TooltipTrigger>
|
|
408
|
+
<TooltipContent side="top">{tooltip}</TooltipContent>
|
|
409
|
+
</Tooltip>
|
|
405
410
|
</span>
|
|
406
411
|
);
|
|
407
412
|
}
|
|
@@ -4,10 +4,18 @@
|
|
|
4
4
|
* ImageToolbar - Floating toolbar for image controls
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
import { useMemo } from 'react';
|
|
7
|
+
import { useMemo, type ReactNode } from 'react';
|
|
8
8
|
import { ZoomIn, ZoomOut, RotateCw, FlipHorizontal, FlipVertical, Maximize2, Expand } from 'lucide-react';
|
|
9
9
|
import { useControls } from 'react-zoom-pan-pinch';
|
|
10
|
-
import {
|
|
10
|
+
import {
|
|
11
|
+
DropdownMenu,
|
|
12
|
+
DropdownMenuContent,
|
|
13
|
+
DropdownMenuItem,
|
|
14
|
+
DropdownMenuTrigger,
|
|
15
|
+
Tooltip,
|
|
16
|
+
TooltipContent,
|
|
17
|
+
TooltipTrigger,
|
|
18
|
+
} from '@djangocfg/ui-core/components';
|
|
11
19
|
import { useAppT } from '@djangocfg/i18n';
|
|
12
20
|
import { cn } from '@djangocfg/ui-core/lib';
|
|
13
21
|
import { ZOOM_PRESETS } from '../utils';
|
|
@@ -29,6 +37,36 @@ const TB_BUTTON =
|
|
|
29
37
|
'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-white/70 ' +
|
|
30
38
|
'disabled:opacity-40 disabled:pointer-events-none';
|
|
31
39
|
|
|
40
|
+
// Small helper so each icon button shares the same Tooltip wrapper +
|
|
41
|
+
// accessibility shape (visible tooltip on hover/focus, aria-label for AT).
|
|
42
|
+
function TbIconButton({
|
|
43
|
+
label,
|
|
44
|
+
onClick,
|
|
45
|
+
className,
|
|
46
|
+
children,
|
|
47
|
+
}: {
|
|
48
|
+
label: string;
|
|
49
|
+
onClick: () => void;
|
|
50
|
+
className?: string;
|
|
51
|
+
children: ReactNode;
|
|
52
|
+
}) {
|
|
53
|
+
return (
|
|
54
|
+
<Tooltip>
|
|
55
|
+
<TooltipTrigger asChild>
|
|
56
|
+
<button
|
|
57
|
+
type="button"
|
|
58
|
+
aria-label={label}
|
|
59
|
+
onClick={onClick}
|
|
60
|
+
className={cn(TB_BUTTON, 'h-7 w-7', className)}
|
|
61
|
+
>
|
|
62
|
+
{children}
|
|
63
|
+
</button>
|
|
64
|
+
</TooltipTrigger>
|
|
65
|
+
<TooltipContent side="top">{label}</TooltipContent>
|
|
66
|
+
</Tooltip>
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
|
|
32
70
|
export function ImageToolbar({
|
|
33
71
|
scale,
|
|
34
72
|
transform,
|
|
@@ -59,14 +97,9 @@ export function ImageToolbar({
|
|
|
59
97
|
return (
|
|
60
98
|
<div className="absolute bottom-4 left-1/2 -translate-x-1/2 z-10 flex items-center gap-0.5 bg-black/60 backdrop-blur-sm border border-white/10 rounded-lg p-1 shadow-lg">
|
|
61
99
|
{/* Zoom controls */}
|
|
62
|
-
<
|
|
63
|
-
type="button"
|
|
64
|
-
className={cn(TB_BUTTON, 'h-7 w-7')}
|
|
65
|
-
onClick={() => zoomOut()}
|
|
66
|
-
title={labels.zoomOut}
|
|
67
|
-
>
|
|
100
|
+
<TbIconButton label={labels.zoomOut} onClick={() => zoomOut()}>
|
|
68
101
|
<ZoomOut className="h-3.5 w-3.5" />
|
|
69
|
-
</
|
|
102
|
+
</TbIconButton>
|
|
70
103
|
|
|
71
104
|
<DropdownMenu>
|
|
72
105
|
<DropdownMenuTrigger asChild>
|
|
@@ -90,56 +123,39 @@ export function ImageToolbar({
|
|
|
90
123
|
</DropdownMenuContent>
|
|
91
124
|
</DropdownMenu>
|
|
92
125
|
|
|
93
|
-
<
|
|
94
|
-
type="button"
|
|
95
|
-
className={cn(TB_BUTTON, 'h-7 w-7')}
|
|
96
|
-
onClick={() => zoomIn()}
|
|
97
|
-
title={labels.zoomIn}
|
|
98
|
-
>
|
|
126
|
+
<TbIconButton label={labels.zoomIn} onClick={() => zoomIn()}>
|
|
99
127
|
<ZoomIn className="h-3.5 w-3.5" />
|
|
100
|
-
</
|
|
128
|
+
</TbIconButton>
|
|
101
129
|
|
|
102
130
|
<div className="w-px h-4 bg-white/20 mx-1" />
|
|
103
131
|
|
|
104
132
|
{/* Fit to view */}
|
|
105
|
-
<
|
|
106
|
-
type="button"
|
|
107
|
-
className={cn(TB_BUTTON, 'h-7 w-7')}
|
|
108
|
-
onClick={() => resetTransform()}
|
|
109
|
-
title={labels.fitToView}
|
|
110
|
-
>
|
|
133
|
+
<TbIconButton label={labels.fitToView} onClick={() => resetTransform()}>
|
|
111
134
|
<Maximize2 className="h-3.5 w-3.5" />
|
|
112
|
-
</
|
|
135
|
+
</TbIconButton>
|
|
113
136
|
|
|
114
137
|
<div className="w-px h-4 bg-white/20 mx-1" />
|
|
115
138
|
|
|
116
139
|
{/* Transform controls */}
|
|
117
|
-
<
|
|
118
|
-
|
|
119
|
-
className={cn(TB_BUTTON, 'h-7 w-7', transform.flipH && 'bg-white/20 text-white')}
|
|
140
|
+
<TbIconButton
|
|
141
|
+
label={labels.flipHorizontal}
|
|
120
142
|
onClick={onFlipH}
|
|
121
|
-
|
|
143
|
+
className={transform.flipH ? 'bg-white/20 text-white' : undefined}
|
|
122
144
|
>
|
|
123
145
|
<FlipHorizontal className="h-3.5 w-3.5" />
|
|
124
|
-
</
|
|
146
|
+
</TbIconButton>
|
|
125
147
|
|
|
126
|
-
<
|
|
127
|
-
|
|
128
|
-
className={cn(TB_BUTTON, 'h-7 w-7', transform.flipV && 'bg-white/20 text-white')}
|
|
148
|
+
<TbIconButton
|
|
149
|
+
label={labels.flipVertical}
|
|
129
150
|
onClick={onFlipV}
|
|
130
|
-
|
|
151
|
+
className={transform.flipV ? 'bg-white/20 text-white' : undefined}
|
|
131
152
|
>
|
|
132
153
|
<FlipVertical className="h-3.5 w-3.5" />
|
|
133
|
-
</
|
|
154
|
+
</TbIconButton>
|
|
134
155
|
|
|
135
|
-
<
|
|
136
|
-
type="button"
|
|
137
|
-
className={cn(TB_BUTTON, 'h-7 w-7')}
|
|
138
|
-
onClick={onRotate}
|
|
139
|
-
title={labels.rotate}
|
|
140
|
-
>
|
|
156
|
+
<TbIconButton label={labels.rotate} onClick={onRotate}>
|
|
141
157
|
<RotateCw className="h-3.5 w-3.5" />
|
|
142
|
-
</
|
|
158
|
+
</TbIconButton>
|
|
143
159
|
|
|
144
160
|
{transform.rotation !== 0 && (
|
|
145
161
|
<span className="text-[10px] text-white/60 font-mono pl-1">
|
|
@@ -150,14 +166,9 @@ export function ImageToolbar({
|
|
|
150
166
|
{onExpand && (
|
|
151
167
|
<>
|
|
152
168
|
<div className="w-px h-4 bg-white/20 mx-1" />
|
|
153
|
-
<
|
|
154
|
-
type="button"
|
|
155
|
-
className={cn(TB_BUTTON, 'h-7 w-7')}
|
|
156
|
-
onClick={onExpand}
|
|
157
|
-
title={labels.fullscreen}
|
|
158
|
-
>
|
|
169
|
+
<TbIconButton label={labels.fullscreen} onClick={onExpand}>
|
|
159
170
|
<Expand className="h-3.5 w-3.5" />
|
|
160
|
-
</
|
|
171
|
+
</TbIconButton>
|
|
161
172
|
</>
|
|
162
173
|
)}
|
|
163
174
|
</div>
|
|
@@ -20,7 +20,7 @@ import {
|
|
|
20
20
|
TransformComponent,
|
|
21
21
|
type ReactZoomPanPinchRef,
|
|
22
22
|
} from 'react-zoom-pan-pinch';
|
|
23
|
-
import { cn, Dialog, DialogContent, DialogTitle, Alert, AlertDescription } from '@djangocfg/ui-core';
|
|
23
|
+
import { cn, Dialog, DialogContent, DialogTitle, Alert, AlertDescription, Tooltip, TooltipContent, TooltipTrigger } from '@djangocfg/ui-core';
|
|
24
24
|
import { useAppT } from '@djangocfg/i18n';
|
|
25
25
|
import { useHotkey } from '@djangocfg/ui-core/hooks';
|
|
26
26
|
|
|
@@ -313,24 +313,32 @@ export function ImageViewer({
|
|
|
313
313
|
{/* Gallery navigation */}
|
|
314
314
|
{hasMultiple && (
|
|
315
315
|
<>
|
|
316
|
-
<
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
316
|
+
<Tooltip>
|
|
317
|
+
<TooltipTrigger asChild>
|
|
318
|
+
<button
|
|
319
|
+
type="button"
|
|
320
|
+
onClick={prev}
|
|
321
|
+
aria-label={labels.prev}
|
|
322
|
+
className="absolute left-2 top-1/2 -translate-y-1/2 z-10 bg-black/50 hover:bg-black/70 text-white rounded-full p-1.5 transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-white"
|
|
323
|
+
>
|
|
324
|
+
<ChevronLeft className="h-5 w-5" />
|
|
325
|
+
</button>
|
|
326
|
+
</TooltipTrigger>
|
|
327
|
+
<TooltipContent side="right">{labels.prev}</TooltipContent>
|
|
328
|
+
</Tooltip>
|
|
329
|
+
<Tooltip>
|
|
330
|
+
<TooltipTrigger asChild>
|
|
331
|
+
<button
|
|
332
|
+
type="button"
|
|
333
|
+
onClick={next}
|
|
334
|
+
aria-label={labels.next}
|
|
335
|
+
className="absolute right-2 top-1/2 -translate-y-1/2 z-10 bg-black/50 hover:bg-black/70 text-white rounded-full p-1.5 transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-white"
|
|
336
|
+
>
|
|
337
|
+
<ChevronRight className="h-5 w-5" />
|
|
338
|
+
</button>
|
|
339
|
+
</TooltipTrigger>
|
|
340
|
+
<TooltipContent side="left">{labels.next}</TooltipContent>
|
|
341
|
+
</Tooltip>
|
|
334
342
|
<div className="absolute top-3 left-1/2 -translate-x-1/2 z-10 bg-black/60 backdrop-blur-sm border border-white/10 text-white/80 text-xs font-mono px-2 py-0.5 rounded-full pointer-events-none">
|
|
335
343
|
{currentIndex + 1} / {images.length}
|
|
336
344
|
</div>
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
* import { ImageViewer } from '@djangocfg/ui-tools/image-viewer'
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
|
-
import { createLazyComponent, LoadingFallback } from '../../../
|
|
13
|
+
import { createLazyComponent, LoadingFallback } from '../../../common';
|
|
14
14
|
import type { ImageViewerProps } from './types';
|
|
15
15
|
|
|
16
16
|
// ============================================================================
|