@djangocfg/ui-tools 2.1.417 → 2.1.419
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/audio-player/index.cjs +1 -2
- package/dist/audio-player/index.cjs.map +1 -1
- package/dist/audio-player/index.d.cts +3 -11
- package/dist/audio-player/index.d.ts +3 -11
- package/dist/audio-player/index.mjs +1 -2
- package/dist/audio-player/index.mjs.map +1 -1
- package/dist/file-icon/index.cjs +3 -3
- package/dist/file-icon/index.cjs.map +1 -1
- package/dist/file-icon/index.mjs +3 -3
- package/dist/file-icon/index.mjs.map +1 -1
- package/dist/tree/index.cjs +0 -3
- package/dist/tree/index.cjs.map +1 -1
- package/dist/tree/index.mjs +0 -3
- package/dist/tree/index.mjs.map +1 -1
- package/package.json +117 -36
- package/src/common/FloatingToolbar/actions/CopyAction.tsx +31 -0
- package/src/{components → common}/FloatingToolbar/actions/DownloadAction.tsx +15 -10
- package/src/common/FloatingToolbar/actions/ExpandAction.tsx +33 -0
- package/src/common/FloatingToolbar/actions/FullscreenAction.tsx +38 -0
- package/src/{components → common}/FloatingToolbar/index.tsx +39 -0
- package/src/lib/http.ts +64 -0
- package/src/tools/chat/index.ts +1 -1
- package/src/tools/chat/launcher/ChatFAB.tsx +66 -74
- package/src/tools/chat/launcher/header/ChatHeaderActionButton.tsx +2 -3
- package/src/tools/chat/lazy.tsx +1 -1
- package/src/tools/chat/messages/MessageBubble.tsx +1 -1
- package/src/tools/chat/messages/blocks/builtin.tsx +1 -1
- package/src/tools/chat/messages/blocks/renderers/CodeBlock.tsx +2 -2
- package/src/tools/chat/messages/blocks/renderers/JsonBlock.tsx +12 -1
- package/src/tools/data/DataGrid/lazy.tsx +1 -1
- package/src/tools/data/DataTable/lazy.tsx +1 -1
- package/src/tools/data/JsonTree/JsonViewer.tsx +720 -0
- package/src/tools/data/JsonTree/README.md +126 -73
- package/src/tools/data/JsonTree/index.tsx +3 -95
- package/src/tools/data/JsonTree/lazy.tsx +10 -50
- package/src/tools/data/JsonTree/types.ts +82 -63
- package/src/tools/data/Kanban/lazy.tsx +1 -1
- package/src/tools/data/Listbox/lazy.tsx +1 -1
- package/src/tools/data/Masonry/lazy.tsx +1 -1
- package/src/tools/data/Timeline/lazy.tsx +1 -1
- package/src/tools/data/Tree/components/TreeRow.tsx +0 -11
- package/src/tools/data/Tree/lazy.tsx +1 -1
- package/src/tools/dev/Map/lazy.tsx +1 -1
- package/src/tools/dev/Mermaid/Mermaid.client.tsx +2 -2
- package/src/tools/dev/Mermaid/lazy.tsx +1 -1
- package/src/tools/dev/api/ApiRefTable/ApiRefTable.tsx +65 -0
- package/src/tools/dev/api/ApiRefTable/README.md +31 -0
- package/src/tools/dev/api/ApiRefTable/components/Row.tsx +96 -0
- package/src/tools/dev/api/ApiRefTable/components/TypeDisplay.tsx +44 -0
- package/src/tools/dev/api/ApiRefTable/index.ts +6 -0
- package/src/tools/dev/api/ApiRefTable/lazy.tsx +21 -0
- package/src/tools/dev/api/ApiRefTable/types.ts +82 -0
- package/src/tools/dev/api/ApiRefTable/utils.ts +42 -0
- package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/ApiIntroSection.tsx +1 -1
- package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/EndpointDoc/CodeSamples/index.tsx +1 -1
- package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/EndpointDoc/Header/index.tsx +1 -1
- package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/EndpointDoc/Responses/ResponseBody.tsx +7 -21
- package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/shared/RequestPanel.tsx +1 -1
- package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/shared/ResponsePanel/PrettyView.tsx +13 -19
- package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/shared/ResponsePanel/types.ts +1 -1
- package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/lazy.tsx +1 -1
- package/src/tools/dev/api/RequestViewer/README.md +33 -0
- package/src/tools/dev/api/RequestViewer/RequestViewer.tsx +121 -0
- package/src/tools/dev/api/RequestViewer/components/BodyTab.tsx +44 -0
- package/src/tools/dev/api/RequestViewer/components/EmptyState.tsx +13 -0
- package/src/tools/dev/api/RequestViewer/components/HeadersTab.tsx +78 -0
- package/src/tools/dev/api/RequestViewer/components/TimingTab.tsx +113 -0
- package/src/tools/dev/api/RequestViewer/components/utils.ts +31 -0
- package/src/tools/dev/api/RequestViewer/index.ts +16 -0
- package/src/tools/dev/api/RequestViewer/lazy.tsx +30 -0
- package/src/tools/dev/api/RequestViewer/types.ts +81 -0
- package/src/tools/dev/code/DiffViewer/DiffViewer.tsx +144 -0
- package/src/tools/dev/code/DiffViewer/README.md +33 -0
- package/src/tools/dev/code/DiffViewer/components/CopyButton.tsx +49 -0
- package/src/tools/dev/code/DiffViewer/components/DiffLineContent.tsx +48 -0
- package/src/tools/dev/code/DiffViewer/components/SplitView.tsx +220 -0
- package/src/tools/dev/code/DiffViewer/components/UnifiedView.tsx +154 -0
- package/src/tools/dev/code/DiffViewer/hooks/useDiff.ts +47 -0
- package/src/tools/dev/code/DiffViewer/hooks/useHighlighter.ts +54 -0
- package/src/tools/dev/code/DiffViewer/index.ts +22 -0
- package/src/tools/dev/code/DiffViewer/lazy.tsx +22 -0
- package/src/tools/dev/code/DiffViewer/types.ts +109 -0
- package/src/tools/dev/code/DiffViewer/utils/computeDiff.ts +159 -0
- package/src/tools/dev/{MarkdownMessage → code/MarkdownMessage}/CollapseToggle.tsx +1 -1
- package/src/tools/dev/{MarkdownMessage → code/MarkdownMessage}/MarkdownMessage.tsx +1 -1
- package/src/tools/dev/{MarkdownMessage → code/MarkdownMessage}/components.tsx +2 -2
- package/src/tools/dev/{PrettyCode → code/PrettyCode}/PrettyCode.client.tsx +2 -2
- package/src/tools/dev/{PrettyCode → code/PrettyCode}/lazy.tsx +1 -1
- package/src/tools/dev/ops/EnvTable/EnvTable.tsx +228 -0
- package/src/tools/dev/ops/EnvTable/README.md +29 -0
- package/src/tools/dev/ops/EnvTable/hooks/useEnvMask.ts +121 -0
- package/src/tools/dev/ops/EnvTable/index.ts +12 -0
- package/src/tools/dev/ops/EnvTable/lazy.tsx +21 -0
- package/src/tools/dev/ops/EnvTable/types.ts +76 -0
- package/src/tools/dev/ops/LogViewer/LogViewer.tsx +194 -0
- package/src/tools/dev/ops/LogViewer/README.md +30 -0
- package/src/tools/dev/ops/LogViewer/components/LogRow.tsx +151 -0
- package/src/tools/dev/ops/LogViewer/components/Toolbar.tsx +199 -0
- package/src/tools/dev/ops/LogViewer/hooks/useAutoScroll.ts +68 -0
- package/src/tools/dev/ops/LogViewer/hooks/useLogFilter.ts +58 -0
- package/src/tools/dev/ops/LogViewer/index.ts +18 -0
- package/src/tools/dev/ops/LogViewer/lazy.tsx +25 -0
- package/src/tools/dev/ops/LogViewer/types.ts +142 -0
- package/src/tools/dev/ops/LogViewer/utils/ansi.ts +231 -0
- package/src/tools/forms/CodeEditor/components/Editor.tsx +19 -0
- package/src/tools/forms/CodeEditor/hooks/useEditorTheme.ts +13 -73
- package/src/tools/forms/CodeEditor/lazy.tsx +1 -1
- package/src/tools/forms/CodeEditor/types/index.ts +7 -0
- package/src/tools/forms/FileUpload/lazy.tsx +1 -1
- package/src/tools/forms/JsonEditor/JsonEditor.tsx +115 -0
- package/src/tools/forms/JsonEditor/index.ts +1 -0
- package/src/tools/forms/JsonEditor/lazy.tsx +24 -0
- package/src/tools/forms/JsonForm/index.ts +1 -1
- package/src/tools/forms/JsonForm/lazy.tsx +1 -1
- package/src/tools/forms/MarkdownEditor/MarkdownEditor.tsx +40 -0
- package/src/tools/forms/MarkdownEditor/lazy.tsx +1 -1
- package/src/tools/forms/MarkdownEditor/styles.css +174 -21
- package/src/tools/forms/NotionEditor/CustomKeymap.ts +48 -0
- package/src/tools/forms/NotionEditor/LinkDialog.tsx +133 -0
- package/src/tools/forms/NotionEditor/NotionEditor.tsx +304 -0
- package/src/tools/forms/NotionEditor/README.md +237 -0
- package/src/tools/forms/NotionEditor/SlashExtension.ts +32 -0
- package/src/tools/forms/NotionEditor/SlashList.tsx +136 -0
- package/src/tools/forms/NotionEditor/TaskItemView.tsx +41 -0
- package/src/tools/forms/NotionEditor/createSlashSuggestion.ts +121 -0
- package/src/tools/forms/NotionEditor/extensions.ts +105 -0
- package/src/tools/forms/NotionEditor/index.ts +1 -0
- package/src/tools/forms/NotionEditor/lazy.tsx +44 -0
- package/src/tools/forms/NotionEditor/slashItems.ts +159 -0
- package/src/tools/forms/NotionEditor/styles.css +478 -0
- package/src/tools/forms/NotionEditor/types.ts +28 -0
- package/src/tools/index.ts +153 -13
- package/src/tools/input/Combobox/lazy.tsx +1 -1
- package/src/tools/input/CronScheduler/components/CronPreview.README.md +28 -0
- package/src/tools/input/CronScheduler/components/CronPreview.tsx +136 -0
- package/src/tools/input/CronScheduler/components/index.ts +3 -0
- package/src/tools/input/CronScheduler/index.tsx +5 -1
- package/src/tools/input/CronScheduler/lazy.tsx +5 -1
- package/src/tools/input/CronScheduler/utils/cron-next-runs.ts +122 -0
- package/src/tools/input/CronScheduler/utils/index.ts +1 -0
- package/src/tools/input/Scroller/lazy.tsx +1 -1
- package/src/tools/input/Sortable/lazy.tsx +1 -1
- package/src/tools/input/SpeechRecognition/lazy.tsx +1 -1
- package/src/tools/input/SpeechRecognition/widgets/VoiceComposerSlot.tsx +41 -36
- package/src/tools/media/AudioPlayer/PlayerShell.tsx +3 -11
- package/src/tools/media/AudioPlayer/types.ts +4 -11
- package/src/tools/media/ImageViewer/components/ImageToolbar.tsx +58 -47
- package/src/tools/media/ImageViewer/components/ImageViewer.tsx +35 -19
- package/src/tools/media/ImageViewer/lazy.tsx +1 -1
- package/src/tools/media/ImageViewer/types.ts +4 -0
- package/src/tools/media/LottiePlayer/lazy.tsx +1 -1
- package/src/tools/media/VideoPlayer/VideoPlayer.tsx +47 -1
- package/src/tools/media/VideoPlayer/parts/fullscreen.tsx +21 -4
- package/src/tools/media/VideoPlayer/parts/pip.tsx +21 -4
- package/src/tools/media/VideoPlayer/parts/play-button.tsx +21 -4
- package/src/tools/media/VideoPlayer/parts/playback-rate.tsx +19 -3
- package/src/tools/media/VideoPlayer/parts/volume.tsx +237 -18
- package/src/tools/media/VideoPlayer/styles/video-player.css +87 -7
- package/src/tools/media/VideoPlayer/types.ts +4 -0
- package/src/tools/overlay/ResponsiveDialog/lazy.tsx +1 -1
- package/src/tools/overlay/ScrollSpy/lazy.tsx +1 -1
- package/src/tools/overlay/SelectionToolbar/lazy.tsx +1 -1
- package/src/tools/overlay/Tour/lazy.tsx +1 -1
- package/src/tools/visual/Marquee/lazy.tsx +1 -1
- package/src/tools/visual/QRCode/lazy.tsx +1 -1
- package/src/tools/visual/charts/ActivityGraph/ActivityGraph.tsx +195 -0
- package/src/tools/visual/charts/ActivityGraph/README.md +28 -0
- package/src/tools/visual/charts/ActivityGraph/index.ts +8 -0
- package/src/tools/visual/charts/ActivityGraph/lazy.tsx +21 -0
- package/src/tools/visual/charts/ActivityGraph/types.ts +59 -0
- package/src/tools/visual/charts/ActivityGraph/utils.ts +88 -0
- package/src/tools/visual/charts/CommitGraph/CommitGraph.tsx +80 -0
- package/src/tools/visual/charts/CommitGraph/README.md +28 -0
- package/src/tools/visual/charts/CommitGraph/components/CommitDetail.tsx +107 -0
- package/src/tools/visual/charts/CommitGraph/components/CommitRow.tsx +122 -0
- package/src/tools/visual/charts/CommitGraph/components/Rails.tsx +171 -0
- package/src/tools/visual/charts/CommitGraph/hooks/useGraphLayout.ts +169 -0
- package/src/tools/visual/charts/CommitGraph/hooks/useLaneColors.ts +45 -0
- package/src/tools/visual/charts/CommitGraph/index.ts +14 -0
- package/src/tools/visual/charts/CommitGraph/lazy.tsx +25 -0
- package/src/tools/visual/charts/CommitGraph/types.ts +85 -0
- package/src/tools/visual/charts/CommitGraph/utils.ts +53 -0
- package/src/tools/visual/{Gauge → charts/Gauge}/lazy.tsx +1 -1
- package/src/tools/visual/charts/Sparkline/README.md +29 -0
- package/src/tools/visual/charts/Sparkline/Sparkline.tsx +217 -0
- package/src/tools/visual/charts/Sparkline/index.ts +9 -0
- package/src/tools/visual/charts/Sparkline/lazy.tsx +26 -0
- package/src/tools/visual/charts/Sparkline/types.ts +58 -0
- package/src/tools/visual/design/ColorPalette/ColorPalette.tsx +129 -0
- package/src/tools/visual/design/ColorPalette/README.md +34 -0
- package/src/tools/visual/design/ColorPalette/components/Swatch.tsx +102 -0
- package/src/tools/visual/design/ColorPalette/hooks/useCopyToClipboard.ts +41 -0
- package/src/tools/visual/design/ColorPalette/index.ts +12 -0
- package/src/tools/visual/design/ColorPalette/lazy.tsx +21 -0
- package/src/tools/visual/design/ColorPalette/types.ts +63 -0
- package/src/tools/visual/design/ColorPalette/utils.ts +83 -0
- package/src/tools/visual/{ColorPicker → design/ColorPicker}/lazy.tsx +1 -1
- package/src/tools/visual/{FileIcon → design/FileIcon}/treeAdapter.tsx +1 -1
- package/src/tools/visual/{Fps → indicators/Fps}/lazy.tsx +1 -1
- package/src/tools/visual/{Rating → indicators/Rating}/lazy.tsx +1 -1
- package/src/tools/visual/indicators/StatusIndicator/README.md +28 -0
- package/src/tools/visual/indicators/StatusIndicator/StatusIndicator.tsx +83 -0
- package/src/tools/visual/indicators/StatusIndicator/index.ts +14 -0
- package/src/tools/visual/indicators/StatusIndicator/lazy.tsx +21 -0
- package/src/tools/visual/indicators/StatusIndicator/types.ts +133 -0
- package/src/components/FloatingToolbar/actions/CopyAction.tsx +0 -22
- package/src/components/FloatingToolbar/actions/ExpandAction.tsx +0 -25
- package/src/components/FloatingToolbar/actions/FullscreenAction.tsx +0 -30
- package/src/tools/data/JsonTree/components/JsonContent.tsx +0 -197
- package/src/tools/data/JsonTree/hooks/useJsonExpand.ts +0 -50
- /package/src/{components → common}/FloatingToolbar/FloatingToolbar.css +0 -0
- /package/src/{components → common}/FloatingToolbar/actions/index.ts +0 -0
- /package/src/{components → common}/FloatingToolbar/hooks/useElementCorner.ts +0 -0
- /package/src/{components → common}/FloatingToolbar/hooks/useScrollIsolation.ts +0 -0
- /package/src/{components → common}/index.ts +0 -0
- /package/src/{components → common}/lazy-wrapper.tsx +0 -0
- /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/README.md +0 -0
- /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/DocsView.tsx +0 -0
- /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/EndpointDoc/CodeSamples/LanguageTabs.tsx +0 -0
- /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/EndpointDoc/CodeSamples/useCodeSnippet.ts +0 -0
- /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/EndpointDoc/Header/MetaActions.tsx +0 -0
- /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/EndpointDoc/Header/MethodBadge.tsx +0 -0
- /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/EndpointDoc/Header/PathDisplay.tsx +0 -0
- /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/EndpointDoc/Parameters/ParamGroup.tsx +0 -0
- /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/EndpointDoc/Parameters/ParamRow.tsx +0 -0
- /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/EndpointDoc/Parameters/index.tsx +0 -0
- /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/EndpointDoc/RequestBody/index.tsx +0 -0
- /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/EndpointDoc/Responses/ResponseRow.tsx +0 -0
- /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/EndpointDoc/Responses/StatusTag.tsx +0 -0
- /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/EndpointDoc/Responses/index.tsx +0 -0
- /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/EndpointDoc/SchemaFields/FieldRow.tsx +0 -0
- /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/EndpointDoc/SchemaFields/buildTree.ts +0 -0
- /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/EndpointDoc/SchemaFields/index.tsx +0 -0
- /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/EndpointDoc/SchemaFields/types.ts +0 -0
- /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/EndpointDoc/Section/SectionHeader.tsx +0 -0
- /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/EndpointDoc/Section/defaults.ts +0 -0
- /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/EndpointDoc/Section/index.tsx +0 -0
- /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/EndpointDoc/context.tsx +0 -0
- /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/EndpointDoc/hooks/useSectionHash.ts +0 -0
- /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/EndpointDoc/index.tsx +0 -0
- /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/EndpointDoc/store/index.ts +0 -0
- /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/EndpointDoc/store/selectors.ts +0 -0
- /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/EndpointDoc/types.ts +0 -0
- /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/SchemaCopyMenu.tsx +0 -0
- /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/Sidebar/BrandHeader.tsx +0 -0
- /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/Sidebar/CategoryBlock.tsx +0 -0
- /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/Sidebar/EndpointRow.tsx +0 -0
- /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/Sidebar/SchemaSection.tsx +0 -0
- /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/Sidebar/SearchInput.tsx +0 -0
- /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/Sidebar/SidebarBody.tsx +0 -0
- /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/Sidebar/Toolbar.tsx +0 -0
- /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/Sidebar/buildVM.ts +0 -0
- /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/Sidebar/index.tsx +0 -0
- /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/Sidebar/types.ts +0 -0
- /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/Sidebar/useDebouncedValue.ts +0 -0
- /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/SlideInPlayground.tsx +0 -0
- /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/TryItSheet.tsx +0 -0
- /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/anchor.ts +0 -0
- /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/grouping.ts +0 -0
- /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/index.tsx +0 -0
- /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/sidebarLabel.ts +0 -0
- /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/index.ts +0 -0
- /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/shared/BodyFormEditor.tsx +0 -0
- /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/shared/EndpointDraftSync.tsx +0 -0
- /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/shared/EndpointResetButton.tsx +0 -0
- /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/shared/ResponsePanel/PreviewView.tsx +0 -0
- /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/shared/ResponsePanel/RawView.tsx +0 -0
- /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/shared/ResponsePanel/StatusBar.tsx +0 -0
- /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/shared/ResponsePanel/ViewTabs.tsx +0 -0
- /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/shared/ResponsePanel/detectContent.ts +0 -0
- /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/shared/ResponsePanel/index.tsx +0 -0
- /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/shared/ResponsePanel/useResponseView.ts +0 -0
- /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/shared/SendButton.tsx +0 -0
- /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/shared/ui.tsx +0 -0
- /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/constants.ts +0 -0
- /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/context/PlaygroundContext.tsx +0 -0
- /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/hooks/index.ts +0 -0
- /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/hooks/useDocsUrlSync.ts +0 -0
- /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/hooks/useEndpointDraft.ts +0 -0
- /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/hooks/useMobile.ts +0 -0
- /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/hooks/useOpenApiSchema.ts +0 -0
- /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/index.tsx +0 -0
- /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/types.ts +0 -0
- /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/utils/apiKeyManager.ts +0 -0
- /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/utils/codeSamples.ts +0 -0
- /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/utils/formatters.ts +0 -0
- /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/utils/index.ts +0 -0
- /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/utils/operationToHar.ts +0 -0
- /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/utils/sampler.ts +0 -0
- /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/utils/schemaExport.ts +0 -0
- /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/utils/scrollParent.ts +0 -0
- /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/utils/url.ts +0 -0
- /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/utils/versionManager.ts +0 -0
- /package/src/tools/dev/{MarkdownMessage → code/MarkdownMessage}/ActionRow.tsx +0 -0
- /package/src/tools/dev/{MarkdownMessage → code/MarkdownMessage}/ChatMessageRow.tsx +0 -0
- /package/src/tools/dev/{MarkdownMessage → code/MarkdownMessage}/CodeBlock.tsx +0 -0
- /package/src/tools/dev/{MarkdownMessage → code/MarkdownMessage}/README.md +0 -0
- /package/src/tools/dev/{MarkdownMessage → code/MarkdownMessage}/index.ts +0 -0
- /package/src/tools/dev/{MarkdownMessage → code/MarkdownMessage}/linkRules.ts +0 -0
- /package/src/tools/dev/{MarkdownMessage → code/MarkdownMessage}/plainText.ts +0 -0
- /package/src/tools/dev/{MarkdownMessage → code/MarkdownMessage}/sanitize.ts +0 -0
- /package/src/tools/dev/{MarkdownMessage → code/MarkdownMessage}/types.ts +0 -0
- /package/src/tools/dev/{PrettyCode → code/PrettyCode}/README.md +0 -0
- /package/src/tools/dev/{PrettyCode → code/PrettyCode}/index.tsx +0 -0
- /package/src/tools/dev/{PrettyCode → code/PrettyCode}/registerPrismLanguages.ts +0 -0
- /package/src/tools/visual/{Gauge → charts/Gauge}/Gauge.tsx +0 -0
- /package/src/tools/visual/{Gauge → charts/Gauge}/index.ts +0 -0
- /package/src/tools/visual/{Gauge → charts/Gauge}/types.ts +0 -0
- /package/src/tools/visual/{ColorPicker → design/ColorPicker}/ColorPicker.tsx +0 -0
- /package/src/tools/visual/{ColorPicker → design/ColorPicker}/context/ColorPickerContext.tsx +0 -0
- /package/src/tools/visual/{ColorPicker → design/ColorPicker}/context/ColorPickerStore.tsx +0 -0
- /package/src/tools/visual/{ColorPicker → design/ColorPicker}/context/index.ts +0 -0
- /package/src/tools/visual/{ColorPicker → design/ColorPicker}/index.ts +0 -0
- /package/src/tools/visual/{ColorPicker → design/ColorPicker}/lib/color-utils.ts +0 -0
- /package/src/tools/visual/{ColorPicker → design/ColorPicker}/parts/ColorPickerAlphaSlider.tsx +0 -0
- /package/src/tools/visual/{ColorPicker → design/ColorPicker}/parts/ColorPickerArea.tsx +0 -0
- /package/src/tools/visual/{ColorPicker → design/ColorPicker}/parts/ColorPickerEyeDropper.tsx +0 -0
- /package/src/tools/visual/{ColorPicker → design/ColorPicker}/parts/ColorPickerFormatSelect.tsx +0 -0
- /package/src/tools/visual/{ColorPicker → design/ColorPicker}/parts/ColorPickerHueSlider.tsx +0 -0
- /package/src/tools/visual/{ColorPicker → design/ColorPicker}/parts/ColorPickerInput.tsx +0 -0
- /package/src/tools/visual/{ColorPicker → design/ColorPicker}/parts/ColorPickerSwatch.tsx +0 -0
- /package/src/tools/visual/{ColorPicker → design/ColorPicker}/parts/index.ts +0 -0
- /package/src/tools/visual/{ColorPicker → design/ColorPicker}/types.ts +0 -0
- /package/src/tools/visual/{FileIcon → design/FileIcon}/FileIcon.tsx +0 -0
- /package/src/tools/visual/{FileIcon → design/FileIcon}/get-file-icon.ts +0 -0
- /package/src/tools/visual/{FileIcon → design/FileIcon}/icons/icon-data.ts +0 -0
- /package/src/tools/visual/{FileIcon → design/FileIcon}/index.ts +0 -0
- /package/src/tools/visual/{FileIcon → design/FileIcon}/loader.ts +0 -0
- /package/src/tools/visual/{FileIcon → design/FileIcon}/specialFolders.ts +0 -0
- /package/src/tools/visual/{Fps → indicators/Fps}/Fps.tsx +0 -0
- /package/src/tools/visual/{Fps → indicators/Fps}/index.ts +0 -0
- /package/src/tools/visual/{Fps → indicators/Fps}/types.ts +0 -0
- /package/src/tools/visual/{Rating → indicators/Rating}/Rating.tsx +0 -0
- /package/src/tools/visual/{Rating → indicators/Rating}/index.ts +0 -0
- /package/src/tools/visual/{Rating → indicators/Rating}/types.ts +0 -0
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
forwardRef,
|
|
5
|
+
useCallback,
|
|
6
|
+
useEffect,
|
|
7
|
+
useImperativeHandle,
|
|
8
|
+
useRef,
|
|
9
|
+
useState,
|
|
10
|
+
} from 'react';
|
|
11
|
+
import type { SlashItem } from './slashItems';
|
|
12
|
+
|
|
13
|
+
export interface SlashListRef {
|
|
14
|
+
/** Forwarded from `@tiptap/suggestion.render().onKeyDown`, which fires
|
|
15
|
+
* the underlying native `KeyboardEvent` — typing this as React's
|
|
16
|
+
* synthetic event would be a lie. */
|
|
17
|
+
onKeyDown: (event: KeyboardEvent) => boolean;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
interface SlashListProps {
|
|
21
|
+
items: SlashItem[];
|
|
22
|
+
command: (item: SlashItem) => void;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const LISTBOX_ID = 'notion-slash-menu';
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Slash-command popover content. Mirrors the MentionList pattern —
|
|
29
|
+
* Arrow keys / Enter / Tab keyboard navigation, mouse hover preview,
|
|
30
|
+
* auto-scroll active item into view.
|
|
31
|
+
*/
|
|
32
|
+
export const SlashList = forwardRef<SlashListRef, SlashListProps>(
|
|
33
|
+
({ items, command }, ref) => {
|
|
34
|
+
const [selectedIndex, setSelectedIndex] = useState(0);
|
|
35
|
+
const itemRefs = useRef<(HTMLButtonElement | null)[]>([]);
|
|
36
|
+
|
|
37
|
+
useEffect(() => setSelectedIndex(0), [items]);
|
|
38
|
+
|
|
39
|
+
useEffect(() => {
|
|
40
|
+
itemRefs.current[selectedIndex]?.scrollIntoView({ block: 'nearest' });
|
|
41
|
+
}, [selectedIndex]);
|
|
42
|
+
|
|
43
|
+
const select = useCallback(
|
|
44
|
+
(index: number) => {
|
|
45
|
+
const item = items[index];
|
|
46
|
+
if (item) command(item);
|
|
47
|
+
},
|
|
48
|
+
[items, command],
|
|
49
|
+
);
|
|
50
|
+
|
|
51
|
+
useImperativeHandle(ref, () => ({
|
|
52
|
+
onKeyDown: (event: KeyboardEvent) => {
|
|
53
|
+
if (items.length === 0) return false;
|
|
54
|
+
if (event.key === 'ArrowUp') {
|
|
55
|
+
setSelectedIndex((i) => (i + items.length - 1) % items.length);
|
|
56
|
+
return true;
|
|
57
|
+
}
|
|
58
|
+
if (event.key === 'ArrowDown') {
|
|
59
|
+
setSelectedIndex((i) => (i + 1) % items.length);
|
|
60
|
+
return true;
|
|
61
|
+
}
|
|
62
|
+
if (event.key === 'Enter' || event.key === 'Tab') {
|
|
63
|
+
select(selectedIndex);
|
|
64
|
+
return true;
|
|
65
|
+
}
|
|
66
|
+
return false;
|
|
67
|
+
},
|
|
68
|
+
}));
|
|
69
|
+
|
|
70
|
+
// Empty state — render a "no matches" pill instead of vanishing so
|
|
71
|
+
// the user gets feedback that the popover is alive (not a bug).
|
|
72
|
+
if (items.length === 0) {
|
|
73
|
+
return (
|
|
74
|
+
<div
|
|
75
|
+
id={LISTBOX_ID}
|
|
76
|
+
role="listbox"
|
|
77
|
+
aria-label="Slash commands"
|
|
78
|
+
className="notion-slash-list"
|
|
79
|
+
>
|
|
80
|
+
<div className="notion-slash-empty">No matching blocks</div>
|
|
81
|
+
</div>
|
|
82
|
+
);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const activeId = `${LISTBOX_ID}-${selectedIndex}`;
|
|
86
|
+
|
|
87
|
+
return (
|
|
88
|
+
<div
|
|
89
|
+
id={LISTBOX_ID}
|
|
90
|
+
role="listbox"
|
|
91
|
+
aria-label="Slash commands"
|
|
92
|
+
aria-activedescendant={activeId}
|
|
93
|
+
className="notion-slash-list"
|
|
94
|
+
>
|
|
95
|
+
{items.map((item, i) => {
|
|
96
|
+
const Icon = item.icon;
|
|
97
|
+
const isActive = i === selectedIndex;
|
|
98
|
+
return (
|
|
99
|
+
<button
|
|
100
|
+
key={item.title}
|
|
101
|
+
id={`${LISTBOX_ID}-${i}`}
|
|
102
|
+
ref={(el) => {
|
|
103
|
+
itemRefs.current[i] = el;
|
|
104
|
+
}}
|
|
105
|
+
type="button"
|
|
106
|
+
role="option"
|
|
107
|
+
aria-selected={isActive}
|
|
108
|
+
onMouseEnter={() => setSelectedIndex(i)}
|
|
109
|
+
onClick={() => select(i)}
|
|
110
|
+
// Prevent the click from blurring the editor — without this,
|
|
111
|
+
// ProseMirror loses its selection before the slash command
|
|
112
|
+
// can resolve the range.
|
|
113
|
+
onMouseDown={(e) => e.preventDefault()}
|
|
114
|
+
className={`notion-slash-item${isActive ? ' notion-slash-item--active' : ''}`}
|
|
115
|
+
>
|
|
116
|
+
<span className="notion-slash-icon">
|
|
117
|
+
<Icon style={{ width: 16, height: 16 }} aria-hidden />
|
|
118
|
+
</span>
|
|
119
|
+
<span className="notion-slash-meta">
|
|
120
|
+
<span className="notion-slash-title">{item.title}</span>
|
|
121
|
+
<span className="notion-slash-desc">{item.description}</span>
|
|
122
|
+
</span>
|
|
123
|
+
{item.hint ? (
|
|
124
|
+
<span className="notion-slash-hint" aria-hidden>
|
|
125
|
+
{item.hint}
|
|
126
|
+
</span>
|
|
127
|
+
) : null}
|
|
128
|
+
</button>
|
|
129
|
+
);
|
|
130
|
+
})}
|
|
131
|
+
</div>
|
|
132
|
+
);
|
|
133
|
+
},
|
|
134
|
+
);
|
|
135
|
+
|
|
136
|
+
SlashList.displayName = 'SlashList';
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* React NodeView for `taskItem`. The default Tiptap renderer mints a
|
|
5
|
+
* raw `<input type="checkbox">`; we swap it for our ui-core `<Checkbox>`
|
|
6
|
+
* so task lists pick up the macOS / app token styling (radius, ring,
|
|
7
|
+
* focus state) and stay theme-aware.
|
|
8
|
+
*
|
|
9
|
+
* Wired via `TaskItem.extend({ addNodeView: () => ReactNodeViewRenderer(TaskItemView) })`
|
|
10
|
+
* — TaskItem.attributes already expose `checked` and `editor.commands`
|
|
11
|
+
* persists the change back into ProseMirror state. We only render the UI.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import { NodeViewContent, NodeViewWrapper, type NodeViewProps } from '@tiptap/react';
|
|
15
|
+
import { Checkbox } from '@djangocfg/ui-core';
|
|
16
|
+
|
|
17
|
+
export function TaskItemView({ node, updateAttributes, editor }: NodeViewProps) {
|
|
18
|
+
const checked = node.attrs.checked === true;
|
|
19
|
+
const isEditable = editor.isEditable;
|
|
20
|
+
|
|
21
|
+
return (
|
|
22
|
+
<NodeViewWrapper
|
|
23
|
+
as="li"
|
|
24
|
+
data-type="taskList"
|
|
25
|
+
data-checked={checked || undefined}
|
|
26
|
+
className="notion-task-item"
|
|
27
|
+
>
|
|
28
|
+
<Checkbox
|
|
29
|
+
checked={checked}
|
|
30
|
+
disabled={!isEditable}
|
|
31
|
+
onCheckedChange={(value) => updateAttributes({ checked: value === true })}
|
|
32
|
+
// Stop ProseMirror from intercepting the click — without this
|
|
33
|
+
// the editor steals focus mid-toggle and the state flickers.
|
|
34
|
+
onClick={(event) => event.stopPropagation()}
|
|
35
|
+
aria-label={checked ? 'Mark as not done' : 'Mark as done'}
|
|
36
|
+
className="notion-task-checkbox"
|
|
37
|
+
/>
|
|
38
|
+
<NodeViewContent as="div" className="notion-task-text" />
|
|
39
|
+
</NodeViewWrapper>
|
|
40
|
+
);
|
|
41
|
+
}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import { ReactRenderer } from '@tiptap/react';
|
|
2
|
+
import type { SuggestionOptions } from '@tiptap/suggestion';
|
|
3
|
+
import { autoUpdate, computePosition, flip, offset, shift } from '@floating-ui/dom';
|
|
4
|
+
import { SlashList, type SlashListRef } from './SlashList';
|
|
5
|
+
import { filterSlashItems, type SlashItem } from './slashItems';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* `@tiptap/suggestion` config for the slash menu. Mirrors the mention
|
|
9
|
+
* suggestion pattern in `MarkdownEditor/createMentionSuggestion.ts`:
|
|
10
|
+
* floating-ui positioning, virtual element backed by the caret rect,
|
|
11
|
+
* keyboard nav delegated to `SlashList` via an imperative ref.
|
|
12
|
+
*
|
|
13
|
+
* Editor never imports this directly — `notionExtensions()` consumes it.
|
|
14
|
+
*/
|
|
15
|
+
export function createSlashSuggestion(): Omit<SuggestionOptions<SlashItem>, 'editor'> {
|
|
16
|
+
return {
|
|
17
|
+
char: '/',
|
|
18
|
+
// Block the menu inside code blocks — `/` is valid syntax there
|
|
19
|
+
// (regex literals, paths) and a floating popover would be noise.
|
|
20
|
+
// Use `editor.isActive('codeBlock')` rather than `state.doc.resolve()`
|
|
21
|
+
// so nested cases (code block in a table cell, in a list item) all
|
|
22
|
+
// resolve correctly — `parent.type.name` only sees the immediate
|
|
23
|
+
// ancestor, which can be the cell/li instead of the code block.
|
|
24
|
+
allow: ({ editor }) => !editor.isActive('codeBlock'),
|
|
25
|
+
items: ({ query }) => filterSlashItems(query),
|
|
26
|
+
command: ({ editor, range, props }) => {
|
|
27
|
+
props.command({ editor, range });
|
|
28
|
+
},
|
|
29
|
+
|
|
30
|
+
render: () => {
|
|
31
|
+
let component: ReactRenderer<SlashListRef> | null = null;
|
|
32
|
+
let popup: HTMLDivElement | null = null;
|
|
33
|
+
let cleanupAutoUpdate: (() => void) | null = null;
|
|
34
|
+
let getReferenceRect: (() => DOMRect | null) | null = null;
|
|
35
|
+
|
|
36
|
+
const buildVirtualElement = () => ({
|
|
37
|
+
getBoundingClientRect: () => getReferenceRect?.() ?? new DOMRect(0, 0, 0, 0),
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
const setPopupVisible = (visible: boolean) => {
|
|
41
|
+
if (popup) popup.style.display = visible ? '' : 'none';
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
const updatePosition = () => {
|
|
45
|
+
if (!popup) return;
|
|
46
|
+
const virtualEl = buildVirtualElement();
|
|
47
|
+
void computePosition(virtualEl, popup, {
|
|
48
|
+
placement: 'bottom-start',
|
|
49
|
+
middleware: [
|
|
50
|
+
offset(6),
|
|
51
|
+
flip({ fallbackPlacements: ['top-start'] }),
|
|
52
|
+
shift({ padding: 8 }),
|
|
53
|
+
],
|
|
54
|
+
}).then(({ x, y }) => {
|
|
55
|
+
if (!popup) return;
|
|
56
|
+
popup.style.transform = `translate3d(${Math.round(x)}px, ${Math.round(y)}px, 0)`;
|
|
57
|
+
});
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
const teardown = () => {
|
|
61
|
+
cleanupAutoUpdate?.();
|
|
62
|
+
cleanupAutoUpdate = null;
|
|
63
|
+
popup?.remove();
|
|
64
|
+
popup = null;
|
|
65
|
+
component?.destroy();
|
|
66
|
+
component = null;
|
|
67
|
+
getReferenceRect = null;
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
return {
|
|
71
|
+
onStart: (props) => {
|
|
72
|
+
component = new ReactRenderer(SlashList, {
|
|
73
|
+
props: {
|
|
74
|
+
items: props.items,
|
|
75
|
+
command: (item: SlashItem) => {
|
|
76
|
+
props.command(item);
|
|
77
|
+
},
|
|
78
|
+
},
|
|
79
|
+
editor: props.editor,
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
popup = document.createElement('div');
|
|
83
|
+
popup.style.cssText = 'position: absolute; top: 0; left: 0; z-index: 99999;';
|
|
84
|
+
popup.appendChild(component.element);
|
|
85
|
+
document.body.appendChild(popup);
|
|
86
|
+
setPopupVisible(props.items.length > 0);
|
|
87
|
+
|
|
88
|
+
getReferenceRect = () => props.clientRect?.() ?? null;
|
|
89
|
+
const virtualEl = buildVirtualElement();
|
|
90
|
+
cleanupAutoUpdate = autoUpdate(virtualEl, popup, updatePosition);
|
|
91
|
+
},
|
|
92
|
+
|
|
93
|
+
onUpdate: (props) => {
|
|
94
|
+
component?.updateProps({
|
|
95
|
+
items: props.items,
|
|
96
|
+
command: (item: SlashItem) => {
|
|
97
|
+
props.command(item);
|
|
98
|
+
},
|
|
99
|
+
});
|
|
100
|
+
setPopupVisible(props.items.length > 0);
|
|
101
|
+
getReferenceRect = () => props.clientRect?.() ?? null;
|
|
102
|
+
updatePosition();
|
|
103
|
+
},
|
|
104
|
+
|
|
105
|
+
onKeyDown: (props) => {
|
|
106
|
+
if (props.event.key === 'Escape') {
|
|
107
|
+
teardown();
|
|
108
|
+
return true;
|
|
109
|
+
}
|
|
110
|
+
// `props.event` is a native KeyboardEvent — SlashListRef types
|
|
111
|
+
// it as such, no cast needed.
|
|
112
|
+
return component?.ref?.onKeyDown(props.event) ?? false;
|
|
113
|
+
},
|
|
114
|
+
|
|
115
|
+
onExit: () => {
|
|
116
|
+
teardown();
|
|
117
|
+
},
|
|
118
|
+
};
|
|
119
|
+
},
|
|
120
|
+
};
|
|
121
|
+
}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import type { AnyExtension } from '@tiptap/core';
|
|
2
|
+
import { ReactNodeViewRenderer } from '@tiptap/react';
|
|
3
|
+
import StarterKit from '@tiptap/starter-kit';
|
|
4
|
+
import Placeholder from '@tiptap/extension-placeholder';
|
|
5
|
+
import Highlight from '@tiptap/extension-highlight';
|
|
6
|
+
import TaskList from '@tiptap/extension-task-list';
|
|
7
|
+
import TaskItem from '@tiptap/extension-task-item';
|
|
8
|
+
import { Table } from '@tiptap/extension-table';
|
|
9
|
+
import { TableRow } from '@tiptap/extension-table-row';
|
|
10
|
+
import { TableHeader } from '@tiptap/extension-table-header';
|
|
11
|
+
import { TableCell } from '@tiptap/extension-table-cell';
|
|
12
|
+
import CodeBlockLowlight from '@tiptap/extension-code-block-lowlight';
|
|
13
|
+
import { Markdown } from '@tiptap/markdown';
|
|
14
|
+
import { common, createLowlight } from 'lowlight';
|
|
15
|
+
import GlobalDragHandle from 'tiptap-extension-global-drag-handle';
|
|
16
|
+
import { SlashExtension } from './SlashExtension';
|
|
17
|
+
import { createSlashSuggestion } from './createSlashSuggestion';
|
|
18
|
+
import { TaskItemView } from './TaskItemView';
|
|
19
|
+
import { CustomKeymap } from './CustomKeymap';
|
|
20
|
+
|
|
21
|
+
// Lowlight bundle: "common" languages cover ts/js/json/py/go/rust/etc.
|
|
22
|
+
// The full language pack pulls in ~300KB more — opt-in case by case.
|
|
23
|
+
const lowlight = createLowlight(common);
|
|
24
|
+
|
|
25
|
+
export interface BuildExtensionsOptions {
|
|
26
|
+
placeholder: string;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Assemble the Notion-flavour TipTap extension stack.
|
|
31
|
+
*
|
|
32
|
+
* Why a single factory rather than inline `useEditor({ extensions: [...] })`:
|
|
33
|
+
* - `useEditor` captures extensions once on first render. If we built
|
|
34
|
+
* them inline, every parent re-render would create new instances and
|
|
35
|
+
* React's deps array would force a teardown loop (or, with stable
|
|
36
|
+
* deps, we'd be hiding the problem).
|
|
37
|
+
* - All consumers want the same baseline. Splitting per-extension wires
|
|
38
|
+
* to a builder keeps the assembly readable.
|
|
39
|
+
*
|
|
40
|
+
* StarterKit overrides:
|
|
41
|
+
* - `codeBlock: false` — replaced by `CodeBlockLowlight` for syntax
|
|
42
|
+
* highlighting via lowlight's `common` language pack.
|
|
43
|
+
*
|
|
44
|
+
* Markdown serialisation:
|
|
45
|
+
* - `@tiptap/markdown` already handles starter-kit nodes + tables.
|
|
46
|
+
* - Task lists serialise as GFM `- [x]` / `- [ ]` via the same package.
|
|
47
|
+
* - `Highlight` round-trips as `==text==` (tiptap-markdown default).
|
|
48
|
+
*/
|
|
49
|
+
export function notionExtensions(opts: BuildExtensionsOptions): AnyExtension[] {
|
|
50
|
+
return [
|
|
51
|
+
StarterKit.configure({
|
|
52
|
+
heading: { levels: [1, 2, 3, 4] },
|
|
53
|
+
// Disable starter-kit's plain CodeBlock — we replace it with the
|
|
54
|
+
// lowlight variant below for syntax highlighting.
|
|
55
|
+
codeBlock: false,
|
|
56
|
+
}),
|
|
57
|
+
Placeholder.configure({
|
|
58
|
+
// Per-node placeholder: empty H1 → "Heading 1", H2 → "Heading 2",
|
|
59
|
+
// empty paragraph → the supplied placeholder ("Type / for…").
|
|
60
|
+
// Matches Notion: an empty heading is labelled with its level so the
|
|
61
|
+
// user sees structure before content; an empty body block shows the
|
|
62
|
+
// command hint. The function form is supported by tiptap-extensions
|
|
63
|
+
// Placeholder; the `node` arg is the block at the caret.
|
|
64
|
+
placeholder: ({ node }) => {
|
|
65
|
+
if (node.type.name === 'heading') {
|
|
66
|
+
const level = node.attrs.level as number | undefined;
|
|
67
|
+
return level ? `Heading ${level}` : 'Heading';
|
|
68
|
+
}
|
|
69
|
+
return opts.placeholder;
|
|
70
|
+
},
|
|
71
|
+
// includeChildren=true so placeholders also reach inside list items
|
|
72
|
+
// and quotes (otherwise an empty bullet line shows no hint).
|
|
73
|
+
includeChildren: true,
|
|
74
|
+
}),
|
|
75
|
+
Markdown,
|
|
76
|
+
Highlight.configure({ multicolor: false }),
|
|
77
|
+
TaskList,
|
|
78
|
+
// React NodeView swaps Tiptap's default `<input type="checkbox">`
|
|
79
|
+
// for our ui-core <Checkbox> — picks up macOS / token styling and
|
|
80
|
+
// tracks the editor theme automatically. See TaskItemView.tsx.
|
|
81
|
+
TaskItem.extend({
|
|
82
|
+
addNodeView() {
|
|
83
|
+
return ReactNodeViewRenderer(TaskItemView);
|
|
84
|
+
},
|
|
85
|
+
}).configure({ nested: true }),
|
|
86
|
+
Table.configure({ resizable: false }),
|
|
87
|
+
TableRow,
|
|
88
|
+
TableHeader,
|
|
89
|
+
TableCell,
|
|
90
|
+
CodeBlockLowlight.configure({ lowlight }),
|
|
91
|
+
// Drag handle for blocks. Renders a small grabber to the left of the
|
|
92
|
+
// hovered block; clicking + dragging reorders the document. Reads CSS
|
|
93
|
+
// from our styles.css (`.drag-handle`).
|
|
94
|
+
GlobalDragHandle.configure({
|
|
95
|
+
dragHandleWidth: 18,
|
|
96
|
+
// `scrollTreshold` (sic) is the option name the upstream library
|
|
97
|
+
// ships — typo and all. Spelling it correctly is silently ignored.
|
|
98
|
+
scrollTreshold: 100,
|
|
99
|
+
}),
|
|
100
|
+
SlashExtension.configure({
|
|
101
|
+
suggestion: createSlashSuggestion(),
|
|
102
|
+
}),
|
|
103
|
+
CustomKeymap,
|
|
104
|
+
];
|
|
105
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './lazy';
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* `@djangocfg/ui-tools/notion-editor` subpath entrypoint.
|
|
5
|
+
*
|
|
6
|
+
* `NotionEditor` is a TipTap WYSIWYG with the full Notion-style stack:
|
|
7
|
+
* StarterKit + Markdown + Highlight + Table + TaskList + CodeBlockLowlight
|
|
8
|
+
* + GlobalDragHandle + a custom `/` slash menu. The full bundle including
|
|
9
|
+
* lowlight's `common` language pack lands around ~350KB minified — wrap
|
|
10
|
+
* the component in React.lazy so pages that don't render the editor pay
|
|
11
|
+
* nothing.
|
|
12
|
+
*
|
|
13
|
+
* Why a hand-rolled lazy wrapper (mirrors MarkdownEditor/lazy.tsx):
|
|
14
|
+
* `createLazyComponent` returns a plain function component that does not
|
|
15
|
+
* forward `ref`, so the imperative `NotionEditorHandle` would be silently
|
|
16
|
+
* dropped. The wrapper below is a `forwardRef` so the ref reaches the
|
|
17
|
+
* underlying TipTap editor through `React.lazy`.
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
import { Suspense, forwardRef, lazy } from 'react';
|
|
21
|
+
import { LoadingFallback } from '../../../common';
|
|
22
|
+
import type { NotionEditorHandle, NotionEditorProps } from './types';
|
|
23
|
+
|
|
24
|
+
const NotionEditorImpl = lazy(() =>
|
|
25
|
+
import('./NotionEditor').then((m) => ({ default: m.NotionEditor })),
|
|
26
|
+
);
|
|
27
|
+
|
|
28
|
+
export const LazyNotionEditor = forwardRef<NotionEditorHandle, NotionEditorProps>(
|
|
29
|
+
function LazyNotionEditor(props, ref) {
|
|
30
|
+
return (
|
|
31
|
+
<Suspense fallback={<LoadingFallback minHeight={240} text="Loading editor…" />}>
|
|
32
|
+
<NotionEditorImpl {...props} ref={ref} />
|
|
33
|
+
</Suspense>
|
|
34
|
+
);
|
|
35
|
+
},
|
|
36
|
+
);
|
|
37
|
+
|
|
38
|
+
// `NotionEditor` is the public eager-looking name — it resolves to the
|
|
39
|
+
// same lazy component so importing it from this subpath does NOT pull
|
|
40
|
+
// ~350KB of TipTap + lowlight into the caller's initial bundle.
|
|
41
|
+
export { LazyNotionEditor as NotionEditor };
|
|
42
|
+
|
|
43
|
+
export type { NotionEditorHandle, NotionEditorProps } from './types';
|
|
44
|
+
export type { SlashItem } from './slashItems';
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
import {
|
|
2
|
+
CheckSquare,
|
|
3
|
+
Code,
|
|
4
|
+
Heading1,
|
|
5
|
+
Heading2,
|
|
6
|
+
Heading3,
|
|
7
|
+
List,
|
|
8
|
+
ListOrdered,
|
|
9
|
+
Minus,
|
|
10
|
+
Quote,
|
|
11
|
+
Table as TableIcon,
|
|
12
|
+
Text,
|
|
13
|
+
type LucideIcon,
|
|
14
|
+
} from 'lucide-react';
|
|
15
|
+
import type { Editor, Range } from '@tiptap/react';
|
|
16
|
+
|
|
17
|
+
export interface SlashItem {
|
|
18
|
+
title: string;
|
|
19
|
+
description: string;
|
|
20
|
+
icon: LucideIcon;
|
|
21
|
+
/** Markdown shorthand hint shown right-aligned in the menu — teaches
|
|
22
|
+
* the user that they can also type this directly (e.g. `#`, `- [ ]`). */
|
|
23
|
+
hint?: string;
|
|
24
|
+
/** Extra search aliases (besides `title`). */
|
|
25
|
+
aliases?: string[];
|
|
26
|
+
command: (ctx: { editor: Editor; range: Range }) => void;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Canonical Notion-style slash menu. Each command runs `deleteRange(range)`
|
|
31
|
+
* first so the typed `/verb` text is removed, then applies the block
|
|
32
|
+
* transformation. `focus()` keeps the caret in the editor after the
|
|
33
|
+
* popover closes.
|
|
34
|
+
*/
|
|
35
|
+
export const slashItems: SlashItem[] = [
|
|
36
|
+
{
|
|
37
|
+
title: 'Text',
|
|
38
|
+
description: 'Plain paragraph.',
|
|
39
|
+
icon: Text,
|
|
40
|
+
aliases: ['p', 'paragraph'],
|
|
41
|
+
command: ({ editor, range }) => {
|
|
42
|
+
editor.chain().focus().deleteRange(range).setNode('paragraph').run();
|
|
43
|
+
},
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
title: 'Heading 1',
|
|
47
|
+
description: 'Big section heading.',
|
|
48
|
+
icon: Heading1,
|
|
49
|
+
hint: '#',
|
|
50
|
+
aliases: ['h1', 'title'],
|
|
51
|
+
command: ({ editor, range }) => {
|
|
52
|
+
editor.chain().focus().deleteRange(range).setNode('heading', { level: 1 }).run();
|
|
53
|
+
},
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
title: 'Heading 2',
|
|
57
|
+
description: 'Medium section heading.',
|
|
58
|
+
icon: Heading2,
|
|
59
|
+
hint: '##',
|
|
60
|
+
aliases: ['h2', 'subtitle'],
|
|
61
|
+
command: ({ editor, range }) => {
|
|
62
|
+
editor.chain().focus().deleteRange(range).setNode('heading', { level: 2 }).run();
|
|
63
|
+
},
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
title: 'Heading 3',
|
|
67
|
+
description: 'Small section heading.',
|
|
68
|
+
icon: Heading3,
|
|
69
|
+
hint: '###',
|
|
70
|
+
aliases: ['h3'],
|
|
71
|
+
command: ({ editor, range }) => {
|
|
72
|
+
editor.chain().focus().deleteRange(range).setNode('heading', { level: 3 }).run();
|
|
73
|
+
},
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
title: 'Bullet list',
|
|
77
|
+
description: 'Simple unordered list.',
|
|
78
|
+
icon: List,
|
|
79
|
+
hint: '- ',
|
|
80
|
+
aliases: ['ul', 'unordered'],
|
|
81
|
+
command: ({ editor, range }) => {
|
|
82
|
+
editor.chain().focus().deleteRange(range).toggleBulletList().run();
|
|
83
|
+
},
|
|
84
|
+
},
|
|
85
|
+
{
|
|
86
|
+
title: 'Numbered list',
|
|
87
|
+
description: 'List with auto-numbering.',
|
|
88
|
+
icon: ListOrdered,
|
|
89
|
+
hint: '1. ',
|
|
90
|
+
aliases: ['ol', 'ordered', 'numbered'],
|
|
91
|
+
command: ({ editor, range }) => {
|
|
92
|
+
editor.chain().focus().deleteRange(range).toggleOrderedList().run();
|
|
93
|
+
},
|
|
94
|
+
},
|
|
95
|
+
{
|
|
96
|
+
title: 'To-do list',
|
|
97
|
+
description: 'Tasks with checkboxes.',
|
|
98
|
+
icon: CheckSquare,
|
|
99
|
+
hint: '- [ ]',
|
|
100
|
+
aliases: ['todo', 'task', 'checkbox', 'check'],
|
|
101
|
+
command: ({ editor, range }) => {
|
|
102
|
+
editor.chain().focus().deleteRange(range).toggleTaskList().run();
|
|
103
|
+
},
|
|
104
|
+
},
|
|
105
|
+
{
|
|
106
|
+
title: 'Quote',
|
|
107
|
+
description: 'Capture a quote.',
|
|
108
|
+
icon: Quote,
|
|
109
|
+
hint: '> ',
|
|
110
|
+
aliases: ['blockquote'],
|
|
111
|
+
command: ({ editor, range }) => {
|
|
112
|
+
editor.chain().focus().deleteRange(range).toggleBlockquote().run();
|
|
113
|
+
},
|
|
114
|
+
},
|
|
115
|
+
{
|
|
116
|
+
title: 'Code block',
|
|
117
|
+
description: 'Syntax-highlighted code.',
|
|
118
|
+
icon: Code,
|
|
119
|
+
hint: '```',
|
|
120
|
+
aliases: ['codeblock', 'pre'],
|
|
121
|
+
command: ({ editor, range }) => {
|
|
122
|
+
editor.chain().focus().deleteRange(range).toggleCodeBlock().run();
|
|
123
|
+
},
|
|
124
|
+
},
|
|
125
|
+
{
|
|
126
|
+
title: 'Divider',
|
|
127
|
+
description: 'Horizontal rule.',
|
|
128
|
+
icon: Minus,
|
|
129
|
+
hint: '---',
|
|
130
|
+
aliases: ['hr', 'rule', 'separator'],
|
|
131
|
+
command: ({ editor, range }) => {
|
|
132
|
+
editor.chain().focus().deleteRange(range).setHorizontalRule().run();
|
|
133
|
+
},
|
|
134
|
+
},
|
|
135
|
+
{
|
|
136
|
+
title: 'Table',
|
|
137
|
+
description: '3×3 starter table.',
|
|
138
|
+
icon: TableIcon,
|
|
139
|
+
aliases: ['grid'],
|
|
140
|
+
command: ({ editor, range }) => {
|
|
141
|
+
editor
|
|
142
|
+
.chain()
|
|
143
|
+
.focus()
|
|
144
|
+
.deleteRange(range)
|
|
145
|
+
.insertTable({ rows: 3, cols: 3, withHeaderRow: true })
|
|
146
|
+
.run();
|
|
147
|
+
},
|
|
148
|
+
},
|
|
149
|
+
];
|
|
150
|
+
|
|
151
|
+
/** Case-insensitive filter over title + aliases. */
|
|
152
|
+
export function filterSlashItems(query: string): SlashItem[] {
|
|
153
|
+
const q = query.trim().toLowerCase();
|
|
154
|
+
if (!q) return slashItems;
|
|
155
|
+
return slashItems.filter((item) => {
|
|
156
|
+
if (item.title.toLowerCase().includes(q)) return true;
|
|
157
|
+
return item.aliases?.some((a) => a.toLowerCase().includes(q)) ?? false;
|
|
158
|
+
});
|
|
159
|
+
}
|