@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
|
@@ -2,49 +2,88 @@
|
|
|
2
2
|
outline: none;
|
|
3
3
|
}
|
|
4
4
|
|
|
5
|
-
/*
|
|
6
|
-
|
|
7
|
-
|
|
5
|
+
/* ─────────────────────────────────────────────────────────────────────
|
|
6
|
+
* Typography — Notion-inspired: comfy line-height, generous heading
|
|
7
|
+
* scale, restrained spacing. Calibrated so the chat composer (compact,
|
|
8
|
+
* single-paragraph) and the document-preview editor (long-form, with
|
|
9
|
+
* many headings) both feel right without a variant prop.
|
|
10
|
+
*
|
|
11
|
+
* Inherit semantic foreground so the editor renders correctly in both
|
|
12
|
+
* light + dark themes (and under any active preset).
|
|
13
|
+
* ───────────────────────────────────────────────────────────────────── */
|
|
8
14
|
.markdown-editor .tiptap,
|
|
9
15
|
.markdown-editor .ProseMirror {
|
|
10
16
|
color: var(--color-foreground, var(--foreground));
|
|
17
|
+
font-family:
|
|
18
|
+
-apple-system, BlinkMacSystemFont, "SF Pro Text", "Segoe UI", Roboto,
|
|
19
|
+
"Helvetica Neue", Arial, sans-serif;
|
|
20
|
+
font-size: 15px;
|
|
21
|
+
line-height: 1.6;
|
|
22
|
+
-webkit-font-smoothing: antialiased;
|
|
23
|
+
-moz-osx-font-smoothing: grayscale;
|
|
24
|
+
text-rendering: optimizeLegibility;
|
|
11
25
|
}
|
|
12
26
|
|
|
13
27
|
.markdown-editor .tiptap a,
|
|
14
28
|
.markdown-editor .ProseMirror a {
|
|
15
29
|
color: var(--color-primary, var(--primary));
|
|
16
30
|
text-decoration: underline;
|
|
31
|
+
text-underline-offset: 2px;
|
|
32
|
+
text-decoration-thickness: 1px;
|
|
17
33
|
}
|
|
18
34
|
|
|
35
|
+
.markdown-editor .tiptap a:hover,
|
|
36
|
+
.markdown-editor .ProseMirror a:hover {
|
|
37
|
+
text-decoration-thickness: 2px;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/* Headings — Notion scale. Top-padding > bottom so a heading sits
|
|
41
|
+
* visually grouped with the paragraph below it. */
|
|
19
42
|
.markdown-editor .tiptap h1 {
|
|
20
|
-
font-size: 1.
|
|
43
|
+
font-size: 1.875em;
|
|
21
44
|
font-weight: 700;
|
|
22
|
-
|
|
23
|
-
|
|
45
|
+
line-height: 1.25;
|
|
46
|
+
margin: 1em 0 0.25em;
|
|
47
|
+
letter-spacing: -0.015em;
|
|
24
48
|
}
|
|
25
49
|
|
|
26
50
|
.markdown-editor .tiptap h2 {
|
|
27
|
-
font-size: 1.
|
|
51
|
+
font-size: 1.5em;
|
|
28
52
|
font-weight: 600;
|
|
29
|
-
margin: 0.5em 0 0.25em;
|
|
30
53
|
line-height: 1.3;
|
|
54
|
+
margin: 0.9em 0 0.25em;
|
|
55
|
+
letter-spacing: -0.01em;
|
|
31
56
|
}
|
|
32
57
|
|
|
33
58
|
.markdown-editor .tiptap h3 {
|
|
59
|
+
font-size: 1.25em;
|
|
60
|
+
font-weight: 600;
|
|
61
|
+
line-height: 1.35;
|
|
62
|
+
margin: 0.8em 0 0.2em;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
.markdown-editor .tiptap h4 {
|
|
34
66
|
font-size: 1.1em;
|
|
35
67
|
font-weight: 600;
|
|
36
|
-
|
|
37
|
-
|
|
68
|
+
line-height: 1.4;
|
|
69
|
+
margin: 0.7em 0 0.2em;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/* First heading at the very top of the document has no leading air —
|
|
73
|
+
* Notion convention. */
|
|
74
|
+
.markdown-editor .tiptap > :is(h1, h2, h3, h4):first-child {
|
|
75
|
+
margin-top: 0;
|
|
38
76
|
}
|
|
39
77
|
|
|
40
78
|
.markdown-editor .tiptap p {
|
|
41
79
|
margin: 0.25em 0;
|
|
42
80
|
}
|
|
43
81
|
|
|
82
|
+
/* Lists — Notion uses tighter row spacing than browser default. */
|
|
44
83
|
.markdown-editor .tiptap ul,
|
|
45
84
|
.markdown-editor .tiptap ol {
|
|
46
85
|
padding-left: 1.5em;
|
|
47
|
-
margin: 0.
|
|
86
|
+
margin: 0.35em 0;
|
|
48
87
|
}
|
|
49
88
|
|
|
50
89
|
.markdown-editor .tiptap ul {
|
|
@@ -56,29 +95,138 @@
|
|
|
56
95
|
}
|
|
57
96
|
|
|
58
97
|
.markdown-editor .tiptap li {
|
|
59
|
-
margin: 0.
|
|
98
|
+
margin: 0.15em 0;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
.markdown-editor .tiptap li > p {
|
|
102
|
+
margin: 0;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/* Nested lists get a subtler marker. */
|
|
106
|
+
.markdown-editor .tiptap ul ul {
|
|
107
|
+
list-style: circle;
|
|
108
|
+
}
|
|
109
|
+
.markdown-editor .tiptap ul ul ul {
|
|
110
|
+
list-style: square;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/* Task list (GFM `- [ ] item`). StarterKit ships TaskList extension. */
|
|
114
|
+
.markdown-editor .tiptap ul[data-type="taskList"] {
|
|
115
|
+
list-style: none;
|
|
116
|
+
padding-left: 0.25em;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
.markdown-editor .tiptap ul[data-type="taskList"] li {
|
|
120
|
+
display: flex;
|
|
121
|
+
align-items: flex-start;
|
|
122
|
+
gap: 0.5em;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
.markdown-editor .tiptap ul[data-type="taskList"] li > label {
|
|
126
|
+
flex-shrink: 0;
|
|
127
|
+
margin-top: 0.3em;
|
|
128
|
+
user-select: none;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
.markdown-editor .tiptap ul[data-type="taskList"] li > div {
|
|
132
|
+
flex: 1;
|
|
133
|
+
min-width: 0;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
.markdown-editor .tiptap ul[data-type="taskList"] li[data-checked="true"] > div {
|
|
137
|
+
opacity: 0.55;
|
|
138
|
+
text-decoration: line-through;
|
|
60
139
|
}
|
|
61
140
|
|
|
141
|
+
/* Blockquote — Notion uses a chunky left bar without italic. */
|
|
62
142
|
.markdown-editor .tiptap blockquote {
|
|
63
143
|
border-left: 3px solid var(--color-border, var(--border));
|
|
64
|
-
padding
|
|
65
|
-
margin: 0.
|
|
66
|
-
|
|
144
|
+
padding: 0.2em 0 0.2em 1em;
|
|
145
|
+
margin: 0.6em 0;
|
|
146
|
+
color: color-mix(in oklab, var(--color-foreground, var(--foreground)) 80%, transparent);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
.markdown-editor .tiptap blockquote > :first-child {
|
|
150
|
+
margin-top: 0;
|
|
151
|
+
}
|
|
152
|
+
.markdown-editor .tiptap blockquote > :last-child {
|
|
153
|
+
margin-bottom: 0;
|
|
67
154
|
}
|
|
68
155
|
|
|
156
|
+
/* Divider — full-width thin rule with breathing room. */
|
|
69
157
|
.markdown-editor .tiptap hr {
|
|
70
158
|
border: none;
|
|
71
159
|
border-top: 1px solid var(--color-border, var(--border));
|
|
72
|
-
margin:
|
|
160
|
+
margin: 1.5em 0;
|
|
73
161
|
}
|
|
74
162
|
|
|
163
|
+
/* Inline code — monospace pill, restrained tint. */
|
|
75
164
|
.markdown-editor .tiptap code {
|
|
165
|
+
background: color-mix(in oklab, var(--color-muted, var(--muted)) 70%, transparent);
|
|
166
|
+
color: var(--color-foreground, var(--foreground));
|
|
167
|
+
padding: 0.15em 0.35em;
|
|
168
|
+
border-radius: 4px;
|
|
169
|
+
font-size: 0.875em;
|
|
170
|
+
font-family:
|
|
171
|
+
"SF Mono", ui-monospace, "JetBrains Mono", "Fira Code", Menlo, Consolas,
|
|
172
|
+
monospace;
|
|
173
|
+
border: 1px solid color-mix(in oklab, var(--color-border, var(--border)) 60%, transparent);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/* Code block — darker surface so it stands apart from inline code. */
|
|
177
|
+
.markdown-editor .tiptap pre {
|
|
76
178
|
background: var(--color-muted, var(--muted));
|
|
77
|
-
color: var(--color-
|
|
78
|
-
padding: 0.
|
|
79
|
-
border-radius:
|
|
80
|
-
|
|
81
|
-
font-family:
|
|
179
|
+
color: var(--color-foreground, var(--foreground));
|
|
180
|
+
padding: 0.9em 1em;
|
|
181
|
+
border-radius: 6px;
|
|
182
|
+
margin: 0.75em 0;
|
|
183
|
+
font-family:
|
|
184
|
+
"SF Mono", ui-monospace, "JetBrains Mono", "Fira Code", Menlo, Consolas,
|
|
185
|
+
monospace;
|
|
186
|
+
font-size: 0.875em;
|
|
187
|
+
line-height: 1.55;
|
|
188
|
+
overflow-x: auto;
|
|
189
|
+
border: 1px solid color-mix(in oklab, var(--color-border, var(--border)) 50%, transparent);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
.markdown-editor .tiptap pre code {
|
|
193
|
+
background: transparent;
|
|
194
|
+
border: none;
|
|
195
|
+
padding: 0;
|
|
196
|
+
border-radius: 0;
|
|
197
|
+
font-size: inherit;
|
|
198
|
+
color: inherit;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
/* Tables — borderless, subtle row separators (Notion-flavour). */
|
|
202
|
+
.markdown-editor .tiptap table {
|
|
203
|
+
border-collapse: collapse;
|
|
204
|
+
width: 100%;
|
|
205
|
+
margin: 0.75em 0;
|
|
206
|
+
font-size: 0.95em;
|
|
207
|
+
table-layout: fixed;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
.markdown-editor .tiptap th,
|
|
211
|
+
.markdown-editor .tiptap td {
|
|
212
|
+
border: 1px solid var(--color-border, var(--border));
|
|
213
|
+
padding: 0.45em 0.75em;
|
|
214
|
+
vertical-align: top;
|
|
215
|
+
text-align: left;
|
|
216
|
+
min-width: 4ch;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
.markdown-editor .tiptap th {
|
|
220
|
+
background: color-mix(in oklab, var(--color-muted, var(--muted)) 60%, transparent);
|
|
221
|
+
font-weight: 600;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
/* Images — rounded, never overflow. */
|
|
225
|
+
.markdown-editor .tiptap img {
|
|
226
|
+
max-width: 100%;
|
|
227
|
+
height: auto;
|
|
228
|
+
border-radius: 6px;
|
|
229
|
+
margin: 0.5em 0;
|
|
82
230
|
}
|
|
83
231
|
|
|
84
232
|
.markdown-editor .tiptap strong {
|
|
@@ -93,6 +241,11 @@
|
|
|
93
241
|
text-decoration: line-through;
|
|
94
242
|
}
|
|
95
243
|
|
|
244
|
+
/* Selection highlight — semantic ring colour, not browser-blue. */
|
|
245
|
+
.markdown-editor .tiptap ::selection {
|
|
246
|
+
background: color-mix(in oklab, var(--color-primary, var(--primary)) 25%, transparent);
|
|
247
|
+
}
|
|
248
|
+
|
|
96
249
|
.markdown-editor .tiptap p.is-editor-empty:first-child::before {
|
|
97
250
|
content: attr(data-placeholder);
|
|
98
251
|
float: left;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { Extension } from '@tiptap/core';
|
|
2
|
+
import { TextSelection } from '@tiptap/pm/state';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Notion-style smart Cmd/Ctrl+A:
|
|
6
|
+
* 1st press → select the current text block
|
|
7
|
+
* 2nd press → select the whole document
|
|
8
|
+
*
|
|
9
|
+
* Default editor behaviour selects everything on first press, which is
|
|
10
|
+
* jarring in long documents where the user almost always wants "select
|
|
11
|
+
* paragraph" first. Ported from Novel (`extensions/custom-keymap.ts`).
|
|
12
|
+
*/
|
|
13
|
+
export const CustomKeymap = Extension.create({
|
|
14
|
+
name: 'notion-custom-keymap',
|
|
15
|
+
|
|
16
|
+
addKeyboardShortcuts() {
|
|
17
|
+
return {
|
|
18
|
+
'Mod-a': ({ editor }) => {
|
|
19
|
+
const { selection, doc } = editor.state;
|
|
20
|
+
const { $from, $to, from, to } = selection;
|
|
21
|
+
|
|
22
|
+
// Resolve the boundaries of the current text block. `start`/`end`
|
|
23
|
+
// on $from/$to give the inclusive content range of the parent
|
|
24
|
+
// textblock (a paragraph, heading, code block, list item body).
|
|
25
|
+
const blockStart = $from.start();
|
|
26
|
+
const blockEnd = $to.end();
|
|
27
|
+
|
|
28
|
+
const isWholeBlockSelected = from === blockStart && to === blockEnd;
|
|
29
|
+
// If the block is already fully selected → escalate to whole doc.
|
|
30
|
+
if (isWholeBlockSelected) {
|
|
31
|
+
editor
|
|
32
|
+
.chain()
|
|
33
|
+
.setTextSelection({ from: 0, to: doc.content.size })
|
|
34
|
+
.run();
|
|
35
|
+
return true;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
editor
|
|
39
|
+
.chain()
|
|
40
|
+
.setTextSelection(
|
|
41
|
+
TextSelection.create(editor.state.doc, blockStart, blockEnd),
|
|
42
|
+
)
|
|
43
|
+
.run();
|
|
44
|
+
return true;
|
|
45
|
+
},
|
|
46
|
+
};
|
|
47
|
+
},
|
|
48
|
+
});
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Cmd/Ctrl+K link prompt. Modeless dialog with a single URL input and
|
|
5
|
+
* Save/Remove/Cancel actions. Mounted as a side-channel above the editor
|
|
6
|
+
* so it survives selection collapse — without this, focusing the input
|
|
7
|
+
* would unset the selection and TipTap couldn't apply the mark.
|
|
8
|
+
*
|
|
9
|
+
* We snapshot `from`/`to` of the selection on open and re-apply it
|
|
10
|
+
* before running the link command. ProseMirror tolerates this because
|
|
11
|
+
* the dialog is rendered in a portal (Radix's `DialogPortal`) and the
|
|
12
|
+
* editor's view does not lose its `state` — only its DOM focus.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import { useEffect, useRef, useState } from 'react';
|
|
16
|
+
import {
|
|
17
|
+
Button,
|
|
18
|
+
Dialog,
|
|
19
|
+
DialogContent,
|
|
20
|
+
DialogFooter,
|
|
21
|
+
DialogHeader,
|
|
22
|
+
DialogTitle,
|
|
23
|
+
Input,
|
|
24
|
+
} from '@djangocfg/ui-core';
|
|
25
|
+
import type { Editor } from '@tiptap/react';
|
|
26
|
+
|
|
27
|
+
/** Reject `javascript:`, `data:`, `vbscript:` schemes. Plain anchors,
|
|
28
|
+
* http(s), mailto, tel, and relative paths pass. Same allow-list TipTap
|
|
29
|
+
* uses internally when `validate` is omitted, made explicit here. */
|
|
30
|
+
function isSafeHref(value: string): boolean {
|
|
31
|
+
const trimmed = value.trim();
|
|
32
|
+
const colon = trimmed.indexOf(':');
|
|
33
|
+
if (colon === -1) return true; // relative or hash link
|
|
34
|
+
const scheme = trimmed.slice(0, colon).toLowerCase();
|
|
35
|
+
return ['http', 'https', 'mailto', 'tel', 'sms', 'ftp'].includes(scheme);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
interface LinkDialogProps {
|
|
39
|
+
editor: Editor;
|
|
40
|
+
open: boolean;
|
|
41
|
+
onOpenChange: (open: boolean) => void;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export function LinkDialog({ editor, open, onOpenChange }: LinkDialogProps) {
|
|
45
|
+
const [href, setHref] = useState('');
|
|
46
|
+
const inputRef = useRef<HTMLInputElement | null>(null);
|
|
47
|
+
// Remember the selection range when the dialog opened — focusing the
|
|
48
|
+
// input collapses selection in the editor; we re-apply the snapshot
|
|
49
|
+
// before running the link command.
|
|
50
|
+
const rangeRef = useRef<{ from: number; to: number } | null>(null);
|
|
51
|
+
|
|
52
|
+
useEffect(() => {
|
|
53
|
+
if (!open) return;
|
|
54
|
+
const { from, to } = editor.state.selection;
|
|
55
|
+
rangeRef.current = { from, to };
|
|
56
|
+
const existing = editor.getAttributes('link').href as string | undefined;
|
|
57
|
+
setHref(existing ?? '');
|
|
58
|
+
// Focus is handled by Radix's `onOpenAutoFocus` below — that fires
|
|
59
|
+
// after the dialog DOM is in the document and avoids racing the
|
|
60
|
+
// built-in focus trap.
|
|
61
|
+
}, [open, editor]);
|
|
62
|
+
|
|
63
|
+
const applyLink = (value: string) => {
|
|
64
|
+
const range = rangeRef.current;
|
|
65
|
+
if (range) {
|
|
66
|
+
editor.chain().focus().setTextSelection(range).run();
|
|
67
|
+
}
|
|
68
|
+
const trimmed = value.trim();
|
|
69
|
+
if (!trimmed) {
|
|
70
|
+
editor.chain().focus().unsetLink().run();
|
|
71
|
+
} else if (isSafeHref(trimmed)) {
|
|
72
|
+
editor.chain().focus().extendMarkRange('link').setLink({ href: trimmed }).run();
|
|
73
|
+
}
|
|
74
|
+
onOpenChange(false);
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
const removeLink = () => {
|
|
78
|
+
const range = rangeRef.current;
|
|
79
|
+
if (range) {
|
|
80
|
+
editor.chain().focus().setTextSelection(range).run();
|
|
81
|
+
}
|
|
82
|
+
editor.chain().focus().unsetLink().run();
|
|
83
|
+
onOpenChange(false);
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
return (
|
|
87
|
+
<Dialog open={open} onOpenChange={onOpenChange}>
|
|
88
|
+
<DialogContent
|
|
89
|
+
className="sm:max-w-md"
|
|
90
|
+
// Override Radix's default auto-focus (first focusable). We want
|
|
91
|
+
// the URL input focused + selected — combining `preventDefault`
|
|
92
|
+
// here with a direct `focus()` call sequences cleanly with the
|
|
93
|
+
// dialog mount; the earlier queueMicrotask version raced Radix.
|
|
94
|
+
onOpenAutoFocus={(e) => {
|
|
95
|
+
e.preventDefault();
|
|
96
|
+
inputRef.current?.focus();
|
|
97
|
+
inputRef.current?.select();
|
|
98
|
+
}}
|
|
99
|
+
>
|
|
100
|
+
<DialogHeader>
|
|
101
|
+
<DialogTitle>Link</DialogTitle>
|
|
102
|
+
</DialogHeader>
|
|
103
|
+
<form
|
|
104
|
+
onSubmit={(e) => {
|
|
105
|
+
e.preventDefault();
|
|
106
|
+
applyLink(href);
|
|
107
|
+
}}
|
|
108
|
+
>
|
|
109
|
+
<Input
|
|
110
|
+
ref={inputRef}
|
|
111
|
+
type="url"
|
|
112
|
+
placeholder="https://example.com"
|
|
113
|
+
value={href}
|
|
114
|
+
onChange={(e) => setHref(e.target.value)}
|
|
115
|
+
autoComplete="off"
|
|
116
|
+
spellCheck={false}
|
|
117
|
+
/>
|
|
118
|
+
<DialogFooter className="mt-4 gap-2 sm:gap-2">
|
|
119
|
+
{editor.isActive('link') ? (
|
|
120
|
+
<Button type="button" variant="outline" onClick={removeLink}>
|
|
121
|
+
Remove
|
|
122
|
+
</Button>
|
|
123
|
+
) : null}
|
|
124
|
+
<Button type="button" variant="ghost" onClick={() => onOpenChange(false)}>
|
|
125
|
+
Cancel
|
|
126
|
+
</Button>
|
|
127
|
+
<Button type="submit">Save</Button>
|
|
128
|
+
</DialogFooter>
|
|
129
|
+
</form>
|
|
130
|
+
</DialogContent>
|
|
131
|
+
</Dialog>
|
|
132
|
+
);
|
|
133
|
+
}
|