@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
|
@@ -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
|
// ============================================================================
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
* import { LottiePlayer } from '@djangocfg/ui-tools/lottie'
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
|
-
import { createLazyComponent } from '../../../
|
|
13
|
+
import { createLazyComponent } from '../../../common';
|
|
14
14
|
import type { LottiePlayerProps } from './types';
|
|
15
15
|
|
|
16
16
|
// ============================================================================
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
* iframe sources where the embed renders its own UI).
|
|
13
13
|
*/
|
|
14
14
|
|
|
15
|
-
import { useEffect, useMemo, useRef, type CSSProperties } from 'react';
|
|
15
|
+
import { useCallback, useEffect, useMemo, useRef, type CSSProperties } from 'react';
|
|
16
16
|
import { MediaController } from 'media-chrome/react';
|
|
17
17
|
import { cn } from '@djangocfg/ui-core/lib';
|
|
18
18
|
import './styles/video-player.css';
|
|
@@ -51,6 +51,7 @@ export function VideoPlayer({
|
|
|
51
51
|
);
|
|
52
52
|
|
|
53
53
|
const isIframe = normalized.type === 'iframe';
|
|
54
|
+
const isYouTube = normalized.type === 'youtube';
|
|
54
55
|
// For iframe embeds media-chrome cannot drive the inner player — hide the
|
|
55
56
|
// control bar to avoid a non-functional UI.
|
|
56
57
|
const showControls = controls && !isIframe;
|
|
@@ -61,6 +62,20 @@ export function VideoPlayer({
|
|
|
61
62
|
// all we use) — media-chrome's full MediaController type pulls private
|
|
62
63
|
// fields we don't need to see.
|
|
63
64
|
const controllerRef = useRef<HTMLElement | null>(null);
|
|
65
|
+
|
|
66
|
+
// Click-to-play overlay for YouTube — the underlying YT iframe is made
|
|
67
|
+
// `pointer-events: none` via CSS to suppress YouTube's own hover-state /
|
|
68
|
+
// UI (title bar, pause-screen, branding tray; ToS forbids hiding them,
|
|
69
|
+
// but we can make them inert). This overlay restores click-to-toggle
|
|
70
|
+
// by dispatching media-chrome request events the controller listens for.
|
|
71
|
+
const onYouTubeShieldClick = useCallback(() => {
|
|
72
|
+
const el = controllerRef.current;
|
|
73
|
+
if (!el) return;
|
|
74
|
+
const paused = el.hasAttribute('mediapaused');
|
|
75
|
+
const eventName = paused ? 'mediaplayrequest' : 'mediapauserequest';
|
|
76
|
+
el.dispatchEvent(new CustomEvent(eventName, { bubbles: true, composed: true }));
|
|
77
|
+
}, []);
|
|
78
|
+
|
|
64
79
|
useEffect(() => {
|
|
65
80
|
if (!autoFocus) return;
|
|
66
81
|
const el = controllerRef.current;
|
|
@@ -85,6 +100,18 @@ export function VideoPlayer({
|
|
|
85
100
|
style={aspectRatioStyle(aspectRatio)}
|
|
86
101
|
>
|
|
87
102
|
<CanvasDispatcher source={normalized} {...settings} />
|
|
103
|
+
{isYouTube && (
|
|
104
|
+
// Transparent click-shield over the YT iframe. The iframe itself
|
|
105
|
+
// is pointer-events:none (see video-player.css); this div absorbs
|
|
106
|
+
// pointer events and forwards a play/pause request to media-chrome,
|
|
107
|
+
// so users still get click-to-toggle without ever interacting with
|
|
108
|
+
// YouTube's own UI.
|
|
109
|
+
<div
|
|
110
|
+
className="vp-yt-click-shield"
|
|
111
|
+
onClick={onYouTubeShieldClick}
|
|
112
|
+
aria-hidden="true"
|
|
113
|
+
/>
|
|
114
|
+
)}
|
|
88
115
|
{children ??
|
|
89
116
|
(showControls && (
|
|
90
117
|
<ControlsBar>
|
|
@@ -2,12 +2,20 @@
|
|
|
2
2
|
|
|
3
3
|
import { MediaFullscreenButton } from 'media-chrome/react';
|
|
4
4
|
import { cn } from '@djangocfg/ui-core/lib';
|
|
5
|
-
import
|
|
5
|
+
import {
|
|
6
|
+
Tooltip,
|
|
7
|
+
TooltipContent,
|
|
8
|
+
TooltipTrigger,
|
|
9
|
+
} from '@djangocfg/ui-core/components';
|
|
10
|
+
import type { ComponentProps, ReactNode } from 'react';
|
|
6
11
|
|
|
7
|
-
export type FullscreenProps = ComponentProps<typeof MediaFullscreenButton
|
|
12
|
+
export type FullscreenProps = ComponentProps<typeof MediaFullscreenButton> & {
|
|
13
|
+
/** Tooltip copy. Defaults to `"Fullscreen"`. Pass `false` to opt out. */
|
|
14
|
+
readonly label?: ReactNode | false;
|
|
15
|
+
};
|
|
8
16
|
|
|
9
|
-
export function Fullscreen({ className, ...props }: FullscreenProps) {
|
|
10
|
-
|
|
17
|
+
export function Fullscreen({ className, label, ...props }: FullscreenProps) {
|
|
18
|
+
const button = (
|
|
11
19
|
<MediaFullscreenButton
|
|
12
20
|
{...props}
|
|
13
21
|
className={cn(
|
|
@@ -16,4 +24,13 @@ export function Fullscreen({ className, ...props }: FullscreenProps) {
|
|
|
16
24
|
)}
|
|
17
25
|
/>
|
|
18
26
|
);
|
|
27
|
+
|
|
28
|
+
if (label === false) return button;
|
|
29
|
+
|
|
30
|
+
return (
|
|
31
|
+
<Tooltip>
|
|
32
|
+
<TooltipTrigger asChild>{button}</TooltipTrigger>
|
|
33
|
+
<TooltipContent side="top">{label ?? 'Fullscreen'}</TooltipContent>
|
|
34
|
+
</Tooltip>
|
|
35
|
+
);
|
|
19
36
|
}
|