@bendyline/squisq-editor-react 1.5.0 → 1.5.2
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/index.d.ts +1991 -90
- package/dist/index.js +17088 -82
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
- package/dist/DocumentSettingsDialog.d.ts +0 -26
- package/dist/DocumentSettingsDialog.d.ts.map +0 -1
- package/dist/DocumentSettingsDialog.js +0 -115
- package/dist/DocumentSettingsDialog.js.map +0 -1
- package/dist/DropZoneOverlay.d.ts +0 -24
- package/dist/DropZoneOverlay.d.ts.map +0 -1
- package/dist/DropZoneOverlay.js +0 -53
- package/dist/DropZoneOverlay.js.map +0 -1
- package/dist/EditorContext.d.ts +0 -391
- package/dist/EditorContext.d.ts.map +0 -1
- package/dist/EditorContext.js +0 -471
- package/dist/EditorContext.js.map +0 -1
- package/dist/EditorShell.d.ts +0 -328
- package/dist/EditorShell.d.ts.map +0 -1
- package/dist/EditorShell.js +0 -290
- package/dist/EditorShell.js.map +0 -1
- package/dist/EmojiPicker.d.ts +0 -50
- package/dist/EmojiPicker.d.ts.map +0 -1
- package/dist/EmojiPicker.js +0 -182
- package/dist/EmojiPicker.js.map +0 -1
- package/dist/ImageEditor.d.ts +0 -68
- package/dist/ImageEditor.d.ts.map +0 -1
- package/dist/ImageEditor.js +0 -166
- package/dist/ImageEditor.js.map +0 -1
- package/dist/ImageNodeView.d.ts +0 -27
- package/dist/ImageNodeView.d.ts.map +0 -1
- package/dist/ImageNodeView.js +0 -215
- package/dist/ImageNodeView.js.map +0 -1
- package/dist/ImageViewer.d.ts +0 -26
- package/dist/ImageViewer.d.ts.map +0 -1
- package/dist/ImageViewer.js +0 -119
- package/dist/ImageViewer.js.map +0 -1
- package/dist/InlineIcon.d.ts +0 -17
- package/dist/InlineIcon.d.ts.map +0 -1
- package/dist/InlineIcon.js +0 -72
- package/dist/InlineIcon.js.map +0 -1
- package/dist/InlinePreviewGutter.d.ts +0 -52
- package/dist/InlinePreviewGutter.d.ts.map +0 -1
- package/dist/InlinePreviewGutter.js +0 -397
- package/dist/InlinePreviewGutter.js.map +0 -1
- package/dist/LinkDialog.d.ts +0 -43
- package/dist/LinkDialog.d.ts.map +0 -1
- package/dist/LinkDialog.js +0 -102
- package/dist/LinkDialog.js.map +0 -1
- package/dist/MediaBin.d.ts +0 -29
- package/dist/MediaBin.d.ts.map +0 -1
- package/dist/MediaBin.js +0 -166
- package/dist/MediaBin.js.map +0 -1
- package/dist/MentionExtension.d.ts +0 -22
- package/dist/MentionExtension.d.ts.map +0 -1
- package/dist/MentionExtension.js +0 -245
- package/dist/MentionExtension.js.map +0 -1
- package/dist/OutlinePanel.d.ts +0 -17
- package/dist/OutlinePanel.d.ts.map +0 -1
- package/dist/OutlinePanel.js +0 -167
- package/dist/OutlinePanel.js.map +0 -1
- package/dist/PlainHtmlPreview.d.ts +0 -50
- package/dist/PlainHtmlPreview.d.ts.map +0 -1
- package/dist/PlainHtmlPreview.js +0 -155
- package/dist/PlainHtmlPreview.js.map +0 -1
- package/dist/PreviewControls.d.ts +0 -55
- package/dist/PreviewControls.d.ts.map +0 -1
- package/dist/PreviewControls.js +0 -277
- package/dist/PreviewControls.js.map +0 -1
- package/dist/PreviewPanel.d.ts +0 -29
- package/dist/PreviewPanel.d.ts.map +0 -1
- package/dist/PreviewPanel.js +0 -94
- package/dist/PreviewPanel.js.map +0 -1
- package/dist/RawEditor.d.ts +0 -32
- package/dist/RawEditor.d.ts.map +0 -1
- package/dist/RawEditor.js +0 -440
- package/dist/RawEditor.js.map +0 -1
- package/dist/RecorderEntry.d.ts +0 -24
- package/dist/RecorderEntry.d.ts.map +0 -1
- package/dist/RecorderEntry.js +0 -139
- package/dist/RecorderEntry.js.map +0 -1
- package/dist/StatusBar.d.ts +0 -15
- package/dist/StatusBar.d.ts.map +0 -1
- package/dist/StatusBar.js +0 -24
- package/dist/StatusBar.js.map +0 -1
- package/dist/TemplateAnnotation.d.ts +0 -20
- package/dist/TemplateAnnotation.d.ts.map +0 -1
- package/dist/TemplateAnnotation.js +0 -97
- package/dist/TemplateAnnotation.js.map +0 -1
- package/dist/TemplatePicker.d.ts +0 -53
- package/dist/TemplatePicker.d.ts.map +0 -1
- package/dist/TemplatePicker.js +0 -388
- package/dist/TemplatePicker.js.map +0 -1
- package/dist/ThemeCustomizerPanel.d.ts +0 -32
- package/dist/ThemeCustomizerPanel.d.ts.map +0 -1
- package/dist/ThemeCustomizerPanel.js +0 -256
- package/dist/ThemeCustomizerPanel.js.map +0 -1
- package/dist/ThemePicker.d.ts +0 -33
- package/dist/ThemePicker.d.ts.map +0 -1
- package/dist/ThemePicker.js +0 -148
- package/dist/ThemePicker.js.map +0 -1
- package/dist/Toolbar.d.ts +0 -36
- package/dist/Toolbar.d.ts.map +0 -1
- package/dist/Toolbar.js +0 -1001
- package/dist/Toolbar.js.map +0 -1
- package/dist/Tooltip.d.ts +0 -10
- package/dist/Tooltip.d.ts.map +0 -1
- package/dist/Tooltip.js +0 -104
- package/dist/Tooltip.js.map +0 -1
- package/dist/VersionHistoryPanel.d.ts +0 -14
- package/dist/VersionHistoryPanel.d.ts.map +0 -1
- package/dist/VersionHistoryPanel.js +0 -147
- package/dist/VersionHistoryPanel.js.map +0 -1
- package/dist/ViewMenuPanel.d.ts +0 -13
- package/dist/ViewMenuPanel.d.ts.map +0 -1
- package/dist/ViewMenuPanel.js +0 -58
- package/dist/ViewMenuPanel.js.map +0 -1
- package/dist/ViewSwitcher.d.ts +0 -14
- package/dist/ViewSwitcher.d.ts.map +0 -1
- package/dist/ViewSwitcher.js +0 -26
- package/dist/ViewSwitcher.js.map +0 -1
- package/dist/WysiwygEditor.d.ts +0 -39
- package/dist/WysiwygEditor.d.ts.map +0 -1
- package/dist/WysiwygEditor.js +0 -537
- package/dist/WysiwygEditor.js.map +0 -1
- package/dist/__tests__/detectMarkdown.test.d.ts +0 -2
- package/dist/__tests__/detectMarkdown.test.d.ts.map +0 -1
- package/dist/__tests__/detectMarkdown.test.js +0 -55
- package/dist/__tests__/detectMarkdown.test.js.map +0 -1
- package/dist/__tests__/documentSettingsDialog.test.d.ts +0 -2
- package/dist/__tests__/documentSettingsDialog.test.d.ts.map +0 -1
- package/dist/__tests__/documentSettingsDialog.test.js +0 -132
- package/dist/__tests__/documentSettingsDialog.test.js.map +0 -1
- package/dist/__tests__/emojiPicker.test.d.ts +0 -2
- package/dist/__tests__/emojiPicker.test.d.ts.map +0 -1
- package/dist/__tests__/emojiPicker.test.js +0 -111
- package/dist/__tests__/emojiPicker.test.js.map +0 -1
- package/dist/__tests__/fileKind.test.d.ts +0 -2
- package/dist/__tests__/fileKind.test.d.ts.map +0 -1
- package/dist/__tests__/fileKind.test.js +0 -94
- package/dist/__tests__/fileKind.test.js.map +0 -1
- package/dist/__tests__/imageEditAffordance.test.d.ts +0 -2
- package/dist/__tests__/imageEditAffordance.test.d.ts.map +0 -1
- package/dist/__tests__/imageEditAffordance.test.js +0 -188
- package/dist/__tests__/imageEditAffordance.test.js.map +0 -1
- package/dist/__tests__/imageEditorShell.test.d.ts +0 -2
- package/dist/__tests__/imageEditorShell.test.d.ts.map +0 -1
- package/dist/__tests__/imageEditorShell.test.js +0 -52
- package/dist/__tests__/imageEditorShell.test.js.map +0 -1
- package/dist/__tests__/imageEditorState.test.d.ts +0 -3
- package/dist/__tests__/imageEditorState.test.d.ts.map +0 -1
- package/dist/__tests__/imageEditorState.test.js +0 -148
- package/dist/__tests__/imageEditorState.test.js.map +0 -1
- package/dist/__tests__/inlinePreviewGutter.test.d.ts +0 -2
- package/dist/__tests__/inlinePreviewGutter.test.d.ts.map +0 -1
- package/dist/__tests__/inlinePreviewGutter.test.js +0 -51
- package/dist/__tests__/inlinePreviewGutter.test.js.map +0 -1
- package/dist/__tests__/inlinePreviewGutterAllBlocks.test.d.ts +0 -2
- package/dist/__tests__/inlinePreviewGutterAllBlocks.test.d.ts.map +0 -1
- package/dist/__tests__/inlinePreviewGutterAllBlocks.test.js +0 -63
- package/dist/__tests__/inlinePreviewGutterAllBlocks.test.js.map +0 -1
- package/dist/__tests__/jsonEditor.test.d.ts +0 -2
- package/dist/__tests__/jsonEditor.test.d.ts.map +0 -1
- package/dist/__tests__/jsonEditor.test.js +0 -134
- package/dist/__tests__/jsonEditor.test.js.map +0 -1
- package/dist/__tests__/layersPanel.test.d.ts +0 -2
- package/dist/__tests__/layersPanel.test.d.ts.map +0 -1
- package/dist/__tests__/layersPanel.test.js +0 -84
- package/dist/__tests__/layersPanel.test.js.map +0 -1
- package/dist/__tests__/linkDialogDocPicker.test.d.ts +0 -7
- package/dist/__tests__/linkDialogDocPicker.test.d.ts.map +0 -1
- package/dist/__tests__/linkDialogDocPicker.test.js +0 -75
- package/dist/__tests__/linkDialogDocPicker.test.js.map +0 -1
- package/dist/__tests__/mediaAttachmentFlow.test.d.ts +0 -2
- package/dist/__tests__/mediaAttachmentFlow.test.d.ts.map +0 -1
- package/dist/__tests__/mediaAttachmentFlow.test.js +0 -99
- package/dist/__tests__/mediaAttachmentFlow.test.js.map +0 -1
- package/dist/__tests__/outlinePanel.test.d.ts +0 -2
- package/dist/__tests__/outlinePanel.test.d.ts.map +0 -1
- package/dist/__tests__/outlinePanel.test.js +0 -68
- package/dist/__tests__/outlinePanel.test.js.map +0 -1
- package/dist/__tests__/plainHtmlPreview.test.d.ts +0 -2
- package/dist/__tests__/plainHtmlPreview.test.d.ts.map +0 -1
- package/dist/__tests__/plainHtmlPreview.test.js +0 -87
- package/dist/__tests__/plainHtmlPreview.test.js.map +0 -1
- package/dist/__tests__/propertiesPanel.test.d.ts +0 -2
- package/dist/__tests__/propertiesPanel.test.d.ts.map +0 -1
- package/dist/__tests__/propertiesPanel.test.js +0 -64
- package/dist/__tests__/propertiesPanel.test.js.map +0 -1
- package/dist/__tests__/recorderFormats.test.d.ts +0 -2
- package/dist/__tests__/recorderFormats.test.d.ts.map +0 -1
- package/dist/__tests__/recorderFormats.test.js +0 -121
- package/dist/__tests__/recorderFormats.test.js.map +0 -1
- package/dist/__tests__/recorderTimingJson.test.d.ts +0 -2
- package/dist/__tests__/recorderTimingJson.test.d.ts.map +0 -1
- package/dist/__tests__/recorderTimingJson.test.js +0 -37
- package/dist/__tests__/recorderTimingJson.test.js.map +0 -1
- package/dist/__tests__/templateAnnotationRoundTrip.test.d.ts +0 -2
- package/dist/__tests__/templateAnnotationRoundTrip.test.d.ts.map +0 -1
- package/dist/__tests__/templateAnnotationRoundTrip.test.js +0 -31
- package/dist/__tests__/templateAnnotationRoundTrip.test.js.map +0 -1
- package/dist/__tests__/tiptapBridge.test.d.ts +0 -2
- package/dist/__tests__/tiptapBridge.test.d.ts.map +0 -1
- package/dist/__tests__/tiptapBridge.test.js +0 -303
- package/dist/__tests__/tiptapBridge.test.js.map +0 -1
- package/dist/__tests__/tiptapImageRoundTrip.test.d.ts +0 -2
- package/dist/__tests__/tiptapImageRoundTrip.test.d.ts.map +0 -1
- package/dist/__tests__/tiptapImageRoundTrip.test.js +0 -68
- package/dist/__tests__/tiptapImageRoundTrip.test.js.map +0 -1
- package/dist/__tests__/useImageEditor.test.d.ts +0 -2
- package/dist/__tests__/useImageEditor.test.d.ts.map +0 -1
- package/dist/__tests__/useImageEditor.test.js +0 -131
- package/dist/__tests__/useImageEditor.test.js.map +0 -1
- package/dist/__tests__/useMediaRecorder.test.d.ts +0 -2
- package/dist/__tests__/useMediaRecorder.test.d.ts.map +0 -1
- package/dist/__tests__/useMediaRecorder.test.js +0 -153
- package/dist/__tests__/useMediaRecorder.test.js.map +0 -1
- package/dist/__tests__/versionHistory.test.d.ts +0 -2
- package/dist/__tests__/versionHistory.test.d.ts.map +0 -1
- package/dist/__tests__/versionHistory.test.js +0 -124
- package/dist/__tests__/versionHistory.test.js.map +0 -1
- package/dist/blockSlice.d.ts +0 -24
- package/dist/blockSlice.d.ts.map +0 -1
- package/dist/blockSlice.js +0 -63
- package/dist/blockSlice.js.map +0 -1
- package/dist/buildPreviewDoc.d.ts +0 -22
- package/dist/buildPreviewDoc.d.ts.map +0 -1
- package/dist/buildPreviewDoc.js +0 -262
- package/dist/buildPreviewDoc.js.map +0 -1
- package/dist/detectMarkdown.d.ts +0 -20
- package/dist/detectMarkdown.d.ts.map +0 -1
- package/dist/detectMarkdown.js +0 -61
- package/dist/detectMarkdown.js.map +0 -1
- package/dist/emojiData.d.ts +0 -81
- package/dist/emojiData.d.ts.map +0 -1
- package/dist/emojiData.js +0 -1283
- package/dist/emojiData.js.map +0 -1
- package/dist/fileKind.d.ts +0 -34
- package/dist/fileKind.d.ts.map +0 -1
- package/dist/fileKind.js +0 -144
- package/dist/fileKind.js.map +0 -1
- package/dist/hooks/useFileDrop.d.ts +0 -41
- package/dist/hooks/useFileDrop.d.ts.map +0 -1
- package/dist/hooks/useFileDrop.js +0 -205
- package/dist/hooks/useFileDrop.js.map +0 -1
- package/dist/imageEditor/CanvasSurface.d.ts +0 -31
- package/dist/imageEditor/CanvasSurface.d.ts.map +0 -1
- package/dist/imageEditor/CanvasSurface.js +0 -264
- package/dist/imageEditor/CanvasSurface.js.map +0 -1
- package/dist/imageEditor/ImageVersionHistoryDropdown.d.ts +0 -39
- package/dist/imageEditor/ImageVersionHistoryDropdown.d.ts.map +0 -1
- package/dist/imageEditor/ImageVersionHistoryDropdown.js +0 -283
- package/dist/imageEditor/ImageVersionHistoryDropdown.js.map +0 -1
- package/dist/imageEditor/LayersPanel.d.ts +0 -14
- package/dist/imageEditor/LayersPanel.d.ts.map +0 -1
- package/dist/imageEditor/LayersPanel.js +0 -43
- package/dist/imageEditor/LayersPanel.js.map +0 -1
- package/dist/imageEditor/PropertiesPanel.d.ts +0 -14
- package/dist/imageEditor/PropertiesPanel.d.ts.map +0 -1
- package/dist/imageEditor/PropertiesPanel.js +0 -97
- package/dist/imageEditor/PropertiesPanel.js.map +0 -1
- package/dist/imageEditor/Toolbar.d.ts +0 -30
- package/dist/imageEditor/Toolbar.d.ts.map +0 -1
- package/dist/imageEditor/Toolbar.js +0 -108
- package/dist/imageEditor/Toolbar.js.map +0 -1
- package/dist/imageEditor/icons.d.ts +0 -24
- package/dist/imageEditor/icons.d.ts.map +0 -1
- package/dist/imageEditor/icons.js +0 -45
- package/dist/imageEditor/icons.js.map +0 -1
- package/dist/imageEditor/layers/EditorImageLayer.d.ts +0 -16
- package/dist/imageEditor/layers/EditorImageLayer.d.ts.map +0 -1
- package/dist/imageEditor/layers/EditorImageLayer.js +0 -37
- package/dist/imageEditor/layers/EditorImageLayer.js.map +0 -1
- package/dist/imageEditor/layers/EditorShapeLayer.d.ts +0 -15
- package/dist/imageEditor/layers/EditorShapeLayer.d.ts.map +0 -1
- package/dist/imageEditor/layers/EditorShapeLayer.js +0 -20
- package/dist/imageEditor/layers/EditorShapeLayer.js.map +0 -1
- package/dist/imageEditor/layers/EditorTextLayer.d.ts +0 -18
- package/dist/imageEditor/layers/EditorTextLayer.d.ts.map +0 -1
- package/dist/imageEditor/layers/EditorTextLayer.js +0 -13
- package/dist/imageEditor/layers/EditorTextLayer.js.map +0 -1
- package/dist/imageEditor/layers/SelectionHandles.d.ts +0 -17
- package/dist/imageEditor/layers/SelectionHandles.d.ts.map +0 -1
- package/dist/imageEditor/layers/SelectionHandles.js +0 -19
- package/dist/imageEditor/layers/SelectionHandles.js.map +0 -1
- package/dist/imageEditor/state.d.ts +0 -76
- package/dist/imageEditor/state.d.ts.map +0 -1
- package/dist/imageEditor/state.js +0 -87
- package/dist/imageEditor/state.js.map +0 -1
- package/dist/imageEditor/useImageEditor.d.ts +0 -53
- package/dist/imageEditor/useImageEditor.d.ts.map +0 -1
- package/dist/imageEditor/useImageEditor.js +0 -244
- package/dist/imageEditor/useImageEditor.js.map +0 -1
- package/dist/imageEditor/useImageEditorTokens.d.ts +0 -16
- package/dist/imageEditor/useImageEditorTokens.d.ts.map +0 -1
- package/dist/imageEditor/useImageEditorTokens.js +0 -45
- package/dist/imageEditor/useImageEditorTokens.js.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/jsonEditor/EmbeddedRichTextField.d.ts +0 -15
- package/dist/jsonEditor/EmbeddedRichTextField.d.ts.map +0 -1
- package/dist/jsonEditor/EmbeddedRichTextField.js +0 -74
- package/dist/jsonEditor/EmbeddedRichTextField.js.map +0 -1
- package/dist/jsonEditor/JsonEditor.d.ts +0 -36
- package/dist/jsonEditor/JsonEditor.d.ts.map +0 -1
- package/dist/jsonEditor/JsonEditor.js +0 -15
- package/dist/jsonEditor/JsonEditor.js.map +0 -1
- package/dist/jsonEditor/JsonEditorContext.d.ts +0 -28
- package/dist/jsonEditor/JsonEditorContext.d.ts.map +0 -1
- package/dist/jsonEditor/JsonEditorContext.js +0 -41
- package/dist/jsonEditor/JsonEditorContext.js.map +0 -1
- package/dist/jsonEditor/RenderNode.d.ts +0 -16
- package/dist/jsonEditor/RenderNode.d.ts.map +0 -1
- package/dist/jsonEditor/RenderNode.js +0 -32
- package/dist/jsonEditor/RenderNode.js.map +0 -1
- package/dist/jsonEditor/editors.d.ts +0 -36
- package/dist/jsonEditor/editors.d.ts.map +0 -1
- package/dist/jsonEditor/editors.js +0 -347
- package/dist/jsonEditor/editors.js.map +0 -1
- package/dist/jsonEditor/index.d.ts +0 -3
- package/dist/jsonEditor/index.d.ts.map +0 -1
- package/dist/jsonEditor/index.js +0 -2
- package/dist/jsonEditor/index.js.map +0 -1
- package/dist/jsonEditor/useJsonEditorTokens.d.ts +0 -13
- package/dist/jsonEditor/useJsonEditorTokens.d.ts.map +0 -1
- package/dist/jsonEditor/useJsonEditorTokens.js +0 -38
- package/dist/jsonEditor/useJsonEditorTokens.js.map +0 -1
- package/dist/mediaDragMime.d.ts +0 -17
- package/dist/mediaDragMime.d.ts.map +0 -1
- package/dist/mediaDragMime.js +0 -22
- package/dist/mediaDragMime.js.map +0 -1
- package/dist/recorder/RecorderButton.d.ts +0 -31
- package/dist/recorder/RecorderButton.d.ts.map +0 -1
- package/dist/recorder/RecorderButton.js +0 -24
- package/dist/recorder/RecorderButton.js.map +0 -1
- package/dist/recorder/RecorderModal.d.ts +0 -59
- package/dist/recorder/RecorderModal.d.ts.map +0 -1
- package/dist/recorder/RecorderModal.js +0 -333
- package/dist/recorder/RecorderModal.js.map +0 -1
- package/dist/recorder/RecorderPanel.d.ts +0 -25
- package/dist/recorder/RecorderPanel.d.ts.map +0 -1
- package/dist/recorder/RecorderPanel.js +0 -30
- package/dist/recorder/RecorderPanel.js.map +0 -1
- package/dist/recorder/formats.d.ts +0 -51
- package/dist/recorder/formats.d.ts.map +0 -1
- package/dist/recorder/formats.js +0 -144
- package/dist/recorder/formats.js.map +0 -1
- package/dist/recorder/hooks/useMediaRecorder.d.ts +0 -90
- package/dist/recorder/hooks/useMediaRecorder.d.ts.map +0 -1
- package/dist/recorder/hooks/useMediaRecorder.js +0 -277
- package/dist/recorder/hooks/useMediaRecorder.js.map +0 -1
- package/dist/recorder/hooks/useStreamPreview.d.ts +0 -22
- package/dist/recorder/hooks/useStreamPreview.d.ts.map +0 -1
- package/dist/recorder/hooks/useStreamPreview.js +0 -44
- package/dist/recorder/hooks/useStreamPreview.js.map +0 -1
- package/dist/recorder/sources/cameraStream.d.ts +0 -22
- package/dist/recorder/sources/cameraStream.d.ts.map +0 -1
- package/dist/recorder/sources/cameraStream.js +0 -24
- package/dist/recorder/sources/cameraStream.js.map +0 -1
- package/dist/recorder/sources/micStream.d.ts +0 -15
- package/dist/recorder/sources/micStream.d.ts.map +0 -1
- package/dist/recorder/sources/micStream.js +0 -24
- package/dist/recorder/sources/micStream.js.map +0 -1
- package/dist/recorder/sources/screenStream.d.ts +0 -53
- package/dist/recorder/sources/screenStream.d.ts.map +0 -1
- package/dist/recorder/sources/screenStream.js +0 -114
- package/dist/recorder/sources/screenStream.js.map +0 -1
- package/dist/recorder/timingJson.d.ts +0 -51
- package/dist/recorder/timingJson.d.ts.map +0 -1
- package/dist/recorder/timingJson.js +0 -42
- package/dist/recorder/timingJson.js.map +0 -1
- package/dist/tiptap/TiptapAudio.d.ts +0 -26
- package/dist/tiptap/TiptapAudio.d.ts.map +0 -1
- package/dist/tiptap/TiptapAudio.js +0 -58
- package/dist/tiptap/TiptapAudio.js.map +0 -1
- package/dist/tiptap/TiptapVideo.d.ts +0 -30
- package/dist/tiptap/TiptapVideo.d.ts.map +0 -1
- package/dist/tiptap/TiptapVideo.js +0 -66
- package/dist/tiptap/TiptapVideo.js.map +0 -1
- package/dist/tiptap/useResolvedMediaSrc.d.ts +0 -2
- package/dist/tiptap/useResolvedMediaSrc.d.ts.map +0 -1
- package/dist/tiptap/useResolvedMediaSrc.js +0 -42
- package/dist/tiptap/useResolvedMediaSrc.js.map +0 -1
- package/dist/tiptapBridge.d.ts +0 -24
- package/dist/tiptapBridge.d.ts.map +0 -1
- package/dist/tiptapBridge.js +0 -749
- package/dist/tiptapBridge.js.map +0 -1
- package/dist/useHeadingLayout.d.ts +0 -54
- package/dist/useHeadingLayout.d.ts.map +0 -1
- package/dist/useHeadingLayout.js +0 -260
- package/dist/useHeadingLayout.js.map +0 -1
- package/dist/utils/collectInlineFontAwesomeCss.d.ts +0 -21
- package/dist/utils/collectInlineFontAwesomeCss.d.ts.map +0 -1
- package/dist/utils/collectInlineFontAwesomeCss.js +0 -68
- package/dist/utils/collectInlineFontAwesomeCss.js.map +0 -1
- package/dist/utils/dropUtils.d.ts +0 -55
- package/dist/utils/dropUtils.d.ts.map +0 -1
- package/dist/utils/dropUtils.js +0 -110
- package/dist/utils/dropUtils.js.map +0 -1
- package/dist/utils/normalizeMalformedAssetUrl.d.ts +0 -15
- package/dist/utils/normalizeMalformedAssetUrl.d.ts.map +0 -1
- package/dist/utils/normalizeMalformedAssetUrl.js +0 -27
- package/dist/utils/normalizeMalformedAssetUrl.js.map +0 -1
package/dist/Toolbar.js
DELETED
|
@@ -1,1001 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
-
import { useCallback, useEffect, useMemo, useReducer, useRef, useState } from 'react';
|
|
3
|
-
import { useEditorContext } from './EditorContext';
|
|
4
|
-
import { VersionHistoryPanel } from './VersionHistoryPanel';
|
|
5
|
-
import { RecorderEntry } from './RecorderEntry';
|
|
6
|
-
import { ViewMenuPanel } from './ViewMenuPanel';
|
|
7
|
-
import { TemplatePicker, TEMPLATE_NAMES } from './TemplatePicker';
|
|
8
|
-
import { profileBlockContents, recommendTemplatesForBlock } from '@bendyline/squisq/recommend';
|
|
9
|
-
import { findBlockSliceAtLine, findBlockSliceByHeadingIndex } from './blockSlice';
|
|
10
|
-
import { LinkDialog } from './LinkDialog';
|
|
11
|
-
import { DocumentSettingsDialog } from './DocumentSettingsDialog';
|
|
12
|
-
import { EmojiPicker, EMOJI_PICKER_WIDTH, EMOJI_PICKER_MAX_HEIGHT } from './EmojiPicker';
|
|
13
|
-
import { createPortal } from 'react-dom';
|
|
14
|
-
const VIEWS = [
|
|
15
|
-
{ id: 'wysiwyg', label: 'Editor', shortcut: '⌘1' },
|
|
16
|
-
{ id: 'raw', label: 'Markdown', shortLabel: 'MD', shortcut: '⌘2' },
|
|
17
|
-
{ id: 'preview', label: 'Play', shortcut: '⌘3' },
|
|
18
|
-
];
|
|
19
|
-
const BUTTONS = [
|
|
20
|
-
// Format group — B/I/S trio.
|
|
21
|
-
{
|
|
22
|
-
id: 'bold',
|
|
23
|
-
label: 'B',
|
|
24
|
-
icon: 'B',
|
|
25
|
-
title: 'Bold (Ctrl+B)',
|
|
26
|
-
group: 'format',
|
|
27
|
-
iconStyle: { fontWeight: 700 },
|
|
28
|
-
},
|
|
29
|
-
{
|
|
30
|
-
id: 'italic',
|
|
31
|
-
label: 'I',
|
|
32
|
-
icon: 'I',
|
|
33
|
-
title: 'Italic (Ctrl+I)',
|
|
34
|
-
group: 'format',
|
|
35
|
-
iconStyle: { fontStyle: 'italic' },
|
|
36
|
-
},
|
|
37
|
-
{
|
|
38
|
-
id: 'strikethrough',
|
|
39
|
-
label: 'S',
|
|
40
|
-
icon: 'S',
|
|
41
|
-
title: 'Strikethrough',
|
|
42
|
-
group: 'format',
|
|
43
|
-
iconStyle: { textDecoration: 'line-through' },
|
|
44
|
-
},
|
|
45
|
-
// Lists group — sits between format and structure so bullets/numbers
|
|
46
|
-
// are adjacent to the inline formatters people reach for together.
|
|
47
|
-
{ id: 'ul', label: '•', icon: '•', title: 'Bullet list', group: 'lists' },
|
|
48
|
-
{ id: 'ol', label: '1.', icon: '1.', title: 'Numbered list', group: 'lists' },
|
|
49
|
-
// Structure group
|
|
50
|
-
{ id: 'h1', label: 'H1', icon: 'H1', title: 'Heading 1', group: 'structure' },
|
|
51
|
-
{ id: 'h2', label: 'H2', icon: 'H2', title: 'Heading 2', group: 'structure' },
|
|
52
|
-
{ id: 'h3', label: 'H3', icon: 'H3', title: 'Heading 3', group: 'structure' },
|
|
53
|
-
{ id: 'h4', label: 'H4', icon: 'H4', title: 'Heading 4', group: 'structure' },
|
|
54
|
-
{ id: 'h5', label: 'H5', icon: 'H5', title: 'Heading 5', group: 'structure' },
|
|
55
|
-
{ id: 'h6', label: 'H6', icon: 'H6', title: 'Heading 6', group: 'structure' },
|
|
56
|
-
// Insert group — block-level inserts (quote, code blocks, rules)
|
|
57
|
-
{ id: 'quote', label: '❝', icon: '❝', title: 'Blockquote', group: 'insert' },
|
|
58
|
-
{ id: 'codeblock', label: '{ }', icon: '{ }', title: 'Code block', group: 'insert' },
|
|
59
|
-
{ id: 'code', label: '</>', icon: '</>', title: 'Inline code', group: 'insert' },
|
|
60
|
-
{ id: 'hr', label: '—', icon: '—', title: 'Horizontal rule', group: 'insert' },
|
|
61
|
-
// Media group — links, tables, images, emoji
|
|
62
|
-
{ id: 'link', label: '🔗', icon: '🔗', title: 'Insert link', group: 'media' },
|
|
63
|
-
{ id: 'table', label: 'table', icon: '', title: 'Insert table', group: 'media' },
|
|
64
|
-
{ id: 'image', label: '🖼', icon: '🖼', title: 'Insert image', group: 'media' },
|
|
65
|
-
{ id: 'emoji', label: '😊', icon: '😊', title: 'Insert emoji', group: 'media' },
|
|
66
|
-
];
|
|
67
|
-
// ─── Inline SVG icons (line-art, currentColor) ──────────
|
|
68
|
-
const TABLE_ICON = (_jsxs("svg", { width: "14", height: "14", viewBox: "0 0 14 14", fill: "none", stroke: "currentColor", strokeWidth: "1.4", strokeLinecap: "round", children: [_jsx("rect", { x: "1", y: "1", width: "12", height: "12", rx: "1" }), _jsx("line", { x1: "1", y1: "5", x2: "13", y2: "5" }), _jsx("line", { x1: "1", y1: "9", x2: "13", y2: "9" }), _jsx("line", { x1: "5", y1: "1", x2: "5", y2: "13" }), _jsx("line", { x1: "9", y1: "1", x2: "9", y2: "13" })] }));
|
|
69
|
-
const LINK_ICON = (_jsxs("svg", { width: "14", height: "14", viewBox: "0 0 14 14", fill: "none", stroke: "currentColor", strokeWidth: "1.4", strokeLinecap: "round", strokeLinejoin: "round", children: [_jsx("path", { d: "M5.75 8.25 L8.25 5.75" }), _jsx("path", { d: "M6.5 3.75 L8 2.25 a2.5 2.5 0 0 1 3.54 3.54 L10 7.25" }), _jsx("path", { d: "M7.5 10.25 L6 11.75 a2.5 2.5 0 0 1 -3.54 -3.54 L4 6.75" })] }));
|
|
70
|
-
const IMAGE_ICON = (_jsxs("svg", { width: "14", height: "14", viewBox: "0 0 14 14", fill: "none", stroke: "currentColor", strokeWidth: "1.4", strokeLinecap: "round", strokeLinejoin: "round", children: [_jsx("rect", { x: "1.5", y: "2.5", width: "11", height: "9", rx: "1" }), _jsx("circle", { cx: "5", cy: "5.5", r: "0.9" }), _jsx("path", { d: "M2 10 L5.5 7 L8 9 L10 7.5 L12.5 10" })] }));
|
|
71
|
-
const PAPERCLIP_ICON = (_jsx("svg", { width: "14", height: "14", viewBox: "0 0 14 14", fill: "none", stroke: "currentColor", strokeWidth: "1.4", strokeLinecap: "round", strokeLinejoin: "round", children: _jsx("path", { d: "M11 4 L5.5 9.5 a1.75 1.75 0 0 0 2.5 2.5 L12.5 7.5 a3 3 0 0 0 -4.25 -4.25 L3 8.5 a4.25 4.25 0 0 0 6 6 L13 10.5" }) }));
|
|
72
|
-
const EMOJI_ICON = (_jsxs("svg", { width: "14", height: "14", viewBox: "0 0 14 14", fill: "none", stroke: "currentColor", strokeWidth: "1.4", strokeLinecap: "round", strokeLinejoin: "round", children: [_jsx("circle", { cx: "7", cy: "7", r: "5.25" }), _jsx("circle", { cx: "5.25", cy: "5.75", r: "0.6", fill: "currentColor", stroke: "none" }), _jsx("circle", { cx: "8.75", cy: "5.75", r: "0.6", fill: "currentColor", stroke: "none" }), _jsx("path", { d: "M4.75 8.5 a2.5 2.5 0 0 0 4.5 0" })] }));
|
|
73
|
-
/** Returns an SVG element when the button id maps to one, otherwise null. */
|
|
74
|
-
function buttonIconSvg(id) {
|
|
75
|
-
switch (id) {
|
|
76
|
-
case 'table':
|
|
77
|
-
return TABLE_ICON;
|
|
78
|
-
case 'link':
|
|
79
|
-
return LINK_ICON;
|
|
80
|
-
case 'image':
|
|
81
|
-
return IMAGE_ICON;
|
|
82
|
-
case 'emoji':
|
|
83
|
-
return EMOJI_ICON;
|
|
84
|
-
default:
|
|
85
|
-
return null;
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
// ─── Tiptap active-state map ────────────────────────────
|
|
89
|
-
/** Returns true if the given button id is currently active in Tiptap */
|
|
90
|
-
function isTiptapActive(editor, id) {
|
|
91
|
-
if (!editor)
|
|
92
|
-
return false;
|
|
93
|
-
switch (id) {
|
|
94
|
-
case 'bold':
|
|
95
|
-
return editor.isActive('bold');
|
|
96
|
-
case 'italic':
|
|
97
|
-
return editor.isActive('italic');
|
|
98
|
-
case 'strikethrough':
|
|
99
|
-
return editor.isActive('strike');
|
|
100
|
-
case 'code':
|
|
101
|
-
return editor.isActive('code');
|
|
102
|
-
case 'h1':
|
|
103
|
-
return editor.isActive('heading', { level: 1 });
|
|
104
|
-
case 'h2':
|
|
105
|
-
return editor.isActive('heading', { level: 2 });
|
|
106
|
-
case 'h3':
|
|
107
|
-
return editor.isActive('heading', { level: 3 });
|
|
108
|
-
case 'h4':
|
|
109
|
-
return editor.isActive('heading', { level: 4 });
|
|
110
|
-
case 'h5':
|
|
111
|
-
return editor.isActive('heading', { level: 5 });
|
|
112
|
-
case 'h6':
|
|
113
|
-
return editor.isActive('heading', { level: 6 });
|
|
114
|
-
case 'quote':
|
|
115
|
-
return editor.isActive('blockquote');
|
|
116
|
-
case 'ul':
|
|
117
|
-
return editor.isActive('bulletList');
|
|
118
|
-
case 'ol':
|
|
119
|
-
return editor.isActive('orderedList');
|
|
120
|
-
case 'codeblock':
|
|
121
|
-
return editor.isActive('codeBlock');
|
|
122
|
-
default:
|
|
123
|
-
return false;
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
/**
|
|
127
|
-
* Formatting toolbar.
|
|
128
|
-
* - WYSIWYG: calls Tiptap chain commands (toggleBold, etc.)
|
|
129
|
-
* - Raw: appends markdown syntax to the source
|
|
130
|
-
*/
|
|
131
|
-
export function Toolbar({ className, showFiles, onToggleFiles, slotLeft, slotAfterActions, slotRight, showPlayTab = true, }) {
|
|
132
|
-
const { activeView, setActiveView, markdownSource, setMarkdownSource, tiptapEditor, monacoEditor, mediaProvider, editorMode, versioning, allowRecording, documentLinkProvider, theme, } = useEditorContext();
|
|
133
|
-
const isCodeMode = editorMode === 'code';
|
|
134
|
-
// In code mode only the raw view is meaningful; the WYSIWYG and Preview
|
|
135
|
-
// surfaces aren't mounted, so hide their tabs.
|
|
136
|
-
const visibleViews = VIEWS.filter((v) => {
|
|
137
|
-
if (isCodeMode)
|
|
138
|
-
return v.id === 'raw';
|
|
139
|
-
if (v.id === 'preview' && !showPlayTab)
|
|
140
|
-
return false;
|
|
141
|
-
return true;
|
|
142
|
-
});
|
|
143
|
-
const showViewTabs = visibleViews.length > 1;
|
|
144
|
-
// Hidden file input for image picker
|
|
145
|
-
const imageInputRef = useRef(null);
|
|
146
|
-
// Link dialog — shared by WYSIWYG and Raw views.
|
|
147
|
-
const [linkDialog, setLinkDialog] = useState(null);
|
|
148
|
-
// Emoji picker — toolbar-anchored popover. We track the trigger
|
|
149
|
-
// button's screen rect so the picker can position itself just below
|
|
150
|
-
// it via createPortal (the toolbar's overflow:hidden actions row
|
|
151
|
-
// would otherwise clip the popover).
|
|
152
|
-
const emojiButtonRef = useRef(null);
|
|
153
|
-
const [emojiPickerAnchor, setEmojiPickerAnchor] = useState(null);
|
|
154
|
-
const openEmojiPicker = useCallback(() => {
|
|
155
|
-
const btn = emojiButtonRef.current;
|
|
156
|
-
if (!btn)
|
|
157
|
-
return;
|
|
158
|
-
const rect = btn.getBoundingClientRect();
|
|
159
|
-
// Position just below the trigger by default, then clamp into the
|
|
160
|
-
// visible viewport so the picker is never clipped on the right or
|
|
161
|
-
// bottom — flips above the trigger when there isn't room below.
|
|
162
|
-
const gap = 6;
|
|
163
|
-
const margin = 8;
|
|
164
|
-
const vw = window.innerWidth;
|
|
165
|
-
const vh = window.innerHeight;
|
|
166
|
-
let left = rect.left;
|
|
167
|
-
if (left + EMOJI_PICKER_WIDTH + margin > vw) {
|
|
168
|
-
left = Math.max(margin, vw - EMOJI_PICKER_WIDTH - margin);
|
|
169
|
-
}
|
|
170
|
-
let top = rect.bottom + gap;
|
|
171
|
-
if (top + EMOJI_PICKER_MAX_HEIGHT + margin > vh) {
|
|
172
|
-
const flipped = rect.top - EMOJI_PICKER_MAX_HEIGHT - gap;
|
|
173
|
-
// Prefer flipping above when there's more room there; otherwise
|
|
174
|
-
// pin to the top edge with margin and let the picker's own
|
|
175
|
-
// maxHeight clip it.
|
|
176
|
-
top = flipped >= margin ? flipped : margin;
|
|
177
|
-
}
|
|
178
|
-
setEmojiPickerAnchor({ top, left });
|
|
179
|
-
}, []);
|
|
180
|
-
const closeEmojiPicker = useCallback(() => setEmojiPickerAnchor(null), []);
|
|
181
|
-
// ── Narrow-screen detection ──────────────────────────
|
|
182
|
-
const [isNarrow, setIsNarrow] = useState(() => typeof window !== 'undefined' && window.matchMedia('(max-width: 768px)').matches);
|
|
183
|
-
useEffect(() => {
|
|
184
|
-
const mq = window.matchMedia('(max-width: 768px)');
|
|
185
|
-
const handler = (e) => setIsNarrow(e.matches);
|
|
186
|
-
mq.addEventListener('change', handler);
|
|
187
|
-
return () => mq.removeEventListener('change', handler);
|
|
188
|
-
}, []);
|
|
189
|
-
// ── Overflow detection ────────────────────────────────
|
|
190
|
-
const actionsRef = useRef(null);
|
|
191
|
-
const [measuredOverflowIndex, setMeasuredOverflowIndex] = useState(null);
|
|
192
|
-
const [showOverflow, setShowOverflow] = useState(false);
|
|
193
|
-
const overflowRef = useRef(null);
|
|
194
|
-
// Document settings (frontmatter) dialog
|
|
195
|
-
const [showDocSettings, setShowDocSettings] = useState(false);
|
|
196
|
-
// On narrow screens, force all buttons into the overflow menu
|
|
197
|
-
const overflowIndex = isNarrow ? 0 : measuredOverflowIndex;
|
|
198
|
-
useEffect(() => {
|
|
199
|
-
if (isNarrow)
|
|
200
|
-
return; // Skip measurement on narrow — everything overflows
|
|
201
|
-
const container = actionsRef.current;
|
|
202
|
-
if (!container)
|
|
203
|
-
return;
|
|
204
|
-
const measure = () => {
|
|
205
|
-
const containerRight = container.getBoundingClientRect().right;
|
|
206
|
-
const children = container.querySelectorAll(':scope > .squisq-toolbar-group > .squisq-toolbar-button');
|
|
207
|
-
let firstHidden = null;
|
|
208
|
-
children.forEach((child, i) => {
|
|
209
|
-
if (firstHidden !== null)
|
|
210
|
-
return;
|
|
211
|
-
const rect = child.getBoundingClientRect();
|
|
212
|
-
// A button is hidden if its right edge extends past the container
|
|
213
|
-
if (rect.right > containerRight + 2) {
|
|
214
|
-
firstHidden = i;
|
|
215
|
-
}
|
|
216
|
-
});
|
|
217
|
-
setMeasuredOverflowIndex(firstHidden);
|
|
218
|
-
};
|
|
219
|
-
const ro = new ResizeObserver(measure);
|
|
220
|
-
ro.observe(container);
|
|
221
|
-
measure();
|
|
222
|
-
return () => ro.disconnect();
|
|
223
|
-
}, [activeView, isNarrow]);
|
|
224
|
-
// Close overflow menu on outside click
|
|
225
|
-
useEffect(() => {
|
|
226
|
-
if (!showOverflow)
|
|
227
|
-
return;
|
|
228
|
-
const handleClick = (e) => {
|
|
229
|
-
if (overflowRef.current && !overflowRef.current.contains(e.target)) {
|
|
230
|
-
setShowOverflow(false);
|
|
231
|
-
}
|
|
232
|
-
};
|
|
233
|
-
document.addEventListener('mousedown', handleClick);
|
|
234
|
-
return () => document.removeEventListener('mousedown', handleClick);
|
|
235
|
-
}, [showOverflow]);
|
|
236
|
-
// Open-up vs open-down: the overflow menu is anchored to its trigger with
|
|
237
|
-
// `top: 100%` by default. When the toolbar lives near the bottom of a
|
|
238
|
-
// small container (e.g. a chat composer), a downward menu gets clipped.
|
|
239
|
-
// Measure on open and flip the anchor to `bottom: 100%` if the space
|
|
240
|
-
// above the trigger is larger than the space below.
|
|
241
|
-
const [overflowPlacement, setOverflowPlacement] = useState('down');
|
|
242
|
-
useEffect(() => {
|
|
243
|
-
if (!showOverflow || !overflowRef.current)
|
|
244
|
-
return;
|
|
245
|
-
const trigger = overflowRef.current.querySelector('.squisq-toolbar-overflow-trigger');
|
|
246
|
-
if (!trigger)
|
|
247
|
-
return;
|
|
248
|
-
const rect = trigger.getBoundingClientRect();
|
|
249
|
-
const spaceBelow = window.innerHeight - rect.bottom;
|
|
250
|
-
const spaceAbove = rect.top;
|
|
251
|
-
// Assume a typical menu height; exact measurement is unreliable on first
|
|
252
|
-
// open because the menu hasn't rendered yet when this runs.
|
|
253
|
-
const ESTIMATED_MENU_HEIGHT = 260;
|
|
254
|
-
if (spaceBelow < ESTIMATED_MENU_HEIGHT && spaceAbove > spaceBelow) {
|
|
255
|
-
setOverflowPlacement('up');
|
|
256
|
-
}
|
|
257
|
-
else {
|
|
258
|
-
setOverflowPlacement('down');
|
|
259
|
-
}
|
|
260
|
-
}, [showOverflow]);
|
|
261
|
-
// Force re-render when Tiptap selection or formatting state changes
|
|
262
|
-
const [, forceUpdate] = useReducer((c) => c + 1, 0);
|
|
263
|
-
useEffect(() => {
|
|
264
|
-
if (!tiptapEditor)
|
|
265
|
-
return;
|
|
266
|
-
tiptapEditor.on('transaction', forceUpdate);
|
|
267
|
-
return () => {
|
|
268
|
-
tiptapEditor.off('transaction', forceUpdate);
|
|
269
|
-
};
|
|
270
|
-
}, [tiptapEditor]);
|
|
271
|
-
// ── Tiptap handler ─────────────────────────────────────
|
|
272
|
-
const handleTiptap = useCallback((id) => {
|
|
273
|
-
if (!tiptapEditor)
|
|
274
|
-
return;
|
|
275
|
-
const chain = tiptapEditor.chain().focus();
|
|
276
|
-
switch (id) {
|
|
277
|
-
case 'bold':
|
|
278
|
-
chain.toggleBold().run();
|
|
279
|
-
break;
|
|
280
|
-
case 'italic':
|
|
281
|
-
chain.toggleItalic().run();
|
|
282
|
-
break;
|
|
283
|
-
case 'strikethrough':
|
|
284
|
-
chain.toggleStrike().run();
|
|
285
|
-
break;
|
|
286
|
-
case 'code':
|
|
287
|
-
chain.toggleCode().run();
|
|
288
|
-
break;
|
|
289
|
-
case 'h1':
|
|
290
|
-
chain.toggleHeading({ level: 1 }).run();
|
|
291
|
-
break;
|
|
292
|
-
case 'h2':
|
|
293
|
-
chain.toggleHeading({ level: 2 }).run();
|
|
294
|
-
break;
|
|
295
|
-
case 'h3':
|
|
296
|
-
chain.toggleHeading({ level: 3 }).run();
|
|
297
|
-
break;
|
|
298
|
-
case 'h4':
|
|
299
|
-
chain.toggleHeading({ level: 4 }).run();
|
|
300
|
-
break;
|
|
301
|
-
case 'h5':
|
|
302
|
-
chain.toggleHeading({ level: 5 }).run();
|
|
303
|
-
break;
|
|
304
|
-
case 'h6':
|
|
305
|
-
chain.toggleHeading({ level: 6 }).run();
|
|
306
|
-
break;
|
|
307
|
-
case 'quote':
|
|
308
|
-
chain.toggleBlockquote().run();
|
|
309
|
-
break;
|
|
310
|
-
case 'ul':
|
|
311
|
-
chain.toggleBulletList().run();
|
|
312
|
-
break;
|
|
313
|
-
case 'ol':
|
|
314
|
-
chain.toggleOrderedList().run();
|
|
315
|
-
break;
|
|
316
|
-
case 'codeblock':
|
|
317
|
-
chain.toggleCodeBlock().run();
|
|
318
|
-
break;
|
|
319
|
-
case 'hr':
|
|
320
|
-
chain.setHorizontalRule().run();
|
|
321
|
-
break;
|
|
322
|
-
case 'link': {
|
|
323
|
-
const isActive = tiptapEditor.isActive('link');
|
|
324
|
-
let initialText = '';
|
|
325
|
-
let initialUrl = '';
|
|
326
|
-
if (isActive) {
|
|
327
|
-
// Snap selection to the full link mark so editing replaces
|
|
328
|
-
// the entire `[text](url)` rather than just the cursor word.
|
|
329
|
-
tiptapEditor.chain().focus().extendMarkRange('link').run();
|
|
330
|
-
const sel = tiptapEditor.state.selection;
|
|
331
|
-
initialText = tiptapEditor.state.doc.textBetween(sel.from, sel.to, ' ');
|
|
332
|
-
initialUrl = tiptapEditor.getAttributes('link').href ?? '';
|
|
333
|
-
}
|
|
334
|
-
else {
|
|
335
|
-
const { from, to, empty } = tiptapEditor.state.selection;
|
|
336
|
-
if (!empty) {
|
|
337
|
-
initialText = tiptapEditor.state.doc.textBetween(from, to, ' ');
|
|
338
|
-
}
|
|
339
|
-
}
|
|
340
|
-
setLinkDialog({
|
|
341
|
-
mode: isActive ? 'update' : 'insert',
|
|
342
|
-
target: 'wysiwyg',
|
|
343
|
-
initialText,
|
|
344
|
-
initialUrl,
|
|
345
|
-
rawRange: null,
|
|
346
|
-
});
|
|
347
|
-
break;
|
|
348
|
-
}
|
|
349
|
-
case 'table':
|
|
350
|
-
tiptapEditor.chain().focus().insertTable({ rows: 3, cols: 3, withHeaderRow: true }).run();
|
|
351
|
-
break;
|
|
352
|
-
}
|
|
353
|
-
}, [tiptapEditor]);
|
|
354
|
-
// ── Raw markdown handler ───────────────────────────────
|
|
355
|
-
const handleRaw = useCallback((id) => {
|
|
356
|
-
if (monacoEditor) {
|
|
357
|
-
// Use Monaco's selection API for proper wrap/insert behavior
|
|
358
|
-
const selection = monacoEditor.getSelection();
|
|
359
|
-
const model = monacoEditor.getModel();
|
|
360
|
-
if (!selection || !model)
|
|
361
|
-
return;
|
|
362
|
-
const selectedText = model.getValueInRange(selection);
|
|
363
|
-
const hasSelection = selectedText.length > 0;
|
|
364
|
-
let replacement = '';
|
|
365
|
-
let newCursorOffset = 0; // offset from start of replacement to place cursor
|
|
366
|
-
// Inline wrapping: wrap selection or insert placeholder
|
|
367
|
-
const wrapInline = (before, after, placeholder) => {
|
|
368
|
-
if (hasSelection) {
|
|
369
|
-
replacement = before + selectedText + after;
|
|
370
|
-
}
|
|
371
|
-
else {
|
|
372
|
-
replacement = before + placeholder + after;
|
|
373
|
-
// Select the placeholder text after insertion
|
|
374
|
-
newCursorOffset = before.length;
|
|
375
|
-
}
|
|
376
|
-
};
|
|
377
|
-
// Block-level: prefix each selected line, or insert a new block
|
|
378
|
-
const prefixLines = (prefix, placeholder) => {
|
|
379
|
-
if (hasSelection) {
|
|
380
|
-
replacement = selectedText
|
|
381
|
-
.split('\n')
|
|
382
|
-
.map((line) => prefix + line)
|
|
383
|
-
.join('\n');
|
|
384
|
-
}
|
|
385
|
-
else {
|
|
386
|
-
replacement = prefix + placeholder;
|
|
387
|
-
newCursorOffset = prefix.length;
|
|
388
|
-
}
|
|
389
|
-
};
|
|
390
|
-
switch (id) {
|
|
391
|
-
case 'bold':
|
|
392
|
-
wrapInline('**', '**', 'bold text');
|
|
393
|
-
break;
|
|
394
|
-
case 'italic':
|
|
395
|
-
wrapInline('*', '*', 'italic text');
|
|
396
|
-
break;
|
|
397
|
-
case 'strikethrough':
|
|
398
|
-
wrapInline('~~', '~~', 'strikethrough');
|
|
399
|
-
break;
|
|
400
|
-
case 'code':
|
|
401
|
-
wrapInline('`', '`', 'code');
|
|
402
|
-
break;
|
|
403
|
-
case 'h1':
|
|
404
|
-
prefixLines('# ', 'Heading 1');
|
|
405
|
-
break;
|
|
406
|
-
case 'h2':
|
|
407
|
-
prefixLines('## ', 'Heading 2');
|
|
408
|
-
break;
|
|
409
|
-
case 'h3':
|
|
410
|
-
prefixLines('### ', 'Heading 3');
|
|
411
|
-
break;
|
|
412
|
-
case 'h4':
|
|
413
|
-
prefixLines('#### ', 'Heading 4');
|
|
414
|
-
break;
|
|
415
|
-
case 'h5':
|
|
416
|
-
prefixLines('##### ', 'Heading 5');
|
|
417
|
-
break;
|
|
418
|
-
case 'h6':
|
|
419
|
-
prefixLines('###### ', 'Heading 6');
|
|
420
|
-
break;
|
|
421
|
-
case 'quote':
|
|
422
|
-
prefixLines('> ', 'Quote');
|
|
423
|
-
break;
|
|
424
|
-
case 'ul':
|
|
425
|
-
prefixLines('- ', 'Item');
|
|
426
|
-
break;
|
|
427
|
-
case 'ol':
|
|
428
|
-
prefixLines('1. ', 'Item');
|
|
429
|
-
break;
|
|
430
|
-
case 'codeblock': {
|
|
431
|
-
const inner = hasSelection ? selectedText : 'code';
|
|
432
|
-
replacement = '```\n' + inner + '\n```';
|
|
433
|
-
if (!hasSelection)
|
|
434
|
-
newCursorOffset = 4; // after ```\n
|
|
435
|
-
break;
|
|
436
|
-
}
|
|
437
|
-
case 'hr': {
|
|
438
|
-
replacement = '\n---\n';
|
|
439
|
-
break;
|
|
440
|
-
}
|
|
441
|
-
case 'link': {
|
|
442
|
-
// Open the LinkDialog instead of inserting literal text. If the
|
|
443
|
-
// cursor sits inside an existing `[text](url)` on this line,
|
|
444
|
-
// prefill from it and replace the whole match on confirm.
|
|
445
|
-
const lineNumber = selection.startLineNumber;
|
|
446
|
-
const lineText = model.getLineContent(lineNumber);
|
|
447
|
-
const cursorCol = selection.startColumn;
|
|
448
|
-
const linkRe = /\[([^\]]*)\]\(([^)]*)\)/g;
|
|
449
|
-
let match;
|
|
450
|
-
let existing = null;
|
|
451
|
-
while ((match = linkRe.exec(lineText)) !== null) {
|
|
452
|
-
const startCol = match.index + 1; // 1-based
|
|
453
|
-
const endCol = startCol + match[0].length;
|
|
454
|
-
if (cursorCol >= startCol && cursorCol <= endCol) {
|
|
455
|
-
existing = {
|
|
456
|
-
text: match[1],
|
|
457
|
-
url: match[2],
|
|
458
|
-
range: {
|
|
459
|
-
startLineNumber: lineNumber,
|
|
460
|
-
startColumn: startCol,
|
|
461
|
-
endLineNumber: lineNumber,
|
|
462
|
-
endColumn: endCol,
|
|
463
|
-
},
|
|
464
|
-
};
|
|
465
|
-
break;
|
|
466
|
-
}
|
|
467
|
-
}
|
|
468
|
-
setLinkDialog({
|
|
469
|
-
mode: existing ? 'update' : 'insert',
|
|
470
|
-
target: 'raw',
|
|
471
|
-
initialText: existing ? existing.text : hasSelection ? selectedText : '',
|
|
472
|
-
initialUrl: existing ? existing.url : '',
|
|
473
|
-
rawRange: existing ? existing.range : null,
|
|
474
|
-
});
|
|
475
|
-
// Skip the executeEdits/setPosition tail below — the dialog will
|
|
476
|
-
// apply its own edit on confirm.
|
|
477
|
-
return;
|
|
478
|
-
}
|
|
479
|
-
case 'table': {
|
|
480
|
-
const tpl = '| Header 1 | Header 2 | Header 3 |\n| --- | --- | --- |\n| Cell | Cell | Cell |\n| Cell | Cell | Cell |';
|
|
481
|
-
replacement = '\n' + tpl + '\n';
|
|
482
|
-
newCursorOffset = 3; // after \n|
|
|
483
|
-
break;
|
|
484
|
-
}
|
|
485
|
-
}
|
|
486
|
-
// Apply the edit via Monaco's executeEdits for proper undo support
|
|
487
|
-
const range = selection;
|
|
488
|
-
monacoEditor.executeEdits('toolbar', [{ range, text: replacement }]);
|
|
489
|
-
// If no selection, select the placeholder text so user can type over it
|
|
490
|
-
if (!hasSelection && newCursorOffset > 0) {
|
|
491
|
-
const startPos = model.getPositionAt(model.getOffsetAt(range.getStartPosition()) + newCursorOffset);
|
|
492
|
-
// Just place cursor after the prefix
|
|
493
|
-
monacoEditor.setPosition(startPos);
|
|
494
|
-
}
|
|
495
|
-
monacoEditor.focus();
|
|
496
|
-
}
|
|
497
|
-
else {
|
|
498
|
-
// Fallback: no Monaco instance, just append
|
|
499
|
-
let insertion = '';
|
|
500
|
-
switch (id) {
|
|
501
|
-
case 'bold':
|
|
502
|
-
insertion = '**bold text**';
|
|
503
|
-
break;
|
|
504
|
-
case 'italic':
|
|
505
|
-
insertion = '*italic text*';
|
|
506
|
-
break;
|
|
507
|
-
case 'strikethrough':
|
|
508
|
-
insertion = '~~strikethrough~~';
|
|
509
|
-
break;
|
|
510
|
-
case 'code':
|
|
511
|
-
insertion = '`code`';
|
|
512
|
-
break;
|
|
513
|
-
case 'h1':
|
|
514
|
-
insertion = '\n# Heading 1\n';
|
|
515
|
-
break;
|
|
516
|
-
case 'h2':
|
|
517
|
-
insertion = '\n## Heading 2\n';
|
|
518
|
-
break;
|
|
519
|
-
case 'h3':
|
|
520
|
-
insertion = '\n### Heading 3\n';
|
|
521
|
-
break;
|
|
522
|
-
case 'quote':
|
|
523
|
-
insertion = '\n> Quote\n';
|
|
524
|
-
break;
|
|
525
|
-
case 'ul':
|
|
526
|
-
insertion = '\n- Item\n';
|
|
527
|
-
break;
|
|
528
|
-
case 'ol':
|
|
529
|
-
insertion = '\n1. Item\n';
|
|
530
|
-
break;
|
|
531
|
-
case 'codeblock':
|
|
532
|
-
insertion = '\n```\ncode\n```\n';
|
|
533
|
-
break;
|
|
534
|
-
case 'hr':
|
|
535
|
-
insertion = '\n---\n';
|
|
536
|
-
break;
|
|
537
|
-
case 'link':
|
|
538
|
-
insertion = '[link text](url)';
|
|
539
|
-
break;
|
|
540
|
-
case 'table':
|
|
541
|
-
insertion =
|
|
542
|
-
'\n| Header 1 | Header 2 | Header 3 |\n| --- | --- | --- |\n| Cell | Cell | Cell |\n| Cell | Cell | Cell |\n';
|
|
543
|
-
break;
|
|
544
|
-
}
|
|
545
|
-
if (insertion) {
|
|
546
|
-
setMarkdownSource(markdownSource + insertion);
|
|
547
|
-
}
|
|
548
|
-
}
|
|
549
|
-
}, [monacoEditor, markdownSource, setMarkdownSource]);
|
|
550
|
-
// ── Image upload handler ───────────────────────────────
|
|
551
|
-
const handleImageFile = useCallback(async (file) => {
|
|
552
|
-
if (!mediaProvider)
|
|
553
|
-
return;
|
|
554
|
-
const buffer = await file.arrayBuffer();
|
|
555
|
-
const relativePath = await mediaProvider.addMedia(file.name, buffer, file.type);
|
|
556
|
-
const altText = file.name.replace(/\.[^.]+$/, '').replace(/[-_]/g, ' ');
|
|
557
|
-
if (activeView === 'wysiwyg' && tiptapEditor) {
|
|
558
|
-
tiptapEditor.chain().focus().setImage({ src: relativePath, alt: altText }).run();
|
|
559
|
-
}
|
|
560
|
-
else if (monacoEditor) {
|
|
561
|
-
const selection = monacoEditor.getSelection();
|
|
562
|
-
const model = monacoEditor.getModel();
|
|
563
|
-
if (selection && model) {
|
|
564
|
-
const md = ``;
|
|
565
|
-
monacoEditor.executeEdits('toolbar', [{ range: selection, text: md }]);
|
|
566
|
-
monacoEditor.focus();
|
|
567
|
-
}
|
|
568
|
-
}
|
|
569
|
-
else {
|
|
570
|
-
setMarkdownSource(markdownSource + `\n\n`);
|
|
571
|
-
}
|
|
572
|
-
}, [mediaProvider, activeView, tiptapEditor, monacoEditor, markdownSource, setMarkdownSource]);
|
|
573
|
-
const handleAction = useCallback((id) => {
|
|
574
|
-
if (id === 'image') {
|
|
575
|
-
imageInputRef.current?.click();
|
|
576
|
-
return;
|
|
577
|
-
}
|
|
578
|
-
if (id === 'emoji') {
|
|
579
|
-
// Toggle the popover: clicking the button again closes it.
|
|
580
|
-
if (emojiPickerAnchor)
|
|
581
|
-
closeEmojiPicker();
|
|
582
|
-
else
|
|
583
|
-
openEmojiPicker();
|
|
584
|
-
return;
|
|
585
|
-
}
|
|
586
|
-
if (activeView === 'wysiwyg' && tiptapEditor) {
|
|
587
|
-
handleTiptap(id);
|
|
588
|
-
}
|
|
589
|
-
else {
|
|
590
|
-
handleRaw(id);
|
|
591
|
-
}
|
|
592
|
-
}, [
|
|
593
|
-
activeView,
|
|
594
|
-
tiptapEditor,
|
|
595
|
-
handleTiptap,
|
|
596
|
-
handleRaw,
|
|
597
|
-
emojiPickerAnchor,
|
|
598
|
-
openEmojiPicker,
|
|
599
|
-
closeEmojiPicker,
|
|
600
|
-
]);
|
|
601
|
-
// ── Picker insert (emoji or FontAwesome icon) ──────
|
|
602
|
-
// Inserts a chosen picker entry at the cursor. We bypass
|
|
603
|
-
// `insertAtCursor` (which routes through markdown→Tiptap conversion
|
|
604
|
-
// and wraps the input in a paragraph) so entries land inline at the
|
|
605
|
-
// caret rather than starting a new block. Emoji insert as a plain
|
|
606
|
-
// character; FontAwesome icons insert as the `InlineIcon` Tiptap
|
|
607
|
-
// node so the editor renders them inline immediately.
|
|
608
|
-
const handleEmojiSelect = useCallback((entry) => {
|
|
609
|
-
if (activeView === 'wysiwyg' && tiptapEditor) {
|
|
610
|
-
if (entry.kind === 'emoji') {
|
|
611
|
-
tiptapEditor.chain().focus().insertContent(entry.char).run();
|
|
612
|
-
}
|
|
613
|
-
else {
|
|
614
|
-
tiptapEditor
|
|
615
|
-
.chain()
|
|
616
|
-
.focus()
|
|
617
|
-
.insertContent({
|
|
618
|
-
type: 'inlineIcon',
|
|
619
|
-
attrs: { token: entry.token, family: entry.family, name: entry.name },
|
|
620
|
-
})
|
|
621
|
-
.run();
|
|
622
|
-
}
|
|
623
|
-
}
|
|
624
|
-
else if (activeView === 'raw' && monacoEditor) {
|
|
625
|
-
const insertion = entry.kind === 'emoji' ? entry.char : `{[${entry.token}]}`;
|
|
626
|
-
const position = monacoEditor.getPosition();
|
|
627
|
-
if (position) {
|
|
628
|
-
const range = {
|
|
629
|
-
startLineNumber: position.lineNumber,
|
|
630
|
-
startColumn: position.column,
|
|
631
|
-
endLineNumber: position.lineNumber,
|
|
632
|
-
endColumn: position.column,
|
|
633
|
-
};
|
|
634
|
-
monacoEditor.executeEdits('picker-insert', [{ range, text: insertion }]);
|
|
635
|
-
monacoEditor.focus();
|
|
636
|
-
}
|
|
637
|
-
else {
|
|
638
|
-
setMarkdownSource(markdownSource + insertion);
|
|
639
|
-
}
|
|
640
|
-
}
|
|
641
|
-
else {
|
|
642
|
-
const insertion = entry.kind === 'emoji' ? entry.char : `{[${entry.token}]}`;
|
|
643
|
-
setMarkdownSource(markdownSource + insertion);
|
|
644
|
-
}
|
|
645
|
-
closeEmojiPicker();
|
|
646
|
-
}, [activeView, tiptapEditor, monacoEditor, markdownSource, setMarkdownSource, closeEmojiPicker]);
|
|
647
|
-
// ── Ctrl+K / Cmd+K → open the link dialog ────────────
|
|
648
|
-
// Mirrors the behaviour of common editors (Word, Google Docs, VS Code's
|
|
649
|
-
// Markdown preview): if the cursor is in a Squisq editor surface, the
|
|
650
|
-
// shortcut routes through the same handler the toolbar Link button uses,
|
|
651
|
-
// which prefills the dialog from the current selection (or the link
|
|
652
|
-
// under the cursor) before opening.
|
|
653
|
-
useEffect(() => {
|
|
654
|
-
const onKeyDown = (e) => {
|
|
655
|
-
if (!(e.ctrlKey || e.metaKey) || e.altKey || e.shiftKey)
|
|
656
|
-
return;
|
|
657
|
-
if (e.key.toLowerCase() !== 'k')
|
|
658
|
-
return;
|
|
659
|
-
const target = e.target;
|
|
660
|
-
if (!target)
|
|
661
|
-
return;
|
|
662
|
-
// Only intercept when focus is inside one of our editor surfaces.
|
|
663
|
-
const inEditor = !!target.closest('.squisq-wysiwyg-editor, .ProseMirror, .squisq-raw-editor-container, .monaco-editor');
|
|
664
|
-
if (!inEditor)
|
|
665
|
-
return;
|
|
666
|
-
e.preventDefault();
|
|
667
|
-
e.stopPropagation();
|
|
668
|
-
handleAction('link');
|
|
669
|
-
};
|
|
670
|
-
window.addEventListener('keydown', onKeyDown, true);
|
|
671
|
-
return () => window.removeEventListener('keydown', onKeyDown, true);
|
|
672
|
-
}, [handleAction]);
|
|
673
|
-
// ── Link dialog confirm ──────────────────────────────
|
|
674
|
-
const handleLinkConfirm = useCallback((text, url) => {
|
|
675
|
-
if (!linkDialog)
|
|
676
|
-
return;
|
|
677
|
-
const trimmedUrl = url.trim();
|
|
678
|
-
const trimmedText = text.trim();
|
|
679
|
-
if (linkDialog.target === 'wysiwyg' && tiptapEditor) {
|
|
680
|
-
if (!trimmedUrl) {
|
|
681
|
-
// Empty URL on update = unlink. On insert with no URL, do nothing.
|
|
682
|
-
if (linkDialog.mode === 'update') {
|
|
683
|
-
tiptapEditor.chain().focus().unsetLink().run();
|
|
684
|
-
}
|
|
685
|
-
setLinkDialog(null);
|
|
686
|
-
return;
|
|
687
|
-
}
|
|
688
|
-
const visibleText = trimmedText || trimmedUrl;
|
|
689
|
-
const chain = tiptapEditor.chain().focus();
|
|
690
|
-
// Insert (or replace selection) with text carrying a link mark. When
|
|
691
|
-
// updating an existing link, the selection was extended to the full
|
|
692
|
-
// mark range earlier, so this replaces the entire `[text](url)`.
|
|
693
|
-
chain
|
|
694
|
-
.insertContent({
|
|
695
|
-
type: 'text',
|
|
696
|
-
text: visibleText,
|
|
697
|
-
marks: [{ type: 'link', attrs: { href: trimmedUrl } }],
|
|
698
|
-
})
|
|
699
|
-
.run();
|
|
700
|
-
setLinkDialog(null);
|
|
701
|
-
return;
|
|
702
|
-
}
|
|
703
|
-
if (linkDialog.target === 'raw' && monacoEditor) {
|
|
704
|
-
const model = monacoEditor.getModel();
|
|
705
|
-
if (!model) {
|
|
706
|
-
setLinkDialog(null);
|
|
707
|
-
return;
|
|
708
|
-
}
|
|
709
|
-
if (!trimmedUrl && linkDialog.mode === 'update' && linkDialog.rawRange) {
|
|
710
|
-
// Empty URL on update = strip the markdown link, keep the text.
|
|
711
|
-
monacoEditor.executeEdits('toolbar-link-edit', [
|
|
712
|
-
{ range: linkDialog.rawRange, text: trimmedText || linkDialog.initialText },
|
|
713
|
-
]);
|
|
714
|
-
monacoEditor.focus();
|
|
715
|
-
setLinkDialog(null);
|
|
716
|
-
return;
|
|
717
|
-
}
|
|
718
|
-
if (!trimmedUrl) {
|
|
719
|
-
setLinkDialog(null);
|
|
720
|
-
return;
|
|
721
|
-
}
|
|
722
|
-
const visibleText = trimmedText || trimmedUrl;
|
|
723
|
-
const replacement = `[${visibleText}](${trimmedUrl})`;
|
|
724
|
-
const range = linkDialog.rawRange ?? monacoEditor.getSelection();
|
|
725
|
-
if (!range) {
|
|
726
|
-
setLinkDialog(null);
|
|
727
|
-
return;
|
|
728
|
-
}
|
|
729
|
-
monacoEditor.executeEdits('toolbar-link-edit', [{ range, text: replacement }]);
|
|
730
|
-
monacoEditor.focus();
|
|
731
|
-
setLinkDialog(null);
|
|
732
|
-
return;
|
|
733
|
-
}
|
|
734
|
-
setLinkDialog(null);
|
|
735
|
-
}, [linkDialog, tiptapEditor, monacoEditor]);
|
|
736
|
-
const groups = ['format', 'lists', 'structure', 'insert', 'media'];
|
|
737
|
-
const isWysiwyg = activeView === 'wysiwyg' && tiptapEditor;
|
|
738
|
-
const isPreview = activeView === 'preview';
|
|
739
|
-
// ── Progressive heading disclosure ───────────────────
|
|
740
|
-
// H1\u2013H3 are always visible. H4 appears once the document already
|
|
741
|
-
// contains an H3, H5 once it contains an H4, and H6 once it contains
|
|
742
|
-
// an H5. This keeps the toolbar compact for typical short documents
|
|
743
|
-
// while letting deeply nested documents reach every level.
|
|
744
|
-
const maxHeadingLevelInDoc = useMemo(() => {
|
|
745
|
-
if (!markdownSource)
|
|
746
|
-
return 0;
|
|
747
|
-
let max = 0;
|
|
748
|
-
let inFence = false;
|
|
749
|
-
for (const rawLine of markdownSource.split('\n')) {
|
|
750
|
-
const line = rawLine.trimEnd();
|
|
751
|
-
if (/^\s*```/.test(line)) {
|
|
752
|
-
inFence = !inFence;
|
|
753
|
-
continue;
|
|
754
|
-
}
|
|
755
|
-
if (inFence)
|
|
756
|
-
continue;
|
|
757
|
-
const m = /^(#{1,6})\s+\S/.exec(line);
|
|
758
|
-
if (m && m[1].length > max)
|
|
759
|
-
max = m[1].length;
|
|
760
|
-
}
|
|
761
|
-
return max;
|
|
762
|
-
}, [markdownSource]);
|
|
763
|
-
// Show H(n+1) when the document already contains H(n), starting from H3.
|
|
764
|
-
const visibleHeadingMax = Math.min(6, Math.max(3, maxHeadingLevelInDoc + 1));
|
|
765
|
-
const isButtonVisible = (id) => {
|
|
766
|
-
const m = /^h([1-6])$/.exec(id);
|
|
767
|
-
if (!m)
|
|
768
|
-
return true;
|
|
769
|
-
return Number(m[1]) <= visibleHeadingMax;
|
|
770
|
-
};
|
|
771
|
-
// Detect whether cursor is inside a table (WYSIWYG mode only)
|
|
772
|
-
const isInTable = isWysiwyg ? tiptapEditor.isActive('table') : false;
|
|
773
|
-
// Detect current heading template (WYSIWYG mode only)
|
|
774
|
-
const wysiwygTemplate = isWysiwyg
|
|
775
|
-
? tiptapEditor.isActive('heading')
|
|
776
|
-
? (tiptapEditor.getAttributes('heading')?.dataTemplate ?? '')
|
|
777
|
-
: null
|
|
778
|
-
: null;
|
|
779
|
-
// ── Monaco heading detection (Markdown view) ─────────────────────
|
|
780
|
-
// Watch the Monaco cursor and surface the template picker whenever the
|
|
781
|
-
// cursor is on a heading line. `null` hides the picker; '' shows it
|
|
782
|
-
// with no template selected; any other string is the current template.
|
|
783
|
-
const isRawView = activeView === 'raw';
|
|
784
|
-
const [rawTemplate, setRawTemplate] = useState(null);
|
|
785
|
-
const [rawHeadingLine, setRawHeadingLine] = useState(null);
|
|
786
|
-
useEffect(() => {
|
|
787
|
-
if (!isRawView || !monacoEditor) {
|
|
788
|
-
setRawTemplate(null);
|
|
789
|
-
setRawHeadingLine(null);
|
|
790
|
-
return;
|
|
791
|
-
}
|
|
792
|
-
const recompute = () => {
|
|
793
|
-
const model = monacoEditor.getModel();
|
|
794
|
-
const pos = monacoEditor.getPosition();
|
|
795
|
-
if (!model || !pos) {
|
|
796
|
-
setRawTemplate(null);
|
|
797
|
-
setRawHeadingLine(null);
|
|
798
|
-
return;
|
|
799
|
-
}
|
|
800
|
-
const line = model.getLineContent(pos.lineNumber);
|
|
801
|
-
const headingMatch = line.match(/^#{1,6}\s+(.+)$/);
|
|
802
|
-
if (!headingMatch) {
|
|
803
|
-
setRawTemplate(null);
|
|
804
|
-
setRawHeadingLine(null);
|
|
805
|
-
return;
|
|
806
|
-
}
|
|
807
|
-
setRawHeadingLine(pos.lineNumber);
|
|
808
|
-
const annotMatch = headingMatch[1].match(/\s*\{\[([^\]]+)\]\}[\s\]}]*$/);
|
|
809
|
-
if (annotMatch) {
|
|
810
|
-
// First whitespace-delimited token is the template name; the rest are params.
|
|
811
|
-
const name = annotMatch[1].trim().split(/\s+/)[0];
|
|
812
|
-
setRawTemplate(name);
|
|
813
|
-
}
|
|
814
|
-
else {
|
|
815
|
-
setRawTemplate('');
|
|
816
|
-
}
|
|
817
|
-
};
|
|
818
|
-
recompute();
|
|
819
|
-
const cursorSub = monacoEditor.onDidChangeCursorPosition(recompute);
|
|
820
|
-
const contentSub = monacoEditor.onDidChangeModelContent(recompute);
|
|
821
|
-
return () => {
|
|
822
|
-
cursorSub.dispose();
|
|
823
|
-
contentSub.dispose();
|
|
824
|
-
};
|
|
825
|
-
}, [isRawView, monacoEditor]);
|
|
826
|
-
// Track the index of the heading the WYSIWYG cursor is in among all
|
|
827
|
-
// top-level headings. Used to locate the same heading in the markdown
|
|
828
|
-
// source for content-based template recommendations.
|
|
829
|
-
const [wysiwygHeadingIndex, setWysiwygHeadingIndex] = useState(null);
|
|
830
|
-
useEffect(() => {
|
|
831
|
-
if (!isWysiwyg || !tiptapEditor) {
|
|
832
|
-
setWysiwygHeadingIndex(null);
|
|
833
|
-
return;
|
|
834
|
-
}
|
|
835
|
-
const recompute = () => {
|
|
836
|
-
if (!tiptapEditor.isActive('heading')) {
|
|
837
|
-
setWysiwygHeadingIndex(null);
|
|
838
|
-
return;
|
|
839
|
-
}
|
|
840
|
-
const cursor = tiptapEditor.state.selection.from;
|
|
841
|
-
let index = -1;
|
|
842
|
-
let count = 0;
|
|
843
|
-
tiptapEditor.state.doc.descendants((node, pos) => {
|
|
844
|
-
if (node.type.name !== 'heading')
|
|
845
|
-
return;
|
|
846
|
-
if (pos <= cursor && pos + node.nodeSize > cursor) {
|
|
847
|
-
index = count;
|
|
848
|
-
return false;
|
|
849
|
-
}
|
|
850
|
-
count++;
|
|
851
|
-
});
|
|
852
|
-
setWysiwygHeadingIndex(index >= 0 ? index : null);
|
|
853
|
-
};
|
|
854
|
-
recompute();
|
|
855
|
-
tiptapEditor.on('selectionUpdate', recompute);
|
|
856
|
-
tiptapEditor.on('update', recompute);
|
|
857
|
-
return () => {
|
|
858
|
-
tiptapEditor.off('selectionUpdate', recompute);
|
|
859
|
-
tiptapEditor.off('update', recompute);
|
|
860
|
-
};
|
|
861
|
-
}, [isWysiwyg, tiptapEditor]);
|
|
862
|
-
const currentTemplate = isWysiwyg ? wysiwygTemplate : isRawView ? rawTemplate : null;
|
|
863
|
-
// Compute recommended templates for the active block. Heading slice
|
|
864
|
-
// comes from markdownSource — raw view supplies the cursor line,
|
|
865
|
-
// WYSIWYG supplies the heading index.
|
|
866
|
-
const recommendedTemplates = useMemo(() => {
|
|
867
|
-
if (currentTemplate === null)
|
|
868
|
-
return undefined;
|
|
869
|
-
let slice = null;
|
|
870
|
-
if (isRawView && rawHeadingLine !== null) {
|
|
871
|
-
slice = findBlockSliceAtLine(markdownSource, rawHeadingLine);
|
|
872
|
-
}
|
|
873
|
-
else if (isWysiwyg && wysiwygHeadingIndex !== null) {
|
|
874
|
-
slice = findBlockSliceByHeadingIndex(markdownSource, wysiwygHeadingIndex);
|
|
875
|
-
}
|
|
876
|
-
if (slice === null)
|
|
877
|
-
return undefined;
|
|
878
|
-
const profile = profileBlockContents(slice);
|
|
879
|
-
return recommendTemplatesForBlock(profile, TEMPLATE_NAMES).recommended;
|
|
880
|
-
}, [currentTemplate, isRawView, isWysiwyg, rawHeadingLine, wysiwygHeadingIndex, markdownSource]);
|
|
881
|
-
const handleTemplatePick = (value) => {
|
|
882
|
-
// Raw (Monaco) — rewrite the heading line's annotation suffix in place.
|
|
883
|
-
if (isRawView && monacoEditor) {
|
|
884
|
-
const model = monacoEditor.getModel();
|
|
885
|
-
const pos = monacoEditor.getPosition();
|
|
886
|
-
if (!model || !pos)
|
|
887
|
-
return;
|
|
888
|
-
const lineNumber = pos.lineNumber;
|
|
889
|
-
const lineText = model.getLineContent(lineNumber);
|
|
890
|
-
const headingMatch = lineText.match(/^(#{1,6}\s+)(.+)$/);
|
|
891
|
-
if (!headingMatch)
|
|
892
|
-
return;
|
|
893
|
-
const prefix = headingMatch[1];
|
|
894
|
-
// Strip any existing trailing annotation
|
|
895
|
-
const bareText = headingMatch[2].replace(/\s*\{\[[^\]]+\]\}[\s\]}]*$/, '').trimEnd();
|
|
896
|
-
const newLine = value === '' ? `${prefix}${bareText}` : `${prefix}${bareText} {[${value}]}`;
|
|
897
|
-
monacoEditor.executeEdits('toolbar-template-pick', [
|
|
898
|
-
{
|
|
899
|
-
range: {
|
|
900
|
-
startLineNumber: lineNumber,
|
|
901
|
-
startColumn: 1,
|
|
902
|
-
endLineNumber: lineNumber,
|
|
903
|
-
endColumn: lineText.length + 1,
|
|
904
|
-
},
|
|
905
|
-
text: newLine,
|
|
906
|
-
},
|
|
907
|
-
]);
|
|
908
|
-
monacoEditor.focus();
|
|
909
|
-
return;
|
|
910
|
-
}
|
|
911
|
-
// WYSIWYG — update the heading node attributes.
|
|
912
|
-
if (!tiptapEditor)
|
|
913
|
-
return;
|
|
914
|
-
if (value === '') {
|
|
915
|
-
tiptapEditor
|
|
916
|
-
.chain()
|
|
917
|
-
.focus()
|
|
918
|
-
.updateAttributes('heading', { dataTemplate: null, dataTemplateParams: null })
|
|
919
|
-
.run();
|
|
920
|
-
}
|
|
921
|
-
else {
|
|
922
|
-
tiptapEditor.chain().focus().updateAttributes('heading', { dataTemplate: value }).run();
|
|
923
|
-
}
|
|
924
|
-
};
|
|
925
|
-
return (_jsxs("div", { className: `squisq-toolbar ${className || ''}`, role: "toolbar", "aria-label": "Formatting toolbar", children: [_jsx("input", { ref: imageInputRef, type: "file", accept: "image/*", style: { display: 'none' }, onChange: (e) => {
|
|
926
|
-
const file = e.target.files?.[0];
|
|
927
|
-
if (file)
|
|
928
|
-
handleImageFile(file);
|
|
929
|
-
// Reset so the same file can be re-selected
|
|
930
|
-
e.target.value = '';
|
|
931
|
-
} }), slotLeft, showViewTabs && (_jsx("div", { className: "squisq-toolbar-view-tabs", role: "tablist", "aria-label": "Editor view", children: visibleViews.map((view) => (_jsxs("button", { role: "tab", "data-view": view.id, "aria-selected": activeView === view.id, className: `squisq-toolbar-view-tab${activeView === view.id ? ' squisq-toolbar-view-tab--active' : ''}`, onClick: () => setActiveView(view.id), "data-tooltip": `${view.label} (${view.shortcut})`, children: [_jsx("span", { className: "squisq-toolbar-view-tab-label squisq-toolbar-view-tab-label--long", "data-label": view.label, children: view.label }), view.shortLabel && view.shortLabel !== view.label && (_jsx("span", { className: "squisq-toolbar-view-tab-label squisq-toolbar-view-tab-label--short", "data-label": view.shortLabel, children: view.shortLabel }))] }, view.id))) })), !isPreview && !isNarrow && !isCodeMode && (_jsxs("div", { className: "squisq-toolbar-actions", ref: actionsRef, children: [groups.map((group, gi) => (_jsxs("div", { className: "squisq-toolbar-group", children: [gi > 0 && _jsx("div", { className: "squisq-toolbar-separator" }), BUTTONS.filter((b) => b.group === group && isButtonVisible(b.id)).map((btn) => {
|
|
932
|
-
const active = btn.id === 'emoji'
|
|
933
|
-
? emojiPickerAnchor !== null
|
|
934
|
-
: isWysiwyg
|
|
935
|
-
? isTiptapActive(tiptapEditor, btn.id)
|
|
936
|
-
: false;
|
|
937
|
-
const disabled = btn.id === 'image' && !mediaProvider;
|
|
938
|
-
return (_jsx("button", { ref: btn.id === 'emoji' ? emojiButtonRef : undefined, className: `squisq-toolbar-button${active ? ' squisq-toolbar-button--active' : ''}`, "data-tooltip": disabled ? 'Insert image (requires media provider)' : btn.title, onClick: () => handleAction(btn.id), "aria-label": btn.title, "aria-pressed": active, disabled: disabled, style: btn.iconStyle, children: buttonIconSvg(btn.id) ?? btn.icon }, btn.id));
|
|
939
|
-
})] }, group))), currentTemplate !== null && (_jsxs(_Fragment, { children: [_jsx("div", { className: "squisq-toolbar-separator" }), _jsx("div", { className: "squisq-toolbar-group squisq-template-picker", children: _jsx(TemplatePicker, { value: currentTemplate, onChange: handleTemplatePick, recommended: recommendedTemplates }) })] })), isInTable && (_jsxs(_Fragment, { children: [_jsx("div", { className: "squisq-toolbar-separator" }), _jsxs("div", { className: "squisq-toolbar-group squisq-table-controls", children: [_jsx("span", { className: "squisq-table-controls-label", children: "Table:" }), _jsx("button", { className: "squisq-toolbar-button", "data-tooltip": "Add column before", onClick: () => tiptapEditor.chain().focus().addColumnBefore().run(), "aria-label": "Add column before", children: _jsxs("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", children: [_jsx("rect", { x: "7", y: "2", width: "8", height: "12", rx: "1" }), _jsx("line", { x1: "11", y1: "2", x2: "11", y2: "14" }), _jsx("line", { x1: "1", y1: "8", x2: "4.5", y2: "8" }), _jsx("line", { x1: "2.75", y1: "6.25", x2: "2.75", y2: "9.75" })] }) }), _jsx("button", { className: "squisq-toolbar-button", "data-tooltip": "Add column after", onClick: () => tiptapEditor.chain().focus().addColumnAfter().run(), "aria-label": "Add column after", children: _jsxs("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", children: [_jsx("rect", { x: "1", y: "2", width: "8", height: "12", rx: "1" }), _jsx("line", { x1: "5", y1: "2", x2: "5", y2: "14" }), _jsx("line", { x1: "11.5", y1: "8", x2: "15", y2: "8" }), _jsx("line", { x1: "13.25", y1: "6.25", x2: "13.25", y2: "9.75" })] }) }), _jsx("button", { className: "squisq-toolbar-button", "data-tooltip": "Delete column", onClick: () => tiptapEditor.chain().focus().deleteColumn().run(), "aria-label": "Delete column", children: _jsxs("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", children: [_jsx("rect", { x: "4", y: "1", width: "8", height: "14", rx: "1" }), _jsx("line", { x1: "6", y1: "5.5", x2: "10", y2: "10.5" }), _jsx("line", { x1: "10", y1: "5.5", x2: "6", y2: "10.5" })] }) }), _jsx("button", { className: "squisq-toolbar-button", "data-tooltip": "Add row above", onClick: () => tiptapEditor.chain().focus().addRowBefore().run(), "aria-label": "Add row above", children: _jsxs("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", children: [_jsx("rect", { x: "2", y: "6", width: "12", height: "9", rx: "1" }), _jsx("line", { x1: "2", y1: "10.5", x2: "14", y2: "10.5" }), _jsx("line", { x1: "8", y1: "1", x2: "8", y2: "4.5" }), _jsx("line", { x1: "6.25", y1: "2.75", x2: "9.75", y2: "2.75" })] }) }), _jsx("button", { className: "squisq-toolbar-button", "data-tooltip": "Add row below", onClick: () => tiptapEditor.chain().focus().addRowAfter().run(), "aria-label": "Add row below", children: _jsxs("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", children: [_jsx("rect", { x: "2", y: "1", width: "12", height: "9", rx: "1" }), _jsx("line", { x1: "2", y1: "5.5", x2: "14", y2: "5.5" }), _jsx("line", { x1: "8", y1: "11.5", x2: "8", y2: "15" }), _jsx("line", { x1: "6.25", y1: "13.25", x2: "9.75", y2: "13.25" })] }) }), _jsx("button", { className: "squisq-toolbar-button", "data-tooltip": "Delete row", onClick: () => tiptapEditor.chain().focus().deleteRow().run(), "aria-label": "Delete row", children: _jsxs("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", children: [_jsx("rect", { x: "1", y: "4", width: "14", height: "8", rx: "1" }), _jsx("line", { x1: "5.5", y1: "6", x2: "10.5", y2: "10" }), _jsx("line", { x1: "10.5", y1: "6", x2: "5.5", y2: "10" })] }) }), _jsx("button", { className: "squisq-toolbar-button squisq-toolbar-button--danger", "data-tooltip": "Delete table", onClick: () => tiptapEditor.chain().focus().deleteTable().run(), "aria-label": "Delete table", children: _jsxs("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", children: [_jsx("rect", { x: "1", y: "1", width: "14", height: "14", rx: "1" }), _jsx("line", { x1: "1", y1: "5.5", x2: "15", y2: "5.5" }), _jsx("line", { x1: "5.5", y1: "1", x2: "5.5", y2: "15" }), _jsx("line", { x1: "4.5", y1: "4.5", x2: "11.5", y2: "11.5", strokeWidth: "2" }), _jsx("line", { x1: "11.5", y1: "4.5", x2: "4.5", y2: "11.5", strokeWidth: "2" })] }) })] })] }))] })), !isPreview && !isCodeMode && overflowIndex !== null && (_jsxs("div", { className: "squisq-toolbar-overflow", ref: overflowRef, children: [_jsx("button", { className: `squisq-toolbar-button squisq-toolbar-overflow-trigger${showOverflow ? ' squisq-toolbar-button--active' : ''}`, "data-tooltip": "More actions", onClick: () => setShowOverflow((v) => !v), "aria-label": "More actions", "aria-expanded": showOverflow, children: "\u00B7\u00B7\u00B7" }), showOverflow && (_jsxs("div", { className: `squisq-toolbar-overflow-menu squisq-toolbar-overflow-menu--${overflowPlacement}`, children: [BUTTONS.slice(overflowIndex)
|
|
940
|
-
.filter((b) => isButtonVisible(b.id))
|
|
941
|
-
.map((btn) => {
|
|
942
|
-
const active = btn.id === 'emoji'
|
|
943
|
-
? emojiPickerAnchor !== null
|
|
944
|
-
: isWysiwyg
|
|
945
|
-
? isTiptapActive(tiptapEditor, btn.id)
|
|
946
|
-
: false;
|
|
947
|
-
const disabled = btn.id === 'image' && !mediaProvider;
|
|
948
|
-
return (_jsxs("button", { ref: btn.id === 'emoji' ? emojiButtonRef : undefined, className: `squisq-toolbar-overflow-item${active ? ' squisq-toolbar-overflow-item--active' : ''}`, onClick: () => {
|
|
949
|
-
handleAction(btn.id);
|
|
950
|
-
// Keep the overflow open when opening the emoji
|
|
951
|
-
// picker — otherwise its anchor (the overflow
|
|
952
|
-
// item) unmounts and the popover loses its ref.
|
|
953
|
-
if (btn.id !== 'emoji')
|
|
954
|
-
setShowOverflow(false);
|
|
955
|
-
}, disabled: disabled, children: [buttonIconSvg(btn.id) ?? (_jsx("span", { className: "squisq-toolbar-overflow-icon", style: btn.iconStyle, children: btn.icon })), _jsx("span", { children: btn.title })] }, btn.id));
|
|
956
|
-
}), currentTemplate !== null && (_jsxs("div", { className: "squisq-toolbar-overflow-item squisq-toolbar-overflow-template", children: [_jsx("span", { children: "Template:" }), _jsx(TemplatePicker, { value: currentTemplate, onChange: (v) => {
|
|
957
|
-
handleTemplatePick(v);
|
|
958
|
-
setShowOverflow(false);
|
|
959
|
-
}, recommended: recommendedTemplates })] })), isInTable && (_jsxs(_Fragment, { children: [_jsx("div", { className: "squisq-toolbar-separator", style: { margin: '4px 0', width: '100%', height: 1 } }), [
|
|
960
|
-
{
|
|
961
|
-
label: 'Add column before',
|
|
962
|
-
action: () => tiptapEditor.chain().focus().addColumnBefore().run(),
|
|
963
|
-
},
|
|
964
|
-
{
|
|
965
|
-
label: 'Add column after',
|
|
966
|
-
action: () => tiptapEditor.chain().focus().addColumnAfter().run(),
|
|
967
|
-
},
|
|
968
|
-
{
|
|
969
|
-
label: 'Delete column',
|
|
970
|
-
action: () => tiptapEditor.chain().focus().deleteColumn().run(),
|
|
971
|
-
},
|
|
972
|
-
{
|
|
973
|
-
label: 'Add row above',
|
|
974
|
-
action: () => tiptapEditor.chain().focus().addRowBefore().run(),
|
|
975
|
-
},
|
|
976
|
-
{
|
|
977
|
-
label: 'Add row below',
|
|
978
|
-
action: () => tiptapEditor.chain().focus().addRowAfter().run(),
|
|
979
|
-
},
|
|
980
|
-
{
|
|
981
|
-
label: 'Delete row',
|
|
982
|
-
action: () => tiptapEditor.chain().focus().deleteRow().run(),
|
|
983
|
-
},
|
|
984
|
-
{
|
|
985
|
-
label: 'Delete table',
|
|
986
|
-
action: () => tiptapEditor.chain().focus().deleteTable().run(),
|
|
987
|
-
},
|
|
988
|
-
].map((item) => (_jsx("button", { className: `squisq-toolbar-overflow-item${item.label.startsWith('Delete') ? ' squisq-toolbar-overflow-item--danger' : ''}`, onClick: () => {
|
|
989
|
-
item.action();
|
|
990
|
-
setShowOverflow(false);
|
|
991
|
-
}, children: _jsx("span", { children: item.label }) }, item.label)))] }))] }))] })), slotAfterActions, (isPreview || isNarrow || isCodeMode) && _jsx("div", { style: { flex: 1 } }), versioning && !isCodeMode && _jsx(VersionHistoryPanel, {}), allowRecording && !isCodeMode && mediaProvider && _jsx(RecorderEntry, {}), !isCodeMode && (_jsx("button", { type: "button", className: "squisq-toolbar-button", onClick: () => setShowDocSettings(true), "data-tooltip": "Document settings", "aria-label": "Document settings", children: _jsxs("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", "aria-hidden": "true", children: [_jsx("path", { d: "M3 2.5h7l3 3v8a1 1 0 0 1-1 1H3a1 1 0 0 1-1-1v-10a1 1 0 0 1 1-1Z", stroke: "currentColor", strokeWidth: "1.3", strokeLinejoin: "round" }), _jsx("path", { d: "M10 2.5v3h3", stroke: "currentColor", strokeWidth: "1.3", strokeLinejoin: "round" }), _jsx("path", { d: "M5 8.5h6M5 11h4", stroke: "currentColor", strokeWidth: "1.3", strokeLinecap: "round" })] }) })), !isCodeMode && _jsx(ViewMenuPanel, {}), onToggleFiles && (_jsx("button", { className: `squisq-toolbar-button squisq-toolbar-files-toggle${showFiles ? ' squisq-toolbar-button--active' : ''}`, onClick: onToggleFiles, "data-tooltip": showFiles ? 'Hide Files panel' : 'Show Files panel', "aria-pressed": showFiles, "aria-label": "Toggle Files panel", children: PAPERCLIP_ICON })), slotRight, showDocSettings && (_jsx(DocumentSettingsDialog, { markdownSource: markdownSource, onSave: (next) => {
|
|
992
|
-
setMarkdownSource(next);
|
|
993
|
-
setShowDocSettings(false);
|
|
994
|
-
}, onClose: () => setShowDocSettings(false) })), linkDialog && (_jsx(LinkDialog, { mode: linkDialog.mode, initialText: linkDialog.initialText, initialUrl: linkDialog.initialUrl, onConfirm: handleLinkConfirm, onClose: () => setLinkDialog(null), documentLinkProvider: documentLinkProvider })), emojiPickerAnchor &&
|
|
995
|
-
createPortal(_jsx(EmojiPicker, { open: true, onSelect: handleEmojiSelect, onClose: closeEmojiPicker, anchorRef: emojiButtonRef, theme: theme === 'dark' ? 'dark' : 'light', style: {
|
|
996
|
-
position: 'fixed',
|
|
997
|
-
top: emojiPickerAnchor.top,
|
|
998
|
-
left: emojiPickerAnchor.left,
|
|
999
|
-
} }), document.body)] }));
|
|
1000
|
-
}
|
|
1001
|
-
//# sourceMappingURL=Toolbar.js.map
|