@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
package/src/RawEditor.tsx
CHANGED
|
@@ -7,10 +7,16 @@
|
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
9
|
import { useRef, useCallback, useEffect } from 'react';
|
|
10
|
-
import Editor, {
|
|
10
|
+
import Editor, {
|
|
11
|
+
loader,
|
|
12
|
+
type OnMount,
|
|
13
|
+
type OnChange,
|
|
14
|
+
type BeforeMount,
|
|
15
|
+
} from '@monaco-editor/react';
|
|
11
16
|
import * as monaco from 'monaco-editor';
|
|
12
17
|
import { useEditorContext } from './EditorContext';
|
|
13
18
|
import { getAvailableTemplates } from '@bendyline/squisq/doc';
|
|
19
|
+
import { suggestIcons, resolveIcon, iconGlyph } from '@bendyline/squisq/icons';
|
|
14
20
|
import { SQUISQ_MEDIA_MIME, parseSquisqMediaPayload } from './mediaDragMime';
|
|
15
21
|
|
|
16
22
|
// Use locally installed monaco-editor instead of CDN.
|
|
@@ -25,6 +31,23 @@ import { SQUISQ_MEDIA_MIME, parseSquisqMediaPayload } from './mediaDragMime';
|
|
|
25
31
|
// only the language contributions needed (e.g. markdown, javascript, etc.).
|
|
26
32
|
loader.config({ monaco });
|
|
27
33
|
|
|
34
|
+
// Squisq Monaco themes: same syntax highlighting as vs / vs-dark, but with
|
|
35
|
+
// Monaco's internal gutter (line numbers + folding margin) and overview
|
|
36
|
+
// ruler tinted to match the side-pane "desk" colors so the canvas's
|
|
37
|
+
// internal furniture blends with its surroundings. The seam color is the
|
|
38
|
+
// 1px line Monaco draws between the white canvas and the overview ruler;
|
|
39
|
+
// matches the `::after` border on `.margin` so both sides of the canvas
|
|
40
|
+
// frame look the same.
|
|
41
|
+
const SQUISQ_LIGHT_GUTTER = '#dcd8d0';
|
|
42
|
+
const SQUISQ_DARK_GUTTER = '#0f1219';
|
|
43
|
+
const SQUISQ_LIGHT_SEAM = '#b0a99a';
|
|
44
|
+
const SQUISQ_DARK_SEAM = '#2a3144';
|
|
45
|
+
|
|
46
|
+
const SQUISQ_THEMES: Record<string, string> = {
|
|
47
|
+
vs: 'squisq-light',
|
|
48
|
+
'vs-dark': 'squisq-dark',
|
|
49
|
+
};
|
|
50
|
+
|
|
28
51
|
export interface RawEditorProps {
|
|
29
52
|
/** Monaco editor theme (default: 'vs-dark') */
|
|
30
53
|
theme?: string;
|
|
@@ -64,6 +87,8 @@ export function RawEditor({
|
|
|
64
87
|
const isExternalUpdate = useRef(false);
|
|
65
88
|
const completionDisposable = useRef<monaco.IDisposable | null>(null);
|
|
66
89
|
const mentionCompletionDisposable = useRef<monaco.IDisposable | null>(null);
|
|
90
|
+
const iconCompletionDisposable = useRef<monaco.IDisposable | null>(null);
|
|
91
|
+
const iconGlyphDecorations = useRef<monaco.editor.IEditorDecorationsCollection | null>(null);
|
|
67
92
|
const dropCleanupRef = useRef<(() => void) | null>(null);
|
|
68
93
|
const keyDisposable = useRef<monaco.IDisposable | null>(null);
|
|
69
94
|
// Ref so the keydown handler always sees the latest callback.
|
|
@@ -78,6 +103,31 @@ export function RawEditor({
|
|
|
78
103
|
mentionProviderRef.current = mentionProvider;
|
|
79
104
|
}, [mentionProvider]);
|
|
80
105
|
|
|
106
|
+
const handleBeforeMount: BeforeMount = useCallback((monaco) => {
|
|
107
|
+
monaco.editor.defineTheme('squisq-light', {
|
|
108
|
+
base: 'vs',
|
|
109
|
+
inherit: true,
|
|
110
|
+
rules: [],
|
|
111
|
+
colors: {
|
|
112
|
+
'editorGutter.background': SQUISQ_LIGHT_GUTTER,
|
|
113
|
+
'editorOverviewRuler.background': SQUISQ_LIGHT_GUTTER,
|
|
114
|
+
'editorOverviewRuler.border': SQUISQ_LIGHT_SEAM,
|
|
115
|
+
'minimap.background': SQUISQ_LIGHT_GUTTER,
|
|
116
|
+
},
|
|
117
|
+
});
|
|
118
|
+
monaco.editor.defineTheme('squisq-dark', {
|
|
119
|
+
base: 'vs-dark',
|
|
120
|
+
inherit: true,
|
|
121
|
+
rules: [],
|
|
122
|
+
colors: {
|
|
123
|
+
'editorGutter.background': SQUISQ_DARK_GUTTER,
|
|
124
|
+
'editorOverviewRuler.background': SQUISQ_DARK_GUTTER,
|
|
125
|
+
'editorOverviewRuler.border': SQUISQ_DARK_SEAM,
|
|
126
|
+
'minimap.background': SQUISQ_DARK_GUTTER,
|
|
127
|
+
},
|
|
128
|
+
});
|
|
129
|
+
}, []);
|
|
130
|
+
|
|
81
131
|
const handleMount: OnMount = useCallback(
|
|
82
132
|
(editor, monaco) => {
|
|
83
133
|
editorRef.current = editor;
|
|
@@ -89,6 +139,8 @@ export function RawEditor({
|
|
|
89
139
|
completionDisposable.current = null;
|
|
90
140
|
mentionCompletionDisposable.current?.dispose();
|
|
91
141
|
mentionCompletionDisposable.current = null;
|
|
142
|
+
iconCompletionDisposable.current?.dispose();
|
|
143
|
+
iconCompletionDisposable.current = null;
|
|
92
144
|
|
|
93
145
|
// Register the `{[template]}` completion provider only for markdown
|
|
94
146
|
// files — it's meaningless for TypeScript, JSON, Python, etc.
|
|
@@ -103,10 +155,17 @@ export function RawEditor({
|
|
|
103
155
|
if (!/^#{1,6}\s/.test(lineContent)) return { suggestions: [] };
|
|
104
156
|
|
|
105
157
|
const textBeforeCursor = lineContent.substring(0, position.column - 1);
|
|
158
|
+
const textAfterCursor = lineContent.substring(position.column - 1);
|
|
106
159
|
const bracketIdx = textBeforeCursor.lastIndexOf('{[');
|
|
107
160
|
if (bracketIdx === -1) return { suggestions: [] };
|
|
108
161
|
|
|
109
|
-
//
|
|
162
|
+
// When Monaco's bracket auto-pair has already produced the
|
|
163
|
+
// closing `]}` we just leave it in place and skip the
|
|
164
|
+
// suffix — otherwise accepting `sectionHeader` on
|
|
165
|
+
// `{[gi]}` would yield `{[sectionHeader]}]}`.
|
|
166
|
+
const closingMatch = textAfterCursor.match(/^\]\}/);
|
|
167
|
+
const suffix = closingMatch ? '' : ']}';
|
|
168
|
+
|
|
110
169
|
const startCol = bracketIdx + 3; // after {[
|
|
111
170
|
const range = new monaco.Range(
|
|
112
171
|
position.lineNumber,
|
|
@@ -117,8 +176,9 @@ export function RawEditor({
|
|
|
117
176
|
|
|
118
177
|
const suggestions = templates.map((name) => ({
|
|
119
178
|
label: name,
|
|
179
|
+
filterText: name,
|
|
120
180
|
kind: monaco.languages.CompletionItemKind.Value,
|
|
121
|
-
insertText: name +
|
|
181
|
+
insertText: name + suffix,
|
|
122
182
|
range,
|
|
123
183
|
detail: 'Block template',
|
|
124
184
|
sortText: name,
|
|
@@ -182,6 +242,86 @@ export function RawEditor({
|
|
|
182
242
|
},
|
|
183
243
|
},
|
|
184
244
|
);
|
|
245
|
+
|
|
246
|
+
// FontAwesome icon completion. Fires inside any `{[…]}` opener
|
|
247
|
+
// anywhere in the doc (not just headings — icons are inline).
|
|
248
|
+
// Suggestions cover the whole FA Free catalog filtered by the
|
|
249
|
+
// partial token; we cap at 50 to keep the popup readable. The
|
|
250
|
+
// template provider above still handles heading lines, so on
|
|
251
|
+
// a `## Title {[…]}` the user sees both template names AND
|
|
252
|
+
// icons interleaved by the regular Monaco filter.
|
|
253
|
+
iconCompletionDisposable.current = monaco.languages.registerCompletionItemProvider(
|
|
254
|
+
'markdown',
|
|
255
|
+
{
|
|
256
|
+
triggerCharacters: ['['],
|
|
257
|
+
provideCompletionItems(model, position) {
|
|
258
|
+
const lineContent = model.getLineContent(position.lineNumber);
|
|
259
|
+
const textBeforeCursor = lineContent.substring(0, position.column - 1);
|
|
260
|
+
const textAfterCursor = lineContent.substring(position.column - 1);
|
|
261
|
+
const bracketIdx = textBeforeCursor.lastIndexOf('{[');
|
|
262
|
+
if (bracketIdx === -1) return { suggestions: [] };
|
|
263
|
+
// Bail if any `]` already closes this annotation between
|
|
264
|
+
// `{[` and the cursor — we'd be past the token, not in it.
|
|
265
|
+
const between = textBeforeCursor.slice(bracketIdx + 2);
|
|
266
|
+
if (between.includes(']')) return { suggestions: [] };
|
|
267
|
+
// Tokens are alphanumeric + `-_:`; anything else means
|
|
268
|
+
// we're not inside an icon (probably a code/link bracket).
|
|
269
|
+
if (between && !/^[a-zA-Z0-9_:-]*$/.test(between)) {
|
|
270
|
+
return { suggestions: [] };
|
|
271
|
+
}
|
|
272
|
+
const query = between.toLowerCase();
|
|
273
|
+
|
|
274
|
+
// When Monaco's bracket auto-pair has already produced a
|
|
275
|
+
// closing `]}` we don't want to insert another — but we
|
|
276
|
+
// also don't want to consume the existing one, since
|
|
277
|
+
// then we'd have to re-emit it and the round-trip is
|
|
278
|
+
// brittle. Leave the closing in place and just insert
|
|
279
|
+
// the bare token; if no closing exists yet, append it.
|
|
280
|
+
const closingMatch = textAfterCursor.match(/^\]\}/);
|
|
281
|
+
const suffix = closingMatch ? '' : ']}';
|
|
282
|
+
|
|
283
|
+
const range = new monaco.Range(
|
|
284
|
+
position.lineNumber,
|
|
285
|
+
bracketIdx + 3, // after `{[`
|
|
286
|
+
position.lineNumber,
|
|
287
|
+
position.column,
|
|
288
|
+
);
|
|
289
|
+
|
|
290
|
+
const top = suggestIcons(query, 50);
|
|
291
|
+
return {
|
|
292
|
+
suggestions: top.map((m, i) => ({
|
|
293
|
+
// Embed the FA codepoint as the first character of
|
|
294
|
+
// the label. CSS targets the suggest widget with
|
|
295
|
+
// FontAwesome as a font fallback, so the codepoint
|
|
296
|
+
// renders as the glyph and the name renders in the
|
|
297
|
+
// editor's normal font.
|
|
298
|
+
label: {
|
|
299
|
+
label: `${iconGlyph(m.entry)} ${m.token}`,
|
|
300
|
+
description: `fa-${m.entry.family}`,
|
|
301
|
+
},
|
|
302
|
+
// `filterText` excludes the glyph + spacing so Monaco
|
|
303
|
+
// filters against the actual icon name only.
|
|
304
|
+
filterText: m.token,
|
|
305
|
+
kind: monaco.languages.CompletionItemKind.Constant,
|
|
306
|
+
insertText: `${m.token}${suffix}`,
|
|
307
|
+
range,
|
|
308
|
+
detail: m.entry.label,
|
|
309
|
+
// Documentation pane (rendered when Monaco's
|
|
310
|
+
// suggestion preview is expanded) shows a large
|
|
311
|
+
// version of the glyph alongside the canonical token.
|
|
312
|
+
documentation: {
|
|
313
|
+
value: `<i class="fa-${m.entry.family} fa-${m.entry.name}" style="font-size: 2em; display: inline-block; margin-right: 8px; vertical-align: middle"></i> **${m.token}** *(${m.entry.label})*`,
|
|
314
|
+
isTrusted: true,
|
|
315
|
+
supportHtml: true,
|
|
316
|
+
},
|
|
317
|
+
// Sort key: 1-digit score prefix keeps "starts with"
|
|
318
|
+
// matches above "contains" / keyword matches.
|
|
319
|
+
sortText: `${m.score}${String(i).padStart(4, '0')}`,
|
|
320
|
+
})),
|
|
321
|
+
};
|
|
322
|
+
},
|
|
323
|
+
},
|
|
324
|
+
);
|
|
185
325
|
}
|
|
186
326
|
|
|
187
327
|
// Chat-composer mode: intercept Enter before Monaco inserts a newline.
|
|
@@ -257,6 +397,10 @@ export function RawEditor({
|
|
|
257
397
|
completionDisposable.current = null;
|
|
258
398
|
mentionCompletionDisposable.current?.dispose();
|
|
259
399
|
mentionCompletionDisposable.current = null;
|
|
400
|
+
iconCompletionDisposable.current?.dispose();
|
|
401
|
+
iconCompletionDisposable.current = null;
|
|
402
|
+
iconGlyphDecorations.current?.clear();
|
|
403
|
+
iconGlyphDecorations.current = null;
|
|
260
404
|
dropCleanupRef.current?.();
|
|
261
405
|
dropCleanupRef.current = null;
|
|
262
406
|
keyDisposable.current?.dispose();
|
|
@@ -287,12 +431,63 @@ export function RawEditor({
|
|
|
287
431
|
}
|
|
288
432
|
}, [markdownSource]);
|
|
289
433
|
|
|
434
|
+
// ── Inline FontAwesome glyph decorations ────────────
|
|
435
|
+
// Walk the markdown source on every change, find each resolvable
|
|
436
|
+
// `{[icon-name]}` span, and overlay the actual glyph (via Monaco's
|
|
437
|
+
// `before:` content decoration) just before the brackets. The CSS
|
|
438
|
+
// classes `.fa-glyph-decoration-<family>` set the per-family font
|
|
439
|
+
// and weight; the codepoint character is the decoration's content.
|
|
440
|
+
useEffect(() => {
|
|
441
|
+
const editor = editorRef.current;
|
|
442
|
+
if (!editor) return;
|
|
443
|
+
if (language !== 'markdown') return;
|
|
444
|
+
const model = editor.getModel();
|
|
445
|
+
if (!model) return;
|
|
446
|
+
|
|
447
|
+
const decorations: monaco.editor.IModelDeltaDecoration[] = [];
|
|
448
|
+
const lines = model.getLineCount();
|
|
449
|
+
const re = /\{\[([a-zA-Z0-9_:-]+)\]\}/g;
|
|
450
|
+
for (let line = 1; line <= lines; line++) {
|
|
451
|
+
const text = model.getLineContent(line);
|
|
452
|
+
re.lastIndex = 0;
|
|
453
|
+
let match: RegExpExecArray | null;
|
|
454
|
+
while ((match = re.exec(text)) !== null) {
|
|
455
|
+
const icon = resolveIcon(match[1]);
|
|
456
|
+
if (!icon) continue;
|
|
457
|
+
const glyph = iconGlyph(icon);
|
|
458
|
+
if (!glyph) continue;
|
|
459
|
+
// Position the decoration as a zero-width range at the `{`
|
|
460
|
+
// of the matched token. `before.contentText` renders the
|
|
461
|
+
// glyph as content prepended visually to that position.
|
|
462
|
+
const col = match.index + 1; // Monaco columns are 1-based
|
|
463
|
+
decorations.push({
|
|
464
|
+
range: new monaco.Range(line, col, line, col),
|
|
465
|
+
options: {
|
|
466
|
+
before: {
|
|
467
|
+
content: glyph,
|
|
468
|
+
inlineClassName: `fa-glyph-decoration-${icon.family}`,
|
|
469
|
+
},
|
|
470
|
+
},
|
|
471
|
+
});
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
if (!iconGlyphDecorations.current) {
|
|
476
|
+
iconGlyphDecorations.current = editor.createDecorationsCollection(decorations);
|
|
477
|
+
} else {
|
|
478
|
+
iconGlyphDecorations.current.set(decorations);
|
|
479
|
+
}
|
|
480
|
+
}, [markdownSource, language]);
|
|
481
|
+
|
|
482
|
+
const effectiveTheme = SQUISQ_THEMES[theme] ?? theme;
|
|
483
|
+
|
|
290
484
|
return (
|
|
291
485
|
<div className={className} style={{ width: '100%', height: '100%' }} data-testid="raw-editor">
|
|
292
486
|
<Editor
|
|
293
487
|
defaultLanguage={language}
|
|
294
488
|
value={markdownSource}
|
|
295
|
-
theme={
|
|
489
|
+
theme={effectiveTheme}
|
|
490
|
+
beforeMount={handleBeforeMount}
|
|
296
491
|
onMount={handleMount}
|
|
297
492
|
onChange={handleChange}
|
|
298
493
|
options={{
|
|
@@ -307,6 +502,22 @@ export function RawEditor({
|
|
|
307
502
|
bracketPairColorization: { enabled: true },
|
|
308
503
|
guides: { indentation: true },
|
|
309
504
|
padding: { top: 12, bottom: 12 },
|
|
505
|
+
// Markdown's tokenizer classifies most body text as "string"
|
|
506
|
+
// or "comment" context, which suppresses `quickSuggestions`
|
|
507
|
+
// by default. Enable in all three so our `{[icon]}` and
|
|
508
|
+
// `@mention` typeaheads keep firing as the user types past
|
|
509
|
+
// the trigger character.
|
|
510
|
+
quickSuggestions: { other: true, comments: true, strings: true },
|
|
511
|
+
suggestOnTriggerCharacters: true,
|
|
512
|
+
// Breathing room between the gutter and the first character.
|
|
513
|
+
// Done via Monaco's own option so cursor + hit-testing stay in
|
|
514
|
+
// sync — CSS-padding `.view-lines` shifts the text but not the
|
|
515
|
+
// cursors layer, which causes the cursor to drift left of the
|
|
516
|
+
// model column. Monaco's default is 10. We set 22 (12 extra),
|
|
517
|
+
// and CSS paints the rightmost 12px of the gutter as canvas
|
|
518
|
+
// color so the breathing room looks like it sits *inside* the
|
|
519
|
+
// canvas rather than widening the gutter.
|
|
520
|
+
lineDecorationsWidth: 22,
|
|
310
521
|
readOnly,
|
|
311
522
|
domReadOnly: readOnly,
|
|
312
523
|
}}
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* RecorderEntry — toolbar slot that wires `RecorderPanel` from
|
|
3
|
+
* `@bendyline/squisq-recorder-react` into the editor.
|
|
4
|
+
*
|
|
5
|
+
* Reads `mediaProvider`, `workspaceContainer`, `activeView`,
|
|
6
|
+
* `tiptapEditor`, and the markdown editing helpers from
|
|
7
|
+
* `useEditorContext()`. On a successful save it:
|
|
8
|
+
* - inserts an HTML5 media element at the cursor — `<video>` for
|
|
9
|
+
* camera / screen recordings, `<audio>` for narration / mic
|
|
10
|
+
* recordings — which the markdown renderer turns into an inline
|
|
11
|
+
* `<InlineVideoPlayer>` / `<InlineAudioPlayer>` with native
|
|
12
|
+
* controls;
|
|
13
|
+
* - for narration, also annotates the nearest heading with
|
|
14
|
+
* `{[audio=filename]}` so `resolveAudioMapping()` continues to tie
|
|
15
|
+
* the recording to that block during slideshow playback;
|
|
16
|
+
* - bumps `mediaRevision` so any blob URLs the media bin cached for
|
|
17
|
+
* the previous file list are invalidated.
|
|
18
|
+
*
|
|
19
|
+
* The component returns `null` when no `mediaProvider` is wired —
|
|
20
|
+
* recording without a place to write the captured bytes is a
|
|
21
|
+
* misconfiguration, not a feature.
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
import { useCallback } from 'react';
|
|
25
|
+
import { RecorderPanel } from './recorder/RecorderPanel.js';
|
|
26
|
+
import type { RecorderSaveResult } from './recorder/RecorderModal.js';
|
|
27
|
+
import { useEditorContext } from './EditorContext';
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Insert a narration annotation `{[audio=filename]}` onto the heading
|
|
31
|
+
* line currently above the cursor in the Monaco editor. Pure-text
|
|
32
|
+
* insertion at the line's end keeps the markdown valid and matches the
|
|
33
|
+
* shape `resolveAudioMapping()` reads. Returns true if a heading line
|
|
34
|
+
* was found and updated.
|
|
35
|
+
*/
|
|
36
|
+
function annotateMonacoHeading(
|
|
37
|
+
editor: NonNullable<ReturnType<typeof useEditorContext>['monacoEditor']>,
|
|
38
|
+
filename: string,
|
|
39
|
+
): boolean {
|
|
40
|
+
const model = editor.getModel();
|
|
41
|
+
if (!model) return false;
|
|
42
|
+
const position = editor.getPosition();
|
|
43
|
+
if (!position) return false;
|
|
44
|
+
// Walk upward from the cursor to find the nearest heading line.
|
|
45
|
+
for (let line = position.lineNumber; line >= 1; line--) {
|
|
46
|
+
const text = model.getLineContent(line);
|
|
47
|
+
if (!/^#{1,6}\s/.test(text)) continue;
|
|
48
|
+
// Skip if the heading already has an audio annotation — don't
|
|
49
|
+
// stomp the user's existing wiring. Add a separate annotation in
|
|
50
|
+
// that case wouldn't help either (resolveAudioMapping reads only
|
|
51
|
+
// the first audio= entry).
|
|
52
|
+
if (/\{\[audio=/.test(text)) return true;
|
|
53
|
+
const trimmed = text.replace(/\s+$/, '');
|
|
54
|
+
const insertText = ` {[audio=${filename}]}`;
|
|
55
|
+
const column = trimmed.length + 1;
|
|
56
|
+
editor.executeEdits('recorder-annotation', [
|
|
57
|
+
{
|
|
58
|
+
range: {
|
|
59
|
+
startLineNumber: line,
|
|
60
|
+
startColumn: column,
|
|
61
|
+
endLineNumber: line,
|
|
62
|
+
endColumn: column,
|
|
63
|
+
},
|
|
64
|
+
text: insertText,
|
|
65
|
+
},
|
|
66
|
+
]);
|
|
67
|
+
return true;
|
|
68
|
+
}
|
|
69
|
+
return false;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export function RecorderEntry() {
|
|
73
|
+
const {
|
|
74
|
+
mediaProvider,
|
|
75
|
+
workspaceContainer,
|
|
76
|
+
activeView,
|
|
77
|
+
monacoEditor,
|
|
78
|
+
tiptapEditor,
|
|
79
|
+
insertAtCursor,
|
|
80
|
+
bumpMediaRevision,
|
|
81
|
+
markdownSource,
|
|
82
|
+
setMarkdownSource,
|
|
83
|
+
} = useEditorContext();
|
|
84
|
+
|
|
85
|
+
const handleSave = useCallback(
|
|
86
|
+
(result: RecorderSaveResult) => {
|
|
87
|
+
bumpMediaRevision();
|
|
88
|
+
|
|
89
|
+
if (result.source === 'mic') {
|
|
90
|
+
// Narration — annotate the nearest heading (drives the
|
|
91
|
+
// slideshow narration pipeline at `core/src/doc/audioMapping.ts`)
|
|
92
|
+
// *and* insert an inline `<audio controls>` so the user can
|
|
93
|
+
// audition the recording inside the editor. The two roles are
|
|
94
|
+
// orthogonal: the annotation is metadata for playback timing,
|
|
95
|
+
// the HTML tag is an in-editor preview control.
|
|
96
|
+
if (activeView === 'raw' && monacoEditor) {
|
|
97
|
+
annotateMonacoHeading(monacoEditor, result.filename);
|
|
98
|
+
}
|
|
99
|
+
const audioTag = `<audio src="${result.relativePath}" controls></audio>`;
|
|
100
|
+
if (activeView === 'wysiwyg' && tiptapEditor) {
|
|
101
|
+
tiptapEditor
|
|
102
|
+
.chain()
|
|
103
|
+
.focus()
|
|
104
|
+
.insertContent({
|
|
105
|
+
type: 'audio',
|
|
106
|
+
attrs: { src: result.relativePath, controls: true },
|
|
107
|
+
})
|
|
108
|
+
.run();
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
if (activeView === 'raw' && monacoEditor) {
|
|
112
|
+
// The annotation went onto the heading line; drop the player
|
|
113
|
+
// on a fresh line at the cursor.
|
|
114
|
+
insertAtCursor(`\n\n${audioTag}\n`);
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
setMarkdownSource(markdownSource ? `${markdownSource}\n\n${audioTag}` : audioTag);
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// Camera / screen / screen+mic — inline video player. We pin
|
|
122
|
+
// width=480 so the editor preview doesn't blow up to natural
|
|
123
|
+
// resolution on big monitors; the height is intrinsic, so the
|
|
124
|
+
// aspect ratio is preserved regardless of source dimensions.
|
|
125
|
+
const videoTag = `<video src="${result.relativePath}" controls width="480"></video>`;
|
|
126
|
+
if (activeView === 'wysiwyg' && tiptapEditor) {
|
|
127
|
+
tiptapEditor
|
|
128
|
+
.chain()
|
|
129
|
+
.focus()
|
|
130
|
+
.insertContent({
|
|
131
|
+
type: 'video',
|
|
132
|
+
attrs: { src: result.relativePath, controls: true, width: 480 },
|
|
133
|
+
})
|
|
134
|
+
.run();
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
if (activeView === 'raw' && monacoEditor) {
|
|
138
|
+
insertAtCursor(videoTag);
|
|
139
|
+
return;
|
|
140
|
+
}
|
|
141
|
+
setMarkdownSource(markdownSource ? `${markdownSource}\n\n${videoTag}` : videoTag);
|
|
142
|
+
},
|
|
143
|
+
[
|
|
144
|
+
activeView,
|
|
145
|
+
monacoEditor,
|
|
146
|
+
tiptapEditor,
|
|
147
|
+
insertAtCursor,
|
|
148
|
+
bumpMediaRevision,
|
|
149
|
+
markdownSource,
|
|
150
|
+
setMarkdownSource,
|
|
151
|
+
],
|
|
152
|
+
);
|
|
153
|
+
|
|
154
|
+
if (!mediaProvider) return null;
|
|
155
|
+
|
|
156
|
+
return (
|
|
157
|
+
<RecorderPanel
|
|
158
|
+
mediaProvider={mediaProvider}
|
|
159
|
+
container={workspaceContainer}
|
|
160
|
+
onSave={handleSave}
|
|
161
|
+
className="squisq-toolbar-button"
|
|
162
|
+
/>
|
|
163
|
+
);
|
|
164
|
+
}
|
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
*/
|
|
15
15
|
|
|
16
16
|
import Heading from '@tiptap/extension-heading';
|
|
17
|
+
import { templateLabel } from './TemplatePicker';
|
|
17
18
|
|
|
18
19
|
/**
|
|
19
20
|
* HeadingWithTemplate — drop-in replacement for Tiptap's Heading that
|
|
@@ -47,11 +48,17 @@ export const HeadingWithTemplate = Heading.extend({
|
|
|
47
48
|
const tag = `h${level}`;
|
|
48
49
|
const templateName = HTMLAttributes['data-template'];
|
|
49
50
|
|
|
51
|
+
// Render heading with a trailing badge span. The badge has no text
|
|
52
|
+
// content — its label is painted via CSS `content: attr(data-template-label)`
|
|
53
|
+
// so the template name never becomes part of the serialized heading
|
|
54
|
+
// text (which would leak into markdown on round-trip).
|
|
55
|
+
//
|
|
56
|
+
// When no template is set we still render a subtle "empty" badge so
|
|
57
|
+
// authors have a visible affordance for opening the template picker
|
|
58
|
+
// straight from the heading (matches the clicky chip shown for
|
|
59
|
+
// templated headings). The empty variant has no `data-template`
|
|
60
|
+
// attribute, so the bridge treats the heading as plain on save.
|
|
50
61
|
if (templateName) {
|
|
51
|
-
// Render heading with a trailing badge span. The badge has no text
|
|
52
|
-
// content — its label is painted via CSS `content: attr(data-template)`
|
|
53
|
-
// so the template name never becomes part of the serialized heading
|
|
54
|
-
// text (which would leak into markdown on round-trip).
|
|
55
62
|
return [
|
|
56
63
|
tag,
|
|
57
64
|
HTMLAttributes,
|
|
@@ -61,13 +68,32 @@ export const HeadingWithTemplate = Heading.extend({
|
|
|
61
68
|
{
|
|
62
69
|
class: 'squisq-template-badge',
|
|
63
70
|
contenteditable: 'false',
|
|
71
|
+
role: 'button',
|
|
72
|
+
tabindex: '0',
|
|
73
|
+
'aria-haspopup': 'listbox',
|
|
74
|
+
title: 'Change block template',
|
|
64
75
|
'data-template': templateName,
|
|
76
|
+
'data-template-label': templateLabel(templateName),
|
|
65
77
|
},
|
|
66
78
|
],
|
|
67
79
|
];
|
|
68
80
|
}
|
|
69
81
|
|
|
70
|
-
|
|
71
|
-
|
|
82
|
+
return [
|
|
83
|
+
tag,
|
|
84
|
+
HTMLAttributes,
|
|
85
|
+
['span', { class: 'squisq-heading-content' }, 0],
|
|
86
|
+
[
|
|
87
|
+
'span',
|
|
88
|
+
{
|
|
89
|
+
class: 'squisq-template-badge squisq-template-badge--empty',
|
|
90
|
+
contenteditable: 'false',
|
|
91
|
+
role: 'button',
|
|
92
|
+
tabindex: '0',
|
|
93
|
+
'aria-haspopup': 'listbox',
|
|
94
|
+
title: 'Choose block template',
|
|
95
|
+
},
|
|
96
|
+
],
|
|
97
|
+
];
|
|
72
98
|
},
|
|
73
99
|
});
|