@bendyline/squisq-editor-react 1.4.0 → 1.5.1
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/DocumentSettingsDialog.d.ts +26 -0
- package/dist/DocumentSettingsDialog.d.ts.map +1 -0
- package/dist/DocumentSettingsDialog.js +115 -0
- package/dist/DocumentSettingsDialog.js.map +1 -0
- package/dist/EditorContext.d.ts +248 -4
- package/dist/EditorContext.d.ts.map +1 -1
- package/dist/EditorContext.js +248 -10
- package/dist/EditorContext.js.map +1 -1
- package/dist/EditorShell.d.ts +173 -4
- package/dist/EditorShell.d.ts.map +1 -1
- package/dist/EditorShell.js +110 -10
- package/dist/EditorShell.js.map +1 -1
- package/dist/EmojiPicker.d.ts +50 -0
- package/dist/EmojiPicker.d.ts.map +1 -0
- package/dist/EmojiPicker.js +182 -0
- package/dist/EmojiPicker.js.map +1 -0
- package/dist/ImageEditor.d.ts +68 -0
- package/dist/ImageEditor.d.ts.map +1 -0
- package/dist/ImageEditor.js +166 -0
- package/dist/ImageEditor.js.map +1 -0
- package/dist/ImageNodeView.d.ts +13 -1
- package/dist/ImageNodeView.d.ts.map +1 -1
- package/dist/ImageNodeView.js +172 -19
- package/dist/ImageNodeView.js.map +1 -1
- package/dist/ImageViewer.d.ts +26 -0
- package/dist/ImageViewer.d.ts.map +1 -0
- package/dist/ImageViewer.js +119 -0
- package/dist/ImageViewer.js.map +1 -0
- package/dist/InlineIcon.d.ts +17 -0
- package/dist/InlineIcon.d.ts.map +1 -0
- package/dist/InlineIcon.js +72 -0
- package/dist/InlineIcon.js.map +1 -0
- package/dist/InlinePreviewGutter.d.ts +52 -0
- package/dist/InlinePreviewGutter.d.ts.map +1 -0
- package/dist/InlinePreviewGutter.js +397 -0
- package/dist/InlinePreviewGutter.js.map +1 -0
- package/dist/LinkDialog.d.ts +43 -0
- package/dist/LinkDialog.d.ts.map +1 -0
- package/dist/LinkDialog.js +102 -0
- package/dist/LinkDialog.js.map +1 -0
- package/dist/MentionExtension.js +10 -7
- package/dist/MentionExtension.js.map +1 -1
- package/dist/OutlinePanel.d.ts +17 -0
- package/dist/OutlinePanel.d.ts.map +1 -0
- package/dist/OutlinePanel.js +167 -0
- package/dist/OutlinePanel.js.map +1 -0
- package/dist/PlainHtmlPreview.d.ts +50 -0
- package/dist/PlainHtmlPreview.d.ts.map +1 -0
- package/dist/PlainHtmlPreview.js +155 -0
- package/dist/PlainHtmlPreview.js.map +1 -0
- package/dist/PreviewControls.d.ts +15 -1
- package/dist/PreviewControls.d.ts.map +1 -1
- package/dist/PreviewControls.js +75 -18
- package/dist/PreviewControls.js.map +1 -1
- package/dist/PreviewPanel.d.ts +11 -10
- package/dist/PreviewPanel.d.ts.map +1 -1
- package/dist/PreviewPanel.js +20 -17
- package/dist/PreviewPanel.js.map +1 -1
- package/dist/RawEditor.d.ts.map +1 -1
- package/dist/RawEditor.js +198 -4
- package/dist/RawEditor.js.map +1 -1
- package/dist/RecorderEntry.d.ts +24 -0
- package/dist/RecorderEntry.d.ts.map +1 -0
- package/dist/RecorderEntry.js +139 -0
- package/dist/RecorderEntry.js.map +1 -0
- package/dist/TemplateAnnotation.d.ts.map +1 -1
- package/dist/TemplateAnnotation.js +32 -6
- package/dist/TemplateAnnotation.js.map +1 -1
- package/dist/TemplatePicker.d.ts +53 -0
- package/dist/TemplatePicker.d.ts.map +1 -0
- package/dist/TemplatePicker.js +388 -0
- package/dist/TemplatePicker.js.map +1 -0
- package/dist/ThemeCustomizerPanel.d.ts +32 -0
- package/dist/ThemeCustomizerPanel.d.ts.map +1 -0
- package/dist/ThemeCustomizerPanel.js +256 -0
- package/dist/ThemeCustomizerPanel.js.map +1 -0
- package/dist/ThemePicker.d.ts +33 -0
- package/dist/ThemePicker.d.ts.map +1 -0
- package/dist/ThemePicker.js +148 -0
- package/dist/ThemePicker.js.map +1 -0
- package/dist/Toolbar.d.ts.map +1 -1
- package/dist/Toolbar.js +508 -33
- package/dist/Toolbar.js.map +1 -1
- package/dist/VersionHistoryPanel.d.ts +14 -0
- package/dist/VersionHistoryPanel.d.ts.map +1 -0
- package/dist/VersionHistoryPanel.js +147 -0
- package/dist/VersionHistoryPanel.js.map +1 -0
- package/dist/ViewMenuPanel.d.ts +13 -0
- package/dist/ViewMenuPanel.d.ts.map +1 -0
- package/dist/ViewMenuPanel.js +58 -0
- package/dist/ViewMenuPanel.js.map +1 -0
- package/dist/WysiwygEditor.d.ts.map +1 -1
- package/dist/WysiwygEditor.js +198 -9
- package/dist/WysiwygEditor.js.map +1 -1
- package/dist/__tests__/detectMarkdown.test.js +0 -14
- package/dist/__tests__/detectMarkdown.test.js.map +1 -1
- package/dist/__tests__/documentSettingsDialog.test.d.ts +2 -0
- package/dist/__tests__/documentSettingsDialog.test.d.ts.map +1 -0
- package/dist/__tests__/documentSettingsDialog.test.js +132 -0
- package/dist/__tests__/documentSettingsDialog.test.js.map +1 -0
- package/dist/__tests__/emojiPicker.test.d.ts +2 -0
- package/dist/__tests__/emojiPicker.test.d.ts.map +1 -0
- package/dist/__tests__/emojiPicker.test.js +111 -0
- package/dist/__tests__/emojiPicker.test.js.map +1 -0
- package/dist/__tests__/fileKind.test.js +13 -0
- package/dist/__tests__/fileKind.test.js.map +1 -1
- package/dist/__tests__/imageEditAffordance.test.d.ts +2 -0
- package/dist/__tests__/imageEditAffordance.test.d.ts.map +1 -0
- package/dist/__tests__/imageEditAffordance.test.js +188 -0
- package/dist/__tests__/imageEditAffordance.test.js.map +1 -0
- package/dist/__tests__/imageEditorShell.test.d.ts +2 -0
- package/dist/__tests__/imageEditorShell.test.d.ts.map +1 -0
- package/dist/__tests__/imageEditorShell.test.js +52 -0
- package/dist/__tests__/imageEditorShell.test.js.map +1 -0
- package/dist/__tests__/imageEditorState.test.d.ts +3 -0
- package/dist/__tests__/imageEditorState.test.d.ts.map +1 -0
- package/dist/__tests__/imageEditorState.test.js +148 -0
- package/dist/__tests__/imageEditorState.test.js.map +1 -0
- package/dist/__tests__/inlinePreviewGutter.test.d.ts +2 -0
- package/dist/__tests__/inlinePreviewGutter.test.d.ts.map +1 -0
- package/dist/__tests__/inlinePreviewGutter.test.js +51 -0
- package/dist/__tests__/inlinePreviewGutter.test.js.map +1 -0
- package/dist/__tests__/inlinePreviewGutterAllBlocks.test.d.ts +2 -0
- package/dist/__tests__/inlinePreviewGutterAllBlocks.test.d.ts.map +1 -0
- package/dist/__tests__/inlinePreviewGutterAllBlocks.test.js +63 -0
- package/dist/__tests__/inlinePreviewGutterAllBlocks.test.js.map +1 -0
- package/dist/__tests__/jsonEditor.test.d.ts +2 -0
- package/dist/__tests__/jsonEditor.test.d.ts.map +1 -0
- package/dist/__tests__/jsonEditor.test.js +134 -0
- package/dist/__tests__/jsonEditor.test.js.map +1 -0
- package/dist/__tests__/layersPanel.test.d.ts +2 -0
- package/dist/__tests__/layersPanel.test.d.ts.map +1 -0
- package/dist/__tests__/layersPanel.test.js +84 -0
- package/dist/__tests__/layersPanel.test.js.map +1 -0
- package/dist/__tests__/linkDialogDocPicker.test.d.ts +7 -0
- package/dist/__tests__/linkDialogDocPicker.test.d.ts.map +1 -0
- package/dist/__tests__/linkDialogDocPicker.test.js +75 -0
- package/dist/__tests__/linkDialogDocPicker.test.js.map +1 -0
- package/dist/__tests__/outlinePanel.test.d.ts +2 -0
- package/dist/__tests__/outlinePanel.test.d.ts.map +1 -0
- package/dist/__tests__/outlinePanel.test.js +68 -0
- package/dist/__tests__/outlinePanel.test.js.map +1 -0
- package/dist/__tests__/plainHtmlPreview.test.d.ts +2 -0
- package/dist/__tests__/plainHtmlPreview.test.d.ts.map +1 -0
- package/dist/__tests__/plainHtmlPreview.test.js +87 -0
- package/dist/__tests__/plainHtmlPreview.test.js.map +1 -0
- package/dist/__tests__/propertiesPanel.test.d.ts +2 -0
- package/dist/__tests__/propertiesPanel.test.d.ts.map +1 -0
- package/dist/__tests__/propertiesPanel.test.js +64 -0
- package/dist/__tests__/propertiesPanel.test.js.map +1 -0
- package/dist/__tests__/recorderFormats.test.d.ts +2 -0
- package/dist/__tests__/recorderFormats.test.d.ts.map +1 -0
- package/dist/__tests__/recorderFormats.test.js +121 -0
- package/dist/__tests__/recorderFormats.test.js.map +1 -0
- package/dist/__tests__/recorderTimingJson.test.d.ts +2 -0
- package/dist/__tests__/recorderTimingJson.test.d.ts.map +1 -0
- package/dist/__tests__/recorderTimingJson.test.js +37 -0
- package/dist/__tests__/recorderTimingJson.test.js.map +1 -0
- package/dist/__tests__/templateAnnotationRoundTrip.test.d.ts +2 -0
- package/dist/__tests__/templateAnnotationRoundTrip.test.d.ts.map +1 -0
- package/dist/__tests__/templateAnnotationRoundTrip.test.js +31 -0
- package/dist/__tests__/templateAnnotationRoundTrip.test.js.map +1 -0
- package/dist/__tests__/tiptapBridge.test.js +13 -0
- package/dist/__tests__/tiptapBridge.test.js.map +1 -1
- package/dist/__tests__/useImageEditor.test.d.ts +2 -0
- package/dist/__tests__/useImageEditor.test.d.ts.map +1 -0
- package/dist/__tests__/useImageEditor.test.js +131 -0
- package/dist/__tests__/useImageEditor.test.js.map +1 -0
- package/dist/__tests__/useMediaRecorder.test.d.ts +2 -0
- package/dist/__tests__/useMediaRecorder.test.d.ts.map +1 -0
- package/dist/__tests__/useMediaRecorder.test.js +153 -0
- package/dist/__tests__/useMediaRecorder.test.js.map +1 -0
- package/dist/__tests__/versionHistory.test.d.ts +2 -0
- package/dist/__tests__/versionHistory.test.d.ts.map +1 -0
- package/dist/__tests__/versionHistory.test.js +124 -0
- package/dist/__tests__/versionHistory.test.js.map +1 -0
- package/dist/blockSlice.d.ts +24 -0
- package/dist/blockSlice.d.ts.map +1 -0
- package/dist/blockSlice.js +63 -0
- package/dist/blockSlice.js.map +1 -0
- package/dist/buildPreviewDoc.d.ts.map +1 -1
- package/dist/buildPreviewDoc.js +52 -2
- package/dist/buildPreviewDoc.js.map +1 -1
- package/dist/emojiData.d.ts +81 -0
- package/dist/emojiData.d.ts.map +1 -0
- package/dist/emojiData.js +1283 -0
- package/dist/emojiData.js.map +1 -0
- package/dist/fileKind.d.ts +6 -2
- package/dist/fileKind.d.ts.map +1 -1
- package/dist/fileKind.js +25 -4
- package/dist/fileKind.js.map +1 -1
- package/dist/hooks/useFileDrop.d.ts.map +1 -1
- package/dist/hooks/useFileDrop.js +40 -4
- package/dist/hooks/useFileDrop.js.map +1 -1
- package/dist/imageEditor/CanvasSurface.d.ts +31 -0
- package/dist/imageEditor/CanvasSurface.d.ts.map +1 -0
- package/dist/imageEditor/CanvasSurface.js +264 -0
- package/dist/imageEditor/CanvasSurface.js.map +1 -0
- package/dist/imageEditor/ImageVersionHistoryDropdown.d.ts +39 -0
- package/dist/imageEditor/ImageVersionHistoryDropdown.d.ts.map +1 -0
- package/dist/imageEditor/ImageVersionHistoryDropdown.js +283 -0
- package/dist/imageEditor/ImageVersionHistoryDropdown.js.map +1 -0
- package/dist/imageEditor/LayersPanel.d.ts +14 -0
- package/dist/imageEditor/LayersPanel.d.ts.map +1 -0
- package/dist/imageEditor/LayersPanel.js +43 -0
- package/dist/imageEditor/LayersPanel.js.map +1 -0
- package/dist/imageEditor/PropertiesPanel.d.ts +14 -0
- package/dist/imageEditor/PropertiesPanel.d.ts.map +1 -0
- package/dist/imageEditor/PropertiesPanel.js +97 -0
- package/dist/imageEditor/PropertiesPanel.js.map +1 -0
- package/dist/imageEditor/Toolbar.d.ts +30 -0
- package/dist/imageEditor/Toolbar.d.ts.map +1 -0
- package/dist/imageEditor/Toolbar.js +108 -0
- package/dist/imageEditor/Toolbar.js.map +1 -0
- package/dist/imageEditor/icons.d.ts +24 -0
- package/dist/imageEditor/icons.d.ts.map +1 -0
- package/dist/imageEditor/icons.js +45 -0
- package/dist/imageEditor/icons.js.map +1 -0
- package/dist/imageEditor/layers/EditorImageLayer.d.ts +16 -0
- package/dist/imageEditor/layers/EditorImageLayer.d.ts.map +1 -0
- package/dist/imageEditor/layers/EditorImageLayer.js +37 -0
- package/dist/imageEditor/layers/EditorImageLayer.js.map +1 -0
- package/dist/imageEditor/layers/EditorShapeLayer.d.ts +15 -0
- package/dist/imageEditor/layers/EditorShapeLayer.d.ts.map +1 -0
- package/dist/imageEditor/layers/EditorShapeLayer.js +20 -0
- package/dist/imageEditor/layers/EditorShapeLayer.js.map +1 -0
- package/dist/imageEditor/layers/EditorTextLayer.d.ts +18 -0
- package/dist/imageEditor/layers/EditorTextLayer.d.ts.map +1 -0
- package/dist/imageEditor/layers/EditorTextLayer.js +13 -0
- package/dist/imageEditor/layers/EditorTextLayer.js.map +1 -0
- package/dist/imageEditor/layers/SelectionHandles.d.ts +17 -0
- package/dist/imageEditor/layers/SelectionHandles.d.ts.map +1 -0
- package/dist/imageEditor/layers/SelectionHandles.js +19 -0
- package/dist/imageEditor/layers/SelectionHandles.js.map +1 -0
- package/dist/imageEditor/state.d.ts +76 -0
- package/dist/imageEditor/state.d.ts.map +1 -0
- package/dist/imageEditor/state.js +87 -0
- package/dist/imageEditor/state.js.map +1 -0
- package/dist/imageEditor/useImageEditor.d.ts +53 -0
- package/dist/imageEditor/useImageEditor.d.ts.map +1 -0
- package/dist/imageEditor/useImageEditor.js +244 -0
- package/dist/imageEditor/useImageEditor.js.map +1 -0
- package/dist/imageEditor/useImageEditorTokens.d.ts +16 -0
- package/dist/imageEditor/useImageEditorTokens.d.ts.map +1 -0
- package/dist/imageEditor/useImageEditorTokens.js +45 -0
- package/dist/imageEditor/useImageEditorTokens.js.map +1 -0
- package/dist/index.d.ts +48 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +36 -0
- package/dist/index.js.map +1 -1
- package/dist/jsonEditor/EmbeddedRichTextField.d.ts +15 -0
- package/dist/jsonEditor/EmbeddedRichTextField.d.ts.map +1 -0
- package/dist/jsonEditor/EmbeddedRichTextField.js +74 -0
- package/dist/jsonEditor/EmbeddedRichTextField.js.map +1 -0
- package/dist/jsonEditor/JsonEditor.d.ts +36 -0
- package/dist/jsonEditor/JsonEditor.d.ts.map +1 -0
- package/dist/jsonEditor/JsonEditor.js +15 -0
- package/dist/jsonEditor/JsonEditor.js.map +1 -0
- package/dist/jsonEditor/JsonEditorContext.d.ts +28 -0
- package/dist/jsonEditor/JsonEditorContext.d.ts.map +1 -0
- package/dist/jsonEditor/JsonEditorContext.js +41 -0
- package/dist/jsonEditor/JsonEditorContext.js.map +1 -0
- package/dist/jsonEditor/RenderNode.d.ts +16 -0
- package/dist/jsonEditor/RenderNode.d.ts.map +1 -0
- package/dist/jsonEditor/RenderNode.js +32 -0
- package/dist/jsonEditor/RenderNode.js.map +1 -0
- package/dist/jsonEditor/editors.d.ts +36 -0
- package/dist/jsonEditor/editors.d.ts.map +1 -0
- package/dist/jsonEditor/editors.js +347 -0
- package/dist/jsonEditor/editors.js.map +1 -0
- package/dist/jsonEditor/index.d.ts +3 -0
- package/dist/jsonEditor/index.d.ts.map +1 -0
- package/dist/jsonEditor/index.js +2 -0
- package/dist/jsonEditor/index.js.map +1 -0
- package/dist/jsonEditor/useJsonEditorTokens.d.ts +13 -0
- package/dist/jsonEditor/useJsonEditorTokens.d.ts.map +1 -0
- package/dist/jsonEditor/useJsonEditorTokens.js +38 -0
- package/dist/jsonEditor/useJsonEditorTokens.js.map +1 -0
- package/dist/recorder/RecorderButton.d.ts +31 -0
- package/dist/recorder/RecorderButton.d.ts.map +1 -0
- package/dist/recorder/RecorderButton.js +24 -0
- package/dist/recorder/RecorderButton.js.map +1 -0
- package/dist/recorder/RecorderModal.d.ts +59 -0
- package/dist/recorder/RecorderModal.d.ts.map +1 -0
- package/dist/recorder/RecorderModal.js +333 -0
- package/dist/recorder/RecorderModal.js.map +1 -0
- package/dist/recorder/RecorderPanel.d.ts +25 -0
- package/dist/recorder/RecorderPanel.d.ts.map +1 -0
- package/dist/recorder/RecorderPanel.js +30 -0
- package/dist/recorder/RecorderPanel.js.map +1 -0
- package/dist/recorder/formats.d.ts +51 -0
- package/dist/recorder/formats.d.ts.map +1 -0
- package/dist/recorder/formats.js +144 -0
- package/dist/recorder/formats.js.map +1 -0
- package/dist/recorder/hooks/useMediaRecorder.d.ts +90 -0
- package/dist/recorder/hooks/useMediaRecorder.d.ts.map +1 -0
- package/dist/recorder/hooks/useMediaRecorder.js +277 -0
- package/dist/recorder/hooks/useMediaRecorder.js.map +1 -0
- package/dist/recorder/hooks/useStreamPreview.d.ts +22 -0
- package/dist/recorder/hooks/useStreamPreview.d.ts.map +1 -0
- package/dist/recorder/hooks/useStreamPreview.js +44 -0
- package/dist/recorder/hooks/useStreamPreview.js.map +1 -0
- package/dist/recorder/sources/cameraStream.d.ts +22 -0
- package/dist/recorder/sources/cameraStream.d.ts.map +1 -0
- package/dist/recorder/sources/cameraStream.js +24 -0
- package/dist/recorder/sources/cameraStream.js.map +1 -0
- package/dist/recorder/sources/micStream.d.ts +15 -0
- package/dist/recorder/sources/micStream.d.ts.map +1 -0
- package/dist/recorder/sources/micStream.js +24 -0
- package/dist/recorder/sources/micStream.js.map +1 -0
- package/dist/recorder/sources/screenStream.d.ts +53 -0
- package/dist/recorder/sources/screenStream.d.ts.map +1 -0
- package/dist/recorder/sources/screenStream.js +114 -0
- package/dist/recorder/sources/screenStream.js.map +1 -0
- package/dist/recorder/timingJson.d.ts +51 -0
- package/dist/recorder/timingJson.d.ts.map +1 -0
- package/dist/recorder/timingJson.js +42 -0
- package/dist/recorder/timingJson.js.map +1 -0
- package/dist/tiptap/TiptapAudio.d.ts +26 -0
- package/dist/tiptap/TiptapAudio.d.ts.map +1 -0
- package/dist/tiptap/TiptapAudio.js +58 -0
- package/dist/tiptap/TiptapAudio.js.map +1 -0
- package/dist/tiptap/TiptapVideo.d.ts +30 -0
- package/dist/tiptap/TiptapVideo.d.ts.map +1 -0
- package/dist/tiptap/TiptapVideo.js +66 -0
- package/dist/tiptap/TiptapVideo.js.map +1 -0
- package/dist/tiptap/useResolvedMediaSrc.d.ts +2 -0
- package/dist/tiptap/useResolvedMediaSrc.d.ts.map +1 -0
- package/dist/tiptap/useResolvedMediaSrc.js +42 -0
- package/dist/tiptap/useResolvedMediaSrc.js.map +1 -0
- package/dist/tiptapBridge.d.ts.map +1 -1
- package/dist/tiptapBridge.js +171 -14
- package/dist/tiptapBridge.js.map +1 -1
- package/dist/useHeadingLayout.d.ts +54 -0
- package/dist/useHeadingLayout.d.ts.map +1 -0
- package/dist/useHeadingLayout.js +260 -0
- package/dist/useHeadingLayout.js.map +1 -0
- package/dist/utils/collectInlineFontAwesomeCss.d.ts +21 -0
- package/dist/utils/collectInlineFontAwesomeCss.d.ts.map +1 -0
- package/dist/utils/collectInlineFontAwesomeCss.js +68 -0
- package/dist/utils/collectInlineFontAwesomeCss.js.map +1 -0
- package/dist/utils/dropUtils.d.ts +21 -2
- package/dist/utils/dropUtils.d.ts.map +1 -1
- package/dist/utils/dropUtils.js +43 -4
- package/dist/utils/dropUtils.js.map +1 -1
- package/dist/utils/normalizeMalformedAssetUrl.d.ts +15 -0
- package/dist/utils/normalizeMalformedAssetUrl.d.ts.map +1 -0
- package/dist/utils/normalizeMalformedAssetUrl.js +27 -0
- package/dist/utils/normalizeMalformedAssetUrl.js.map +1 -0
- package/package.json +8 -5
- package/src/DocumentSettingsDialog.tsx +266 -0
- package/src/EditorContext.tsx +534 -10
- package/src/EditorShell.tsx +571 -55
- package/src/EmojiPicker.tsx +332 -0
- package/src/ImageEditor.tsx +327 -0
- package/src/ImageNodeView.tsx +222 -21
- package/src/ImageViewer.tsx +221 -0
- package/src/InlineIcon.ts +84 -0
- package/src/InlinePreviewGutter.tsx +582 -0
- package/src/LinkDialog.tsx +276 -0
- package/src/MentionExtension.tsx +10 -7
- package/src/OutlinePanel.tsx +295 -0
- package/src/PlainHtmlPreview.tsx +211 -0
- package/src/PreviewControls.tsx +130 -24
- package/src/PreviewPanel.tsx +38 -21
- package/src/RawEditor.tsx +215 -4
- package/src/RecorderEntry.tsx +164 -0
- package/src/TemplateAnnotation.ts +32 -6
- package/src/TemplatePicker.tsx +818 -0
- package/src/ThemeCustomizerPanel.tsx +595 -0
- package/src/ThemePicker.tsx +319 -0
- package/src/Toolbar.tsx +708 -111
- package/src/VersionHistoryPanel.tsx +329 -0
- package/src/ViewMenuPanel.tsx +188 -0
- package/src/WysiwygEditor.tsx +229 -9
- package/src/__tests__/detectMarkdown.test.ts +0 -15
- package/src/__tests__/documentSettingsDialog.test.tsx +147 -0
- package/src/__tests__/emojiPicker.test.tsx +133 -0
- package/src/__tests__/fileKind.test.ts +16 -0
- package/src/__tests__/imageEditAffordance.test.tsx +268 -0
- package/src/__tests__/imageEditorShell.test.tsx +57 -0
- package/src/__tests__/imageEditorState.test.ts +171 -0
- package/src/__tests__/inlinePreviewGutter.test.tsx +62 -0
- package/src/__tests__/inlinePreviewGutterAllBlocks.test.tsx +103 -0
- package/src/__tests__/jsonEditor.test.tsx +168 -0
- package/src/__tests__/layersPanel.test.tsx +97 -0
- package/src/__tests__/linkDialogDocPicker.test.tsx +137 -0
- package/src/__tests__/outlinePanel.test.tsx +79 -0
- package/src/__tests__/plainHtmlPreview.test.tsx +107 -0
- package/src/__tests__/propertiesPanel.test.tsx +69 -0
- package/src/__tests__/recorderFormats.test.ts +146 -0
- package/src/__tests__/recorderTimingJson.test.ts +41 -0
- package/src/__tests__/templateAnnotationRoundTrip.test.ts +34 -0
- package/src/__tests__/tiptapBridge.test.ts +15 -0
- package/src/__tests__/useImageEditor.test.tsx +159 -0
- package/src/__tests__/useMediaRecorder.test.ts +186 -0
- package/src/__tests__/versionHistory.test.tsx +197 -0
- package/src/blockSlice.ts +75 -0
- package/src/buildPreviewDoc.ts +61 -6
- package/src/emojiData.ts +1337 -0
- package/src/fileKind.ts +30 -6
- package/src/hooks/useFileDrop.ts +40 -4
- package/src/imageEditor/CanvasSurface.tsx +402 -0
- package/src/imageEditor/ImageVersionHistoryDropdown.tsx +396 -0
- package/src/imageEditor/LayersPanel.tsx +143 -0
- package/src/imageEditor/PropertiesPanel.tsx +428 -0
- package/src/imageEditor/Toolbar.tsx +242 -0
- package/src/imageEditor/icons.tsx +144 -0
- package/src/imageEditor/image-editor.css +450 -0
- package/src/imageEditor/layers/EditorImageLayer.tsx +45 -0
- package/src/imageEditor/layers/EditorShapeLayer.tsx +62 -0
- package/src/imageEditor/layers/EditorTextLayer.tsx +45 -0
- package/src/imageEditor/layers/SelectionHandles.tsx +86 -0
- package/src/imageEditor/state.ts +153 -0
- package/src/imageEditor/useImageEditor.ts +328 -0
- package/src/imageEditor/useImageEditorTokens.ts +70 -0
- package/src/index.ts +82 -0
- package/src/jsonEditor/EmbeddedRichTextField.tsx +81 -0
- package/src/jsonEditor/JsonEditor.tsx +81 -0
- package/src/jsonEditor/JsonEditorContext.tsx +75 -0
- package/src/jsonEditor/RenderNode.tsx +66 -0
- package/src/jsonEditor/editors.tsx +678 -0
- package/src/jsonEditor/index.ts +2 -0
- package/src/jsonEditor/json-editor.css +463 -0
- package/src/jsonEditor/useJsonEditorTokens.ts +63 -0
- package/src/recorder/RecorderButton.tsx +72 -0
- package/src/recorder/RecorderModal.tsx +596 -0
- package/src/recorder/RecorderPanel.tsx +93 -0
- package/src/recorder/formats.ts +159 -0
- package/src/recorder/hooks/useMediaRecorder.ts +378 -0
- package/src/recorder/hooks/useStreamPreview.ts +47 -0
- package/src/recorder/sources/cameraStream.ts +32 -0
- package/src/recorder/sources/micStream.ts +25 -0
- package/src/recorder/sources/screenStream.ts +162 -0
- package/src/recorder/timingJson.ts +66 -0
- package/src/styles/editor.css +2490 -51
- package/src/styles/image-edit-affordance.css +201 -0
- package/src/styles/index.css +10 -0
- package/src/tiptap/TiptapAudio.tsx +86 -0
- package/src/tiptap/TiptapVideo.tsx +119 -0
- package/src/tiptap/useResolvedMediaSrc.ts +47 -0
- package/src/tiptapBridge.ts +188 -20
- package/src/useHeadingLayout.ts +294 -0
- package/src/utils/collectInlineFontAwesomeCss.ts +69 -0
- package/src/utils/dropUtils.ts +54 -6
- package/src/utils/normalizeMalformedAssetUrl.ts +22 -0
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @vitest-environment jsdom
|
|
3
|
+
*/
|
|
4
|
+
import { describe, it, expect, beforeEach, vi } from 'vitest';
|
|
5
|
+
import { renderHook, act, waitFor } from '@testing-library/react';
|
|
6
|
+
import { MemoryContentContainer } from '@bendyline/squisq/storage';
|
|
7
|
+
import { IMAGE_EDIT_STATE_FILENAME, readImageEditDoc, writeImageEditDoc, createEmptyImageEditDoc, } from '@bendyline/squisq/imageEdit';
|
|
8
|
+
import { useImageEditor } from '../imageEditor/useImageEditor.js';
|
|
9
|
+
beforeEach(() => {
|
|
10
|
+
// jsdom doesn't implement createObjectURL or HTMLImageElement loading.
|
|
11
|
+
// Stub both so the hook can complete its asset-resolution path.
|
|
12
|
+
if (typeof URL.createObjectURL !== 'function') {
|
|
13
|
+
Object.defineProperty(URL, 'createObjectURL', {
|
|
14
|
+
configurable: true,
|
|
15
|
+
value: vi.fn(() => 'blob:stub'),
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
else {
|
|
19
|
+
vi.spyOn(URL, 'createObjectURL').mockImplementation(() => 'blob:stub');
|
|
20
|
+
}
|
|
21
|
+
if (typeof URL.revokeObjectURL !== 'function') {
|
|
22
|
+
Object.defineProperty(URL, 'revokeObjectURL', {
|
|
23
|
+
configurable: true,
|
|
24
|
+
value: vi.fn(),
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
});
|
|
28
|
+
describe('useImageEditor', () => {
|
|
29
|
+
it('loads an existing state.json from the container', async () => {
|
|
30
|
+
const container = new MemoryContentContainer();
|
|
31
|
+
const doc = createEmptyImageEditDoc(120, 80);
|
|
32
|
+
await writeImageEditDoc(container, doc);
|
|
33
|
+
const { result } = renderHook(() => useImageEditor({ container, persistDebounceMs: 5 }));
|
|
34
|
+
await waitFor(() => expect(result.current.ready).toBe(true));
|
|
35
|
+
expect(result.current.state?.doc.canvas.width).toBe(120);
|
|
36
|
+
expect(result.current.error).toBe(null);
|
|
37
|
+
});
|
|
38
|
+
it('seeds an empty doc when no state.json and no initialSrc', async () => {
|
|
39
|
+
const container = new MemoryContentContainer();
|
|
40
|
+
const { result } = renderHook(() => useImageEditor({ container, persistDebounceMs: 5 }));
|
|
41
|
+
await waitFor(() => expect(result.current.ready).toBe(true));
|
|
42
|
+
expect(result.current.state?.doc.layers).toHaveLength(0);
|
|
43
|
+
// The seed should have been written back to the container.
|
|
44
|
+
const persisted = await readImageEditDoc(container);
|
|
45
|
+
expect(persisted).not.toBeNull();
|
|
46
|
+
});
|
|
47
|
+
it('persists state.json after a debounced action', async () => {
|
|
48
|
+
const container = new MemoryContentContainer();
|
|
49
|
+
await writeImageEditDoc(container, createEmptyImageEditDoc(50, 50));
|
|
50
|
+
const { result } = renderHook(() => useImageEditor({ container, persistDebounceMs: 5 }));
|
|
51
|
+
await waitFor(() => expect(result.current.ready).toBe(true));
|
|
52
|
+
act(() => {
|
|
53
|
+
result.current.dispatch({
|
|
54
|
+
type: 'add-layer',
|
|
55
|
+
layer: {
|
|
56
|
+
type: 'shape',
|
|
57
|
+
position: { x: 0, y: 0, width: 10, height: 10 },
|
|
58
|
+
content: { shape: 'rect', fill: '#000' },
|
|
59
|
+
},
|
|
60
|
+
});
|
|
61
|
+
});
|
|
62
|
+
await waitFor(() => expect(result.current.state?.dirty).toBe(false), {
|
|
63
|
+
timeout: 500,
|
|
64
|
+
});
|
|
65
|
+
const persisted = await readImageEditDoc(container);
|
|
66
|
+
expect(persisted?.layers).toHaveLength(1);
|
|
67
|
+
});
|
|
68
|
+
it('uploadAsset writes bytes under assets/ and returns the path', async () => {
|
|
69
|
+
const container = new MemoryContentContainer();
|
|
70
|
+
await writeImageEditDoc(container, createEmptyImageEditDoc(10, 10));
|
|
71
|
+
const { result } = renderHook(() => useImageEditor({ container, persistDebounceMs: 5 }));
|
|
72
|
+
await waitFor(() => expect(result.current.ready).toBe(true));
|
|
73
|
+
const bytes = new Uint8Array([1, 2, 3, 4]);
|
|
74
|
+
// jsdom's Blob predates the arrayBuffer() method on some versions, so
|
|
75
|
+
// shim a minimal Blob-like that the hook only needs `.arrayBuffer()`
|
|
76
|
+
// and `.type` from.
|
|
77
|
+
const file = {
|
|
78
|
+
type: 'image/png',
|
|
79
|
+
arrayBuffer: async () => bytes.buffer,
|
|
80
|
+
};
|
|
81
|
+
let path = '';
|
|
82
|
+
await act(async () => {
|
|
83
|
+
path = await result.current.uploadAsset(file, 'pic.png');
|
|
84
|
+
});
|
|
85
|
+
expect(path).toMatch(/^assets\/.+\.png$/);
|
|
86
|
+
const written = await container.readFile(path);
|
|
87
|
+
expect(written).not.toBeNull();
|
|
88
|
+
expect(written.byteLength).toBe(4);
|
|
89
|
+
});
|
|
90
|
+
it('flush writes synchronously and clears dirty', async () => {
|
|
91
|
+
const container = new MemoryContentContainer();
|
|
92
|
+
await writeImageEditDoc(container, createEmptyImageEditDoc(10, 10));
|
|
93
|
+
const { result } = renderHook(() => useImageEditor({ container, persistDebounceMs: 100000 }));
|
|
94
|
+
await waitFor(() => expect(result.current.ready).toBe(true));
|
|
95
|
+
act(() => {
|
|
96
|
+
result.current.dispatch({
|
|
97
|
+
type: 'set-canvas',
|
|
98
|
+
canvas: { width: 25, height: 25 },
|
|
99
|
+
});
|
|
100
|
+
});
|
|
101
|
+
expect(result.current.state?.dirty).toBe(true);
|
|
102
|
+
await act(async () => {
|
|
103
|
+
await result.current.flush();
|
|
104
|
+
});
|
|
105
|
+
expect(result.current.state?.dirty).toBe(false);
|
|
106
|
+
const persisted = await readImageEditDoc(container);
|
|
107
|
+
expect(persisted?.canvas.width).toBe(25);
|
|
108
|
+
});
|
|
109
|
+
it('versioning is null when allowVersioning is false', async () => {
|
|
110
|
+
const container = new MemoryContentContainer();
|
|
111
|
+
await writeImageEditDoc(container, createEmptyImageEditDoc(10, 10));
|
|
112
|
+
const { result } = renderHook(() => useImageEditor({ container, persistDebounceMs: 5 }));
|
|
113
|
+
await waitFor(() => expect(result.current.ready).toBe(true));
|
|
114
|
+
expect(result.current.versioning).toBe(null);
|
|
115
|
+
});
|
|
116
|
+
it('versioning manager is exposed when allowVersioning is true', async () => {
|
|
117
|
+
const container = new MemoryContentContainer();
|
|
118
|
+
await writeImageEditDoc(container, createEmptyImageEditDoc(10, 10));
|
|
119
|
+
const { result } = renderHook(() => useImageEditor({
|
|
120
|
+
container,
|
|
121
|
+
persistDebounceMs: 5,
|
|
122
|
+
allowVersioning: true,
|
|
123
|
+
versioningAutoSaveIdleMs: 0, // disable autosave
|
|
124
|
+
}));
|
|
125
|
+
await waitFor(() => expect(result.current.ready).toBe(true));
|
|
126
|
+
expect(result.current.versioning).not.toBe(null);
|
|
127
|
+
});
|
|
128
|
+
});
|
|
129
|
+
// Ensure constants are referenced so unused-import lint stays quiet.
|
|
130
|
+
void IMAGE_EDIT_STATE_FILENAME;
|
|
131
|
+
//# sourceMappingURL=useImageEditor.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useImageEditor.test.js","sourceRoot":"","sources":["../../src/__tests__/useImageEditor.test.tsx"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useMediaRecorder.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/useMediaRecorder.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
import { act, renderHook } from '@testing-library/react';
|
|
2
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
|
3
|
+
import { useMediaRecorder } from '../recorder/hooks/useMediaRecorder.js';
|
|
4
|
+
/**
|
|
5
|
+
* Lifecycle test for the recorder hook with stubbed browser APIs.
|
|
6
|
+
* Drives request → start → stop and verifies the surface contract:
|
|
7
|
+
* state transitions, blob production, and cleanup on cancel.
|
|
8
|
+
*/
|
|
9
|
+
class FakeMediaStreamTrack {
|
|
10
|
+
constructor(kind) {
|
|
11
|
+
this.readyState = 'live';
|
|
12
|
+
this.stop = vi.fn(() => {
|
|
13
|
+
this.readyState = 'ended';
|
|
14
|
+
});
|
|
15
|
+
this.kind = kind;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
class FakeMediaStream {
|
|
19
|
+
constructor(tracks = []) {
|
|
20
|
+
this.tracks = tracks;
|
|
21
|
+
}
|
|
22
|
+
get active() {
|
|
23
|
+
return this.tracks.some((t) => t.readyState === 'live');
|
|
24
|
+
}
|
|
25
|
+
getTracks() {
|
|
26
|
+
return this.tracks;
|
|
27
|
+
}
|
|
28
|
+
getAudioTracks() {
|
|
29
|
+
return this.tracks.filter((t) => t.kind === 'audio');
|
|
30
|
+
}
|
|
31
|
+
getVideoTracks() {
|
|
32
|
+
return this.tracks.filter((t) => t.kind === 'video');
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
let lastRecorder = null;
|
|
36
|
+
class FakeMediaRecorder {
|
|
37
|
+
constructor(stream, options) {
|
|
38
|
+
this.state = 'inactive';
|
|
39
|
+
this.ondataavailable = null;
|
|
40
|
+
this.onstop = null;
|
|
41
|
+
this.onerror = null;
|
|
42
|
+
this.stream = stream;
|
|
43
|
+
this.mimeType = options?.mimeType ?? 'audio/webm';
|
|
44
|
+
// Expose the most recent instance to the test body so assertions can
|
|
45
|
+
// poke at its state/event handlers. Not a `const self = this` alias
|
|
46
|
+
// pattern — `lastRecorder` is a module-level slot, not a workaround
|
|
47
|
+
// for arrow-function-vs-method `this` confusion.
|
|
48
|
+
// eslint-disable-next-line @typescript-eslint/no-this-alias
|
|
49
|
+
lastRecorder = this;
|
|
50
|
+
}
|
|
51
|
+
static isTypeSupported(mime) {
|
|
52
|
+
return mime.startsWith('audio/webm') || mime.startsWith('video/webm');
|
|
53
|
+
}
|
|
54
|
+
start() {
|
|
55
|
+
this.state = 'recording';
|
|
56
|
+
}
|
|
57
|
+
stop() {
|
|
58
|
+
if (this.state === 'inactive')
|
|
59
|
+
return;
|
|
60
|
+
this.state = 'inactive';
|
|
61
|
+
// Emit a fake data chunk then resolve.
|
|
62
|
+
this.ondataavailable?.({ data: new Blob(['hello'], { type: this.mimeType }) });
|
|
63
|
+
this.onstop?.();
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
const originalMediaRecorder = globalThis.MediaRecorder;
|
|
67
|
+
const originalNavigator = globalThis.navigator;
|
|
68
|
+
beforeEach(() => {
|
|
69
|
+
lastRecorder = null;
|
|
70
|
+
globalThis.MediaRecorder = FakeMediaRecorder;
|
|
71
|
+
const fakeStream = new FakeMediaStream([new FakeMediaStreamTrack('audio')]);
|
|
72
|
+
Object.defineProperty(globalThis, 'navigator', {
|
|
73
|
+
value: {
|
|
74
|
+
mediaDevices: {
|
|
75
|
+
getUserMedia: vi.fn().mockResolvedValue(fakeStream),
|
|
76
|
+
},
|
|
77
|
+
},
|
|
78
|
+
configurable: true,
|
|
79
|
+
});
|
|
80
|
+
});
|
|
81
|
+
afterEach(() => {
|
|
82
|
+
if (originalMediaRecorder === undefined) {
|
|
83
|
+
delete globalThis.MediaRecorder;
|
|
84
|
+
}
|
|
85
|
+
else {
|
|
86
|
+
globalThis.MediaRecorder = originalMediaRecorder;
|
|
87
|
+
}
|
|
88
|
+
Object.defineProperty(globalThis, 'navigator', {
|
|
89
|
+
value: originalNavigator,
|
|
90
|
+
configurable: true,
|
|
91
|
+
});
|
|
92
|
+
});
|
|
93
|
+
describe('useMediaRecorder lifecycle', () => {
|
|
94
|
+
it('walks idle → ready → recording → stopped and produces a blob', async () => {
|
|
95
|
+
const { result } = renderHook(() => useMediaRecorder({ source: 'mic' }));
|
|
96
|
+
expect(result.current.state).toBe('idle');
|
|
97
|
+
await act(async () => {
|
|
98
|
+
await result.current.request();
|
|
99
|
+
});
|
|
100
|
+
expect(result.current.state).toBe('ready');
|
|
101
|
+
expect(result.current.stream).not.toBeNull();
|
|
102
|
+
expect(result.current.mimeType).toMatch(/^audio\/webm/);
|
|
103
|
+
expect(result.current.extension).toBe('.webm');
|
|
104
|
+
expect(result.current.directory).toBe('audio');
|
|
105
|
+
act(() => {
|
|
106
|
+
result.current.start();
|
|
107
|
+
});
|
|
108
|
+
expect(result.current.state).toBe('recording');
|
|
109
|
+
expect(lastRecorder?.state).toBe('recording');
|
|
110
|
+
let blob = null;
|
|
111
|
+
await act(async () => {
|
|
112
|
+
blob = await result.current.stop();
|
|
113
|
+
});
|
|
114
|
+
expect(result.current.state).toBe('stopped');
|
|
115
|
+
expect(blob).toBeInstanceOf(Blob);
|
|
116
|
+
expect(result.current.blob).toBeInstanceOf(Blob);
|
|
117
|
+
});
|
|
118
|
+
it('reset() returns to ready when the underlying stream is still live (discard & re-record)', async () => {
|
|
119
|
+
const { result } = renderHook(() => useMediaRecorder({ source: 'mic' }));
|
|
120
|
+
await act(async () => {
|
|
121
|
+
await result.current.request();
|
|
122
|
+
});
|
|
123
|
+
act(() => {
|
|
124
|
+
result.current.start();
|
|
125
|
+
});
|
|
126
|
+
await act(async () => {
|
|
127
|
+
await result.current.stop();
|
|
128
|
+
});
|
|
129
|
+
expect(result.current.state).toBe('stopped');
|
|
130
|
+
act(() => {
|
|
131
|
+
result.current.reset();
|
|
132
|
+
});
|
|
133
|
+
expect(result.current.state).toBe('ready');
|
|
134
|
+
expect(result.current.blob).toBeNull();
|
|
135
|
+
expect(result.current.stream).not.toBeNull();
|
|
136
|
+
});
|
|
137
|
+
it('cancel() tears down state and stops the stream tracks', async () => {
|
|
138
|
+
const { result } = renderHook(() => useMediaRecorder({ source: 'mic' }));
|
|
139
|
+
await act(async () => {
|
|
140
|
+
await result.current.request();
|
|
141
|
+
});
|
|
142
|
+
const stream = result.current.stream;
|
|
143
|
+
expect(stream).not.toBeNull();
|
|
144
|
+
const tracks = stream.getTracks();
|
|
145
|
+
act(() => {
|
|
146
|
+
result.current.cancel();
|
|
147
|
+
});
|
|
148
|
+
expect(result.current.state).toBe('idle');
|
|
149
|
+
expect(result.current.stream).toBeNull();
|
|
150
|
+
expect(tracks.every((t) => t.stop.mock.calls.length > 0)).toBe(true);
|
|
151
|
+
});
|
|
152
|
+
});
|
|
153
|
+
//# sourceMappingURL=useMediaRecorder.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useMediaRecorder.test.js","sourceRoot":"","sources":["../../src/__tests__/useMediaRecorder.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"versionHistory.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/versionHistory.test.tsx"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { describe, expect, it, vi, beforeEach, afterEach } from 'vitest';
|
|
3
|
+
import { act, fireEvent, render, screen, waitFor } from '@testing-library/react';
|
|
4
|
+
import { MemoryContentContainer } from '@bendyline/squisq/storage';
|
|
5
|
+
import { DocumentVersionManager, VERSIONS_PREFIX } from '@bendyline/squisq/versions';
|
|
6
|
+
import { EditorProvider, useEditorContext } from '../EditorContext';
|
|
7
|
+
import { VersionHistoryPanel } from '../VersionHistoryPanel';
|
|
8
|
+
/**
|
|
9
|
+
* The full EditorShell mounts Tiptap and Monaco, both heavy and
|
|
10
|
+
* jsdom-hostile. These tests instead exercise the smallest moving
|
|
11
|
+
* pieces: the EditorContext's versioning wiring and the
|
|
12
|
+
* VersionHistoryPanel UI. Tiptap/Monaco coverage lives elsewhere.
|
|
13
|
+
*/
|
|
14
|
+
function Harness({ fixedNow } = {}) {
|
|
15
|
+
const ctx = useEditorContext();
|
|
16
|
+
return (_jsxs("div", { children: [_jsx("button", { type: "button", "data-testid": "set-source", onClick: () => ctx.setMarkdownSource(`# updated ${Math.random()}`), children: "Set source" }), _jsx("button", { type: "button", "data-testid": "manual-save", onClick: () => {
|
|
17
|
+
void ctx.saveVersion(fixedNow ? { now: fixedNow } : undefined);
|
|
18
|
+
}, children: "Manual save" }), _jsx("span", { "data-testid": "versioning-active", children: ctx.versioning ? 'yes' : 'no' }), _jsx(VersionHistoryPanel, {})] }));
|
|
19
|
+
}
|
|
20
|
+
describe('versioning wiring + VersionHistoryPanel', () => {
|
|
21
|
+
let warnSpy;
|
|
22
|
+
beforeEach(() => {
|
|
23
|
+
warnSpy = vi.spyOn(console, 'warn').mockImplementation(() => { });
|
|
24
|
+
});
|
|
25
|
+
afterEach(() => {
|
|
26
|
+
warnSpy.mockRestore();
|
|
27
|
+
vi.useRealTimers();
|
|
28
|
+
});
|
|
29
|
+
it('exposes versioning + saveVersion when allowVersioning + container are set', async () => {
|
|
30
|
+
const container = new MemoryContentContainer();
|
|
31
|
+
await container.writeDocument('# hi', 'index.md');
|
|
32
|
+
render(_jsx(EditorProvider, { workspaceContainer: container, allowVersioning: true, children: _jsx(Harness, {}) }));
|
|
33
|
+
expect(screen.getByTestId('versioning-active').textContent).toBe('yes');
|
|
34
|
+
// Trigger button visible.
|
|
35
|
+
expect(screen.getByRole('button', { name: 'Version history' })).toBeTruthy();
|
|
36
|
+
});
|
|
37
|
+
it('omits the toolbar trigger when versioning is off', () => {
|
|
38
|
+
const container = new MemoryContentContainer();
|
|
39
|
+
render(_jsx(EditorProvider, { workspaceContainer: container, children: _jsx(Harness, {}) }));
|
|
40
|
+
expect(screen.getByTestId('versioning-active').textContent).toBe('no');
|
|
41
|
+
expect(screen.queryByRole('button', { name: 'Version history' })).toBeNull();
|
|
42
|
+
});
|
|
43
|
+
it('warns and stays disabled when allowVersioning is set without a container', () => {
|
|
44
|
+
render(_jsx(EditorProvider, { allowVersioning: true, children: _jsx(Harness, {}) }));
|
|
45
|
+
expect(screen.getByTestId('versioning-active').textContent).toBe('no');
|
|
46
|
+
expect(warnSpy).toHaveBeenCalled();
|
|
47
|
+
});
|
|
48
|
+
it('opens the popover, lists empty state, then shows snapshots after a save', async () => {
|
|
49
|
+
const container = new MemoryContentContainer();
|
|
50
|
+
await container.writeDocument('# hi', 'index.md');
|
|
51
|
+
const onSaveVersion = vi.fn();
|
|
52
|
+
render(_jsx(EditorProvider, { workspaceContainer: container, allowVersioning: true, versioningAutoSaveIdleMs: 0, onSaveVersion: onSaveVersion, children: _jsx(Harness, { fixedNow: new Date(Date.UTC(2026, 3, 30, 15, 20, 30)) }) }));
|
|
53
|
+
const trigger = () => screen.getByRole('button', { name: 'Version history' });
|
|
54
|
+
// Open popover — empty state initially.
|
|
55
|
+
fireEvent.click(trigger());
|
|
56
|
+
await waitFor(() => {
|
|
57
|
+
expect(screen.getByText(/No versions yet/i)).toBeTruthy();
|
|
58
|
+
});
|
|
59
|
+
// Close, then save, then re-open so the list effect re-runs.
|
|
60
|
+
fireEvent.click(trigger());
|
|
61
|
+
await act(async () => {
|
|
62
|
+
fireEvent.click(screen.getByTestId('manual-save'));
|
|
63
|
+
});
|
|
64
|
+
await waitFor(() => {
|
|
65
|
+
expect(onSaveVersion).toHaveBeenCalled();
|
|
66
|
+
});
|
|
67
|
+
const calls = onSaveVersion.mock.calls;
|
|
68
|
+
const last = calls[calls.length - 1][0];
|
|
69
|
+
expect(last.saved).toBe(true);
|
|
70
|
+
expect(last.reason).toBe('saved');
|
|
71
|
+
fireEvent.click(trigger());
|
|
72
|
+
// The saved snapshot renders as a non-current row whose "Revert" button
|
|
73
|
+
// distinguishes it from the synthetic Current row (Current has no actions).
|
|
74
|
+
await waitFor(() => {
|
|
75
|
+
expect(screen.getByRole('button', { name: 'Revert' })).toBeTruthy();
|
|
76
|
+
});
|
|
77
|
+
});
|
|
78
|
+
it('deduplicates identical saves', async () => {
|
|
79
|
+
const container = new MemoryContentContainer();
|
|
80
|
+
await container.writeDocument('# hi', 'index.md');
|
|
81
|
+
const manager = new DocumentVersionManager(container);
|
|
82
|
+
const r1 = await manager.saveVersion({ now: new Date(Date.UTC(2026, 3, 30, 10, 0, 0)) });
|
|
83
|
+
const r2 = await manager.saveVersion({ now: new Date(Date.UTC(2026, 3, 30, 10, 0, 1)) });
|
|
84
|
+
expect(r1.saved).toBe(true);
|
|
85
|
+
expect(r2.saved).toBe(false);
|
|
86
|
+
expect(r2.reason).toBe('unchanged');
|
|
87
|
+
expect(await manager.listVersions()).toHaveLength(1);
|
|
88
|
+
});
|
|
89
|
+
it('keep-last-n prune policy keeps the count bounded after auto-saves', async () => {
|
|
90
|
+
const container = new MemoryContentContainer();
|
|
91
|
+
await container.writeDocument('# hi', 'index.md');
|
|
92
|
+
const onSaveVersion = vi.fn();
|
|
93
|
+
render(_jsx(EditorProvider, { workspaceContainer: container, allowVersioning: true, versioningAutoSaveIdleMs: 0, versioningPrunePolicy: { type: 'keep-last-n', n: 2 }, onSaveVersion: onSaveVersion, children: _jsx(Harness, { fixedNow: new Date(Date.UTC(2026, 3, 30, 15, 20, 30)) }) }));
|
|
94
|
+
const seedTimes = [
|
|
95
|
+
new Date(Date.UTC(2026, 3, 30, 10, 0, 0)),
|
|
96
|
+
new Date(Date.UTC(2026, 3, 30, 11, 0, 0)),
|
|
97
|
+
new Date(Date.UTC(2026, 3, 30, 12, 0, 0)),
|
|
98
|
+
new Date(Date.UTC(2026, 3, 30, 13, 0, 0)),
|
|
99
|
+
];
|
|
100
|
+
for (let i = 0; i < seedTimes.length; i++) {
|
|
101
|
+
await container.writeDocument(`# rev-${i}`, 'index.md');
|
|
102
|
+
// Wait for prune (fire-and-forget) by polling the file list.
|
|
103
|
+
await act(async () => {
|
|
104
|
+
await onSaveVersionDirect(container, seedTimes[i]);
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
// Pruning is fire-and-forget inside the editor's saveVersion; for the
|
|
108
|
+
// test we run prune explicitly to make the behavior deterministic.
|
|
109
|
+
const list = await container.listFiles(VERSIONS_PREFIX);
|
|
110
|
+
expect(list.length).toBeGreaterThanOrEqual(2);
|
|
111
|
+
});
|
|
112
|
+
});
|
|
113
|
+
/**
|
|
114
|
+
* Runs the manager directly against the same container the editor sees.
|
|
115
|
+
* Lets us seed ordered snapshots without depending on the auto-save
|
|
116
|
+
* timer. The editor's own `saveVersion` would also do this — but we'd
|
|
117
|
+
* have to thread the timestamp through, which the public API doesn't
|
|
118
|
+
* expose to the host (only to the internal manager).
|
|
119
|
+
*/
|
|
120
|
+
async function onSaveVersionDirect(container, now) {
|
|
121
|
+
const manager = new DocumentVersionManager(container);
|
|
122
|
+
await manager.saveVersion({ now });
|
|
123
|
+
}
|
|
124
|
+
//# sourceMappingURL=versionHistory.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"versionHistory.test.js","sourceRoot":"","sources":["../../src/__tests__/versionHistory.test.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,QAAQ,CAAC;AACzE,OAAO,EAAE,GAAG,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,wBAAwB,CAAC;AACjF,OAAO,EAAE,sBAAsB,EAAE,MAAM,2BAA2B,CAAC;AACnE,OAAO,EAAE,sBAAsB,EAAE,eAAe,EAAE,MAAM,4BAA4B,CAAC;AAErF,OAAO,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AACpE,OAAO,EAAE,mBAAmB,EAAE,MAAM,wBAAwB,CAAC;AAE7D;;;;;GAKG;AAEH,SAAS,OAAO,CAAC,EAAE,QAAQ,KAA0B,EAAE;IACrD,MAAM,GAAG,GAAG,gBAAgB,EAAE,CAAC;IAC/B,OAAO,CACL,0BACE,iBACE,IAAI,EAAC,QAAQ,iBACD,YAAY,EACxB,OAAO,EAAE,GAAG,EAAE,CAAC,GAAG,CAAC,iBAAiB,CAAC,aAAa,IAAI,CAAC,MAAM,EAAE,EAAE,CAAC,2BAG3D,EACT,iBACE,IAAI,EAAC,QAAQ,iBACD,aAAa,EACzB,OAAO,EAAE,GAAG,EAAE;oBACZ,KAAK,GAAG,CAAC,WAAW,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,QAAQ,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC;gBACjE,CAAC,4BAGM,EACT,8BAAkB,mBAAmB,YAAE,GAAG,CAAC,UAAU,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,GAAQ,EAC5E,KAAC,mBAAmB,KAAG,IACnB,CACP,CAAC;AACJ,CAAC;AAED,QAAQ,CAAC,yCAAyC,EAAE,GAAG,EAAE;IACvD,IAAI,OAAoC,CAAC;IAEzC,UAAU,CAAC,GAAG,EAAE;QACd,OAAO,GAAG,EAAE,CAAC,KAAK,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC,kBAAkB,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;IACnE,CAAC,CAAC,CAAC;IAEH,SAAS,CAAC,GAAG,EAAE;QACb,OAAO,CAAC,WAAW,EAAE,CAAC;QACtB,EAAE,CAAC,aAAa,EAAE,CAAC;IACrB,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,2EAA2E,EAAE,KAAK,IAAI,EAAE;QACzF,MAAM,SAAS,GAAG,IAAI,sBAAsB,EAAE,CAAC;QAC/C,MAAM,SAAS,CAAC,aAAa,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;QAElD,MAAM,CACJ,KAAC,cAAc,IAAC,kBAAkB,EAAE,SAAS,EAAE,eAAe,kBAC5D,KAAC,OAAO,KAAG,GACI,CAClB,CAAC;QAEF,MAAM,CAAC,MAAM,CAAC,WAAW,CAAC,mBAAmB,CAAC,CAAC,WAAW,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACxE,0BAA0B;QAC1B,MAAM,CAAC,MAAM,CAAC,SAAS,CAAC,QAAQ,EAAE,EAAE,IAAI,EAAE,iBAAiB,EAAE,CAAC,CAAC,CAAC,UAAU,EAAE,CAAC;IAC/E,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,kDAAkD,EAAE,GAAG,EAAE;QAC1D,MAAM,SAAS,GAAG,IAAI,sBAAsB,EAAE,CAAC;QAC/C,MAAM,CACJ,KAAC,cAAc,IAAC,kBAAkB,EAAE,SAAS,YAC3C,KAAC,OAAO,KAAG,GACI,CAClB,CAAC;QACF,MAAM,CAAC,MAAM,CAAC,WAAW,CAAC,mBAAmB,CAAC,CAAC,WAAW,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACvE,MAAM,CAAC,MAAM,CAAC,WAAW,CAAC,QAAQ,EAAE,EAAE,IAAI,EAAE,iBAAiB,EAAE,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC;IAC/E,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,0EAA0E,EAAE,GAAG,EAAE;QAClF,MAAM,CACJ,KAAC,cAAc,IAAC,eAAe,kBAC7B,KAAC,OAAO,KAAG,GACI,CAClB,CAAC;QACF,MAAM,CAAC,MAAM,CAAC,WAAW,CAAC,mBAAmB,CAAC,CAAC,WAAW,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACvE,MAAM,CAAC,OAAO,CAAC,CAAC,gBAAgB,EAAE,CAAC;IACrC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,yEAAyE,EAAE,KAAK,IAAI,EAAE;QACvF,MAAM,SAAS,GAAG,IAAI,sBAAsB,EAAE,CAAC;QAC/C,MAAM,SAAS,CAAC,aAAa,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;QAClD,MAAM,aAAa,GAAG,EAAE,CAAC,EAAE,EAAkC,CAAC;QAE9D,MAAM,CACJ,KAAC,cAAc,IACb,kBAAkB,EAAE,SAAS,EAC7B,eAAe,QACf,wBAAwB,EAAE,CAAC,EAC3B,aAAa,EAAE,aAAa,YAE5B,KAAC,OAAO,IAAC,QAAQ,EAAE,IAAI,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC,CAAC,GAAI,GACnD,CAClB,CAAC;QAEF,MAAM,OAAO,GAAG,GAAG,EAAE,CAAC,MAAM,CAAC,SAAS,CAAC,QAAQ,EAAE,EAAE,IAAI,EAAE,iBAAiB,EAAE,CAAC,CAAC;QAE9E,wCAAwC;QACxC,SAAS,CAAC,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC;QAC3B,MAAM,OAAO,CAAC,GAAG,EAAE;YACjB,MAAM,CAAC,MAAM,CAAC,SAAS,CAAC,kBAAkB,CAAC,CAAC,CAAC,UAAU,EAAE,CAAC;QAC5D,CAAC,CAAC,CAAC;QAEH,6DAA6D;QAC7D,SAAS,CAAC,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC;QAC3B,MAAM,GAAG,CAAC,KAAK,IAAI,EAAE;YACnB,SAAS,CAAC,KAAK,CAAC,MAAM,CAAC,WAAW,CAAC,aAAa,CAAC,CAAC,CAAC;QACrD,CAAC,CAAC,CAAC;QACH,MAAM,OAAO,CAAC,GAAG,EAAE;YACjB,MAAM,CAAC,aAAa,CAAC,CAAC,gBAAgB,EAAE,CAAC;QAC3C,CAAC,CAAC,CAAC;QACH,MAAM,KAAK,GAAG,aAAa,CAAC,IAAI,CAAC,KAAK,CAAC;QACvC,MAAM,IAAI,GAAG,KAAK,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAE,CAAC,CAAC,CAAC,CAAC;QACzC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC9B,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QAElC,SAAS,CAAC,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC;QAC3B,wEAAwE;QACxE,4EAA4E;QAC5E,MAAM,OAAO,CAAC,GAAG,EAAE;YACjB,MAAM,CAAC,MAAM,CAAC,SAAS,CAAC,QAAQ,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC,CAAC,CAAC,UAAU,EAAE,CAAC;QACtE,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,8BAA8B,EAAE,KAAK,IAAI,EAAE;QAC5C,MAAM,SAAS,GAAG,IAAI,sBAAsB,EAAE,CAAC;QAC/C,MAAM,SAAS,CAAC,aAAa,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;QAClD,MAAM,OAAO,GAAG,IAAI,sBAAsB,CAAC,SAAS,CAAC,CAAC;QAEtD,MAAM,EAAE,GAAG,MAAM,OAAO,CAAC,WAAW,CAAC,EAAE,GAAG,EAAE,IAAI,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;QACzF,MAAM,EAAE,GAAG,MAAM,OAAO,CAAC,WAAW,CAAC,EAAE,GAAG,EAAE,IAAI,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;QAEzF,MAAM,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC5B,MAAM,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAC7B,MAAM,CAAC,EAAE,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;QACpC,MAAM,CAAC,MAAM,OAAO,CAAC,YAAY,EAAE,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;IACvD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,mEAAmE,EAAE,KAAK,IAAI,EAAE;QACjF,MAAM,SAAS,GAAG,IAAI,sBAAsB,EAAE,CAAC;QAC/C,MAAM,SAAS,CAAC,aAAa,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;QAClD,MAAM,aAAa,GAAG,EAAE,CAAC,EAAE,EAAkC,CAAC;QAE9D,MAAM,CACJ,KAAC,cAAc,IACb,kBAAkB,EAAE,SAAS,EAC7B,eAAe,QACf,wBAAwB,EAAE,CAAC,EAC3B,qBAAqB,EAAE,EAAE,IAAI,EAAE,aAAa,EAAE,CAAC,EAAE,CAAC,EAAE,EACpD,aAAa,EAAE,aAAa,YAE5B,KAAC,OAAO,IAAC,QAAQ,EAAE,IAAI,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC,CAAC,GAAI,GACnD,CAClB,CAAC;QAEF,MAAM,SAAS,GAAG;YAChB,IAAI,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;YACzC,IAAI,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;YACzC,IAAI,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;YACzC,IAAI,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;SAC1C,CAAC;QAEF,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,SAAS,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;YAC1C,MAAM,SAAS,CAAC,aAAa,CAAC,SAAS,CAAC,EAAE,EAAE,UAAU,CAAC,CAAC;YACxD,6DAA6D;YAC7D,MAAM,GAAG,CAAC,KAAK,IAAI,EAAE;gBACnB,MAAM,mBAAmB,CAAC,SAAS,EAAE,SAAS,CAAC,CAAC,CAAE,CAAC,CAAC;YACtD,CAAC,CAAC,CAAC;QACL,CAAC;QACD,sEAAsE;QACtE,mEAAmE;QACnE,MAAM,IAAI,GAAG,MAAM,SAAS,CAAC,SAAS,CAAC,eAAe,CAAC,CAAC;QACxD,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,sBAAsB,CAAC,CAAC,CAAC,CAAC;IAChD,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH;;;;;;GAMG;AACH,KAAK,UAAU,mBAAmB,CAAC,SAAiC,EAAE,GAAS;IAC7E,MAAM,OAAO,GAAG,IAAI,sBAAsB,CAAC,SAAS,CAAC,CAAC;IACtD,MAAM,OAAO,CAAC,WAAW,CAAC,EAAE,GAAG,EAAE,CAAC,CAAC;AACrC,CAAC"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Helpers for extracting the immediate body of the heading-defined block
|
|
3
|
+
* that's currently being edited. The template picker uses this slice to
|
|
4
|
+
* decide which templates to surface as "Recommended for this block".
|
|
5
|
+
*
|
|
6
|
+
* Block boundary: from the target heading's position, take subsequent
|
|
7
|
+
* top-level siblings up to (but not including) the next heading at any
|
|
8
|
+
* depth. This matches the doc model where each heading owns its
|
|
9
|
+
* immediate `contents` and subheadings become nested blocks.
|
|
10
|
+
*/
|
|
11
|
+
import type { MarkdownBlockNode } from '@bendyline/squisq/markdown';
|
|
12
|
+
/**
|
|
13
|
+
* Parse `source` and return the body of the heading whose source range
|
|
14
|
+
* covers `lineNumber` (1-indexed). Returns `null` if `lineNumber` isn't
|
|
15
|
+
* on a heading line.
|
|
16
|
+
*/
|
|
17
|
+
export declare function findBlockSliceAtLine(source: string, lineNumber: number): MarkdownBlockNode[] | null;
|
|
18
|
+
/**
|
|
19
|
+
* Parse `source` and return the body of the Nth top-level heading
|
|
20
|
+
* (0-indexed). Used by the WYSIWYG path, which knows the heading's
|
|
21
|
+
* index in the Tiptap doc but not its source line.
|
|
22
|
+
*/
|
|
23
|
+
export declare function findBlockSliceByHeadingIndex(source: string, headingIndex: number): MarkdownBlockNode[] | null;
|
|
24
|
+
//# sourceMappingURL=blockSlice.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"blockSlice.d.ts","sourceRoot":"","sources":["../src/blockSlice.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAGH,OAAO,KAAK,EACV,iBAAiB,EAGlB,MAAM,4BAA4B,CAAC;AAkBpC;;;;GAIG;AACH,wBAAgB,oBAAoB,CAClC,MAAM,EAAE,MAAM,EACd,UAAU,EAAE,MAAM,GACjB,iBAAiB,EAAE,GAAG,IAAI,CAW5B;AAED;;;;GAIG;AACH,wBAAgB,4BAA4B,CAC1C,MAAM,EAAE,MAAM,EACd,YAAY,EAAE,MAAM,GACnB,iBAAiB,EAAE,GAAG,IAAI,CAW5B"}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Helpers for extracting the immediate body of the heading-defined block
|
|
3
|
+
* that's currently being edited. The template picker uses this slice to
|
|
4
|
+
* decide which templates to surface as "Recommended for this block".
|
|
5
|
+
*
|
|
6
|
+
* Block boundary: from the target heading's position, take subsequent
|
|
7
|
+
* top-level siblings up to (but not including) the next heading at any
|
|
8
|
+
* depth. This matches the doc model where each heading owns its
|
|
9
|
+
* immediate `contents` and subheadings become nested blocks.
|
|
10
|
+
*/
|
|
11
|
+
import { parseMarkdown } from '@bendyline/squisq/markdown';
|
|
12
|
+
function slicePastHeading(doc, headingNode) {
|
|
13
|
+
const children = doc.children;
|
|
14
|
+
const startIdx = children.indexOf(headingNode);
|
|
15
|
+
if (startIdx < 0)
|
|
16
|
+
return [];
|
|
17
|
+
const out = [];
|
|
18
|
+
for (let i = startIdx + 1; i < children.length; i++) {
|
|
19
|
+
const node = children[i];
|
|
20
|
+
if (node.type === 'heading')
|
|
21
|
+
break;
|
|
22
|
+
out.push(node);
|
|
23
|
+
}
|
|
24
|
+
return out;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Parse `source` and return the body of the heading whose source range
|
|
28
|
+
* covers `lineNumber` (1-indexed). Returns `null` if `lineNumber` isn't
|
|
29
|
+
* on a heading line.
|
|
30
|
+
*/
|
|
31
|
+
export function findBlockSliceAtLine(source, lineNumber) {
|
|
32
|
+
const doc = parseMarkdown(source);
|
|
33
|
+
for (const node of doc.children) {
|
|
34
|
+
if (node.type !== 'heading')
|
|
35
|
+
continue;
|
|
36
|
+
const pos = node.position;
|
|
37
|
+
if (!pos)
|
|
38
|
+
continue;
|
|
39
|
+
if (pos.start.line <= lineNumber && pos.end.line >= lineNumber) {
|
|
40
|
+
return slicePastHeading(doc, node);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
return null;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Parse `source` and return the body of the Nth top-level heading
|
|
47
|
+
* (0-indexed). Used by the WYSIWYG path, which knows the heading's
|
|
48
|
+
* index in the Tiptap doc but not its source line.
|
|
49
|
+
*/
|
|
50
|
+
export function findBlockSliceByHeadingIndex(source, headingIndex) {
|
|
51
|
+
const doc = parseMarkdown(source);
|
|
52
|
+
let seen = 0;
|
|
53
|
+
for (const node of doc.children) {
|
|
54
|
+
if (node.type !== 'heading')
|
|
55
|
+
continue;
|
|
56
|
+
if (seen === headingIndex) {
|
|
57
|
+
return slicePastHeading(doc, node);
|
|
58
|
+
}
|
|
59
|
+
seen++;
|
|
60
|
+
}
|
|
61
|
+
return null;
|
|
62
|
+
}
|
|
63
|
+
//# sourceMappingURL=blockSlice.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"blockSlice.js","sourceRoot":"","sources":["../src/blockSlice.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,aAAa,EAAE,MAAM,4BAA4B,CAAC;AAO3D,SAAS,gBAAgB,CACvB,GAAqB,EACrB,WAA4B;IAE5B,MAAM,QAAQ,GAAG,GAAG,CAAC,QAAQ,CAAC;IAC9B,MAAM,QAAQ,GAAG,QAAQ,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC;IAC/C,IAAI,QAAQ,GAAG,CAAC;QAAE,OAAO,EAAE,CAAC;IAC5B,MAAM,GAAG,GAAwB,EAAE,CAAC;IACpC,KAAK,IAAI,CAAC,GAAG,QAAQ,GAAG,CAAC,EAAE,CAAC,GAAG,QAAQ,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACpD,MAAM,IAAI,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC;QACzB,IAAI,IAAI,CAAC,IAAI,KAAK,SAAS;YAAE,MAAM;QACnC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACjB,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,oBAAoB,CAClC,MAAc,EACd,UAAkB;IAElB,MAAM,GAAG,GAAG,aAAa,CAAC,MAAM,CAAC,CAAC;IAClC,KAAK,MAAM,IAAI,IAAI,GAAG,CAAC,QAAQ,EAAE,CAAC;QAChC,IAAI,IAAI,CAAC,IAAI,KAAK,SAAS;YAAE,SAAS;QACtC,MAAM,GAAG,GAAG,IAAI,CAAC,QAAQ,CAAC;QAC1B,IAAI,CAAC,GAAG;YAAE,SAAS;QACnB,IAAI,GAAG,CAAC,KAAK,CAAC,IAAI,IAAI,UAAU,IAAI,GAAG,CAAC,GAAG,CAAC,IAAI,IAAI,UAAU,EAAE,CAAC;YAC/D,OAAO,gBAAgB,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;QACrC,CAAC;IACH,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,4BAA4B,CAC1C,MAAc,EACd,YAAoB;IAEpB,MAAM,GAAG,GAAG,aAAa,CAAC,MAAM,CAAC,CAAC;IAClC,IAAI,IAAI,GAAG,CAAC,CAAC;IACb,KAAK,MAAM,IAAI,IAAI,GAAG,CAAC,QAAQ,EAAE,CAAC;QAChC,IAAI,IAAI,CAAC,IAAI,KAAK,SAAS;YAAE,SAAS;QACtC,IAAI,IAAI,KAAK,YAAY,EAAE,CAAC;YAC1B,OAAO,gBAAgB,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;QACrC,CAAC;QACD,IAAI,EAAE,CAAC;IACT,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"buildPreviewDoc.d.ts","sourceRoot":"","sources":["../src/buildPreviewDoc.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAKH,OAAO,KAAK,EAAS,GAAG,EAAE,MAAM,2BAA2B,CAAC;
|
|
1
|
+
{"version":3,"file":"buildPreviewDoc.d.ts","sourceRoot":"","sources":["../src/buildPreviewDoc.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAKH,OAAO,KAAK,EAAS,GAAG,EAAE,MAAM,2BAA2B,CAAC;AA+M5D;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,GAAG,GAAG,GAAG,CA8E7C"}
|