@bendyline/squisq-editor-react 2.1.0 → 2.2.0
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/LICENSE +21 -0
- package/NOTICE.md +40 -0
- package/dist/index.d.ts +188 -55
- package/dist/index.js +5094 -2948
- package/dist/monaco.js +0 -1
- package/dist/styles/index.css +234 -145
- package/package.json +9 -6
- package/dist/index.js.map +0 -1
- package/dist/monaco.js.map +0 -1
- package/src/BlockCardView.tsx +0 -121
- package/src/BlockPreviewPanel.tsx +0 -69
- package/src/BlockPropertiesPopover.tsx +0 -222
- package/src/DocumentSettingsDialog.tsx +0 -272
- package/src/DropZoneOverlay.tsx +0 -137
- package/src/EditorContext.tsx +0 -1274
- package/src/EditorShell.tsx +0 -1358
- package/src/EmojiPicker.tsx +0 -332
- package/src/FolderView.tsx +0 -131
- package/src/Icon.tsx +0 -26
- package/src/ImageEditor.tsx +0 -395
- package/src/ImageNodeView.tsx +0 -284
- package/src/ImageViewer.tsx +0 -221
- package/src/InlineIcon.ts +0 -84
- package/src/InlinePreviewGutter.tsx +0 -590
- package/src/LinkDialog.tsx +0 -276
- package/src/MediaBin.tsx +0 -500
- package/src/MentionExtension.tsx +0 -261
- package/src/OutlinePanel.tsx +0 -547
- package/src/PlainHtmlPreview.tsx +0 -277
- package/src/PreviewControls.tsx +0 -1265
- package/src/PreviewPanel.tsx +0 -426
- package/src/RawEditor.tsx +0 -743
- package/src/RecorderEntry.tsx +0 -162
- package/src/StatusBar.tsx +0 -51
- package/src/TemplateAnnotation.ts +0 -167
- package/src/TemplateContentPreview.tsx +0 -56
- package/src/TemplatePicker.tsx +0 -1314
- package/src/ThemeCustomizerPanel.tsx +0 -304
- package/src/ThemePicker.tsx +0 -433
- package/src/TimelineBlockPreview.tsx +0 -37
- package/src/TimelineTrack.tsx +0 -671
- package/src/Toolbar.tsx +0 -2885
- package/src/Tooltip.tsx +0 -142
- package/src/TransitionPicker.tsx +0 -390
- package/src/VersionHistoryPanel.tsx +0 -359
- package/src/ViewMenuPanel.tsx +0 -189
- package/src/ViewSwitcher.tsx +0 -56
- package/src/WysiwygEditor.tsx +0 -806
- package/src/__tests__/blockProperties.test.ts +0 -92
- package/src/__tests__/blockPropertiesPopoverTheme.test.tsx +0 -33
- package/src/__tests__/blockRange.test.ts +0 -105
- package/src/__tests__/blockTagActivity.test.ts +0 -183
- package/src/__tests__/buildDocumentPreviewMarkdown.test.ts +0 -75
- package/src/__tests__/buildPreviewDocContent.test.ts +0 -65
- package/src/__tests__/buildPreviewDocTransition.test.ts +0 -92
- package/src/__tests__/canvasSurfaceTextEditing.test.tsx +0 -60
- package/src/__tests__/codeContextSectionView.test.tsx +0 -97
- package/src/__tests__/codeContextZoneManager.test.ts +0 -127
- package/src/__tests__/createShapeLayer.test.ts +0 -46
- package/src/__tests__/customLayoutManagerTheme.test.tsx +0 -34
- package/src/__tests__/detectMarkdown.test.ts +0 -73
- package/src/__tests__/diffContextSections.test.ts +0 -39
- package/src/__tests__/documentSettingsDialog.test.tsx +0 -197
- package/src/__tests__/drawingShapeRoundTrip.test.ts +0 -49
- package/src/__tests__/editorScrollHandoff.test.tsx +0 -102
- package/src/__tests__/editorScrollSync.test.ts +0 -65
- package/src/__tests__/editorShellCodeContext.test.tsx +0 -86
- package/src/__tests__/editorShellProps.test.tsx +0 -550
- package/src/__tests__/embeddedMedia.test.ts +0 -48
- package/src/__tests__/emojiPicker.test.tsx +0 -93
- package/src/__tests__/fileKind.test.ts +0 -112
- package/src/__tests__/findMode.test.tsx +0 -104
- package/src/__tests__/findModel.test.ts +0 -55
- package/src/__tests__/headingTransition.test.ts +0 -176
- package/src/__tests__/imageEditAffordance.test.tsx +0 -272
- package/src/__tests__/imageEditModalContract.test.tsx +0 -119
- package/src/__tests__/imageEditorShell.test.tsx +0 -94
- package/src/__tests__/imageEditorState.test.ts +0 -171
- package/src/__tests__/inlinePreviewGutter.test.tsx +0 -62
- package/src/__tests__/inlinePreviewGutterAllBlocks.test.tsx +0 -103
- package/src/__tests__/jsonEditor.test.tsx +0 -223
- package/src/__tests__/layersPanel.test.tsx +0 -167
- package/src/__tests__/layoutChildRoundTrip.test.ts +0 -71
- package/src/__tests__/linkDialogDocPicker.test.tsx +0 -137
- package/src/__tests__/majorApiTypes.test.ts +0 -26
- package/src/__tests__/markdownCodeFence.test.ts +0 -45
- package/src/__tests__/mediaAttachmentFlow.test.ts +0 -110
- package/src/__tests__/mediaBinDrop.test.tsx +0 -90
- package/src/__tests__/mediaReferences.test.ts +0 -97
- package/src/__tests__/narrationSave.test.ts +0 -148
- package/src/__tests__/outlinePanel.test.tsx +0 -169
- package/src/__tests__/outlineSource.test.ts +0 -217
- package/src/__tests__/plainHtmlPreview.test.tsx +0 -125
- package/src/__tests__/presentationMode.test.tsx +0 -290
- package/src/__tests__/previewControls.test.tsx +0 -571
- package/src/__tests__/previewPanelAudioFailure.test.tsx +0 -46
- package/src/__tests__/previewPanelPresentation.test.tsx +0 -126
- package/src/__tests__/previewPanelTransforms.test.tsx +0 -117
- package/src/__tests__/propertiesPanel.test.tsx +0 -69
- package/src/__tests__/rawEditorModelIsolation.test.ts +0 -19
- package/src/__tests__/recorderFormats.test.ts +0 -146
- package/src/__tests__/recorderMediaInsert.test.ts +0 -86
- package/src/__tests__/recorderTheme.test.tsx +0 -42
- package/src/__tests__/recorderTimingJson.test.ts +0 -41
- package/src/__tests__/resolveBlockVisual.test.ts +0 -43
- package/src/__tests__/selectionConversions.test.ts +0 -80
- package/src/__tests__/statusBar.test.tsx +0 -27
- package/src/__tests__/teleprompter.test.tsx +0 -190
- package/src/__tests__/templateAnnotationRoundTrip.test.ts +0 -99
- package/src/__tests__/templateContentPreview.test.ts +0 -101
- package/src/__tests__/templatePickerMetadata.test.ts +0 -32
- package/src/__tests__/templatePickerPortal.test.tsx +0 -62
- package/src/__tests__/timelineSource.test.ts +0 -134
- package/src/__tests__/tiptapBridge.test.ts +0 -575
- package/src/__tests__/tiptapBridgeConformance.test.ts +0 -47
- package/src/__tests__/tiptapCodeBlockRoundTrip.test.ts +0 -109
- package/src/__tests__/tiptapImageRoundTrip.test.ts +0 -73
- package/src/__tests__/toolbarSelectionConversion.test.tsx +0 -190
- package/src/__tests__/tooltip.test.tsx +0 -72
- package/src/__tests__/transformStyleId.test.ts +0 -13
- package/src/__tests__/transitionCatalog.test.ts +0 -64
- package/src/__tests__/useBlockNavigator.test.tsx +0 -67
- package/src/__tests__/useImageEditor.test.tsx +0 -226
- package/src/__tests__/useJsonEditorTokens.test.ts +0 -59
- package/src/__tests__/useMediaRecorder.test.ts +0 -302
- package/src/__tests__/useTimelineClock.test.ts +0 -21
- package/src/__tests__/versionHistory.test.tsx +0 -197
- package/src/__tests__/viewMenuPanel.test.tsx +0 -130
- package/src/__tests__/writeCanvasSettings.test.ts +0 -15
- package/src/__tests__/wysiwygImageUpload.test.ts +0 -69
- package/src/__tests__/wysiwygTemplateBadgeFocus.test.tsx +0 -56
- package/src/asciiDiagram/AsciiDiagramExtension.ts +0 -343
- package/src/asciiDiagram/AsciiDiagramWidget.tsx +0 -254
- package/src/asciiDiagram/RepairableDiagramExtension.ts +0 -200
- package/src/asciiDiagram/__tests__/AsciiDiagramExtension.test.ts +0 -164
- package/src/asciiDiagram/__tests__/RepairableDiagramExtension.test.ts +0 -159
- package/src/asciiDiagram/__tests__/asciiDiagramCommands.test.ts +0 -284
- package/src/asciiDiagram/__tests__/asciiDiagramOps.test.ts +0 -114
- package/src/asciiDiagram/__tests__/asciiPaste.test.ts +0 -43
- package/src/asciiDiagram/asciiDiagramCommands.ts +0 -174
- package/src/asciiDiagram/asciiDiagramData.ts +0 -149
- package/src/asciiDiagram/asciiDiagramOps.ts +0 -238
- package/src/asciiDiagram/asciiPaste.ts +0 -19
- package/src/blockProperties.ts +0 -88
- package/src/blockRange.ts +0 -132
- package/src/blockSlice.ts +0 -75
- package/src/blockTagActivity.ts +0 -233
- package/src/buildDocumentPreviewMarkdown.ts +0 -168
- package/src/buildPreviewDoc.ts +0 -407
- package/src/codeContext/CodeContextSectionView.tsx +0 -124
- package/src/codeContext/CodeContextZoneManager.ts +0 -149
- package/src/codeContext/CodeContextZones.tsx +0 -121
- package/src/codeContext/diffContextSections.ts +0 -38
- package/src/codeContext/types.ts +0 -75
- package/src/codeSnippet/CodeSnippetExtension.ts +0 -205
- package/src/codeSnippet/CodeSnippetWidget.tsx +0 -109
- package/src/codeSnippet/__tests__/CodeSnippetExtension.test.ts +0 -95
- package/src/codeSnippet/__tests__/codeSnippetLanguages.test.ts +0 -50
- package/src/codeSnippet/codeSnippetCommands.ts +0 -21
- package/src/codeSnippet/codeSnippetData.ts +0 -43
- package/src/codeSnippet/codeSnippetLanguages.ts +0 -216
- package/src/customTemplates/AddBin.tsx +0 -126
- package/src/customTemplates/CustomLayoutManager.tsx +0 -234
- package/src/customTemplates/CustomTemplateContext.tsx +0 -188
- package/src/customTemplates/LayerToolbar.tsx +0 -580
- package/src/customTemplates/ShapeGlyph.tsx +0 -47
- package/src/customTemplates/TemplateDesigner.tsx +0 -433
- package/src/customTemplates/__tests__/library.test.ts +0 -96
- package/src/customTemplates/__tests__/normalizePositions.test.ts +0 -109
- package/src/customTemplates/__tests__/shapeDefs.test.ts +0 -49
- package/src/customTemplates/__tests__/useMemoryLayerAdapter.test.ts +0 -108
- package/src/customTemplates/designer.css +0 -732
- package/src/customTemplates/index.ts +0 -31
- package/src/customTemplates/library.ts +0 -110
- package/src/customTemplates/normalizePositions.ts +0 -75
- package/src/customTemplates/shapeDefs.ts +0 -131
- package/src/customTemplates/thumbnail.tsx +0 -63
- package/src/customTemplates/tokenDefs.ts +0 -60
- package/src/customTemplates/useDocCustomTemplates.ts +0 -52
- package/src/customTemplates/useMemoryLayerAdapter.ts +0 -129
- package/src/customThemes/CustomThemeContext.tsx +0 -185
- package/src/customThemes/CustomThemeDialog.tsx +0 -316
- package/src/customThemes/ImportThemeSection.tsx +0 -178
- package/src/customThemes/__tests__/CustomThemeContext.test.tsx +0 -64
- package/src/customThemes/__tests__/CustomThemeDialog.test.tsx +0 -47
- package/src/customThemes/__tests__/customThemeLibrary.test.ts +0 -60
- package/src/customThemes/__tests__/importThemeSection.test.tsx +0 -192
- package/src/customThemes/customThemeLibrary.ts +0 -100
- package/src/customThemes/index.ts +0 -37
- package/src/customThemes/themeControls.tsx +0 -229
- package/src/customThemes/themeDraft.ts +0 -288
- package/src/customThemes/useDocCustomThemes.ts +0 -49
- package/src/detectMarkdown.ts +0 -62
- package/src/diagram/DiagramCanvas.tsx +0 -260
- package/src/diagram/DiagramMaximizedOverlay.tsx +0 -46
- package/src/diagram/diagramCommands.ts +0 -604
- package/src/diagram/diagramConstants.ts +0 -17
- package/src/diagram/types.ts +0 -35
- package/src/editorScrollSync.ts +0 -73
- package/src/embeddedMedia.ts +0 -78
- package/src/emojiData.ts +0 -1317
- package/src/fileKind.ts +0 -158
- package/src/find/FindHighlightExtension.ts +0 -81
- package/src/find/FindToolbar.tsx +0 -314
- package/src/find/findModel.ts +0 -77
- package/src/frontmatter.ts +0 -29
- package/src/frontmatterSettings.ts +0 -23
- package/src/headingTransition.ts +0 -291
- package/src/hooks/useFileDrop.ts +0 -266
- package/src/imageEditor/CanvasSurface.tsx +0 -703
- package/src/imageEditor/ImageVersionHistoryDropdown.tsx +0 -396
- package/src/imageEditor/LayersPanel.tsx +0 -218
- package/src/imageEditor/PropertiesPanel.tsx +0 -474
- package/src/imageEditor/Toolbar.tsx +0 -459
- package/src/imageEditor/createShapeLayer.ts +0 -280
- package/src/imageEditor/icons.tsx +0 -68
- package/src/imageEditor/image-editor.css +0 -574
- package/src/imageEditor/layers/EditorImageLayer.tsx +0 -45
- package/src/imageEditor/layers/EditorShapeLayer.tsx +0 -62
- package/src/imageEditor/layers/EditorTextLayer.tsx +0 -45
- package/src/imageEditor/layers/SelectionHandles.tsx +0 -86
- package/src/imageEditor/state.ts +0 -173
- package/src/imageEditor/useImageEditor.ts +0 -385
- package/src/imageEditor/useImageEditorTokens.ts +0 -70
- package/src/index.ts +0 -559
- package/src/jsonEditor/EmbeddedRichTextField.tsx +0 -81
- package/src/jsonEditor/JsonEditor.tsx +0 -81
- package/src/jsonEditor/JsonEditorContext.tsx +0 -81
- package/src/jsonEditor/RenderNode.tsx +0 -87
- package/src/jsonEditor/editors.tsx +0 -745
- package/src/jsonEditor/index.ts +0 -2
- package/src/jsonEditor/json-editor.css +0 -463
- package/src/jsonEditor/useJsonEditorTokens.ts +0 -33
- package/src/markdownCodeFence.ts +0 -72
- package/src/mediaDragMime.ts +0 -32
- package/src/mediaEntries.ts +0 -12
- package/src/mediaReferences.ts +0 -301
- package/src/mermaid/MermaidDiagramCanvas.tsx +0 -693
- package/src/mermaid/MermaidDiagramExtension.ts +0 -243
- package/src/mermaid/MermaidDiagramTypeThumbnail.tsx +0 -184
- package/src/mermaid/MermaidDiagramWidget.tsx +0 -653
- package/src/mermaid/MermaidShapePalette.tsx +0 -245
- package/src/mermaid/__tests__/MermaidDiagramExtension.test.ts +0 -361
- package/src/mermaid/__tests__/mermaidDiagramTypes.test.ts +0 -35
- package/src/mermaid/__tests__/mermaidRenderer.test.ts +0 -49
- package/src/mermaid/__tests__/mermaidSourceOps.test.ts +0 -213
- package/src/mermaid/__tests__/mermaidSyntax.test.ts +0 -71
- package/src/mermaid/mermaidCommands.ts +0 -34
- package/src/mermaid/mermaidData.ts +0 -31
- package/src/mermaid/mermaidDiagramTypes.ts +0 -325
- package/src/mermaid/mermaidModel.ts +0 -31
- package/src/mermaid/mermaidRenderer.ts +0 -181
- package/src/mermaid/mermaidShapes.ts +0 -113
- package/src/mermaid/mermaidSourceOps.ts +0 -454
- package/src/monaco.ts +0 -35
- package/src/monacoWorkers.ts +0 -89
- package/src/outlineSource.ts +0 -171
- package/src/presentation/PresentationMode.tsx +0 -596
- package/src/rawEditorIsolation.ts +0 -18
- package/src/recorder/RecorderButton.tsx +0 -80
- package/src/recorder/RecorderModal.tsx +0 -706
- package/src/recorder/RecorderPanel.tsx +0 -77
- package/src/recorder/formats.ts +0 -159
- package/src/recorder/hooks/useMediaRecorder.ts +0 -436
- package/src/recorder/hooks/useStreamPreview.ts +0 -47
- package/src/recorder/insertMediaBlock.ts +0 -30
- package/src/recorder/sources/cameraStream.ts +0 -32
- package/src/recorder/sources/micStream.ts +0 -25
- package/src/recorder/sources/screenStream.ts +0 -162
- package/src/recorder/timingJson.ts +0 -66
- package/src/resolveBlockVisual.ts +0 -39
- package/src/scene/Scene.tsx +0 -647
- package/src/scene/SceneBlockExtension.ts +0 -205
- package/src/scene/SceneBlockToolbar.tsx +0 -216
- package/src/scene/SceneBlockWidget.tsx +0 -441
- package/src/scene/ScenePropsBar.tsx +0 -85
- package/src/scene/SceneSelection.tsx +0 -111
- package/src/scene/SceneSideToolbar.tsx +0 -89
- package/src/scene/SceneViewControls.tsx +0 -43
- package/src/scene/SceneViewport.tsx +0 -106
- package/src/scene/ShapePalette.tsx +0 -196
- package/src/scene/__tests__/DiagramAdapter.test.ts +0 -142
- package/src/scene/__tests__/SceneBlockExtension.test.ts +0 -33
- package/src/scene/__tests__/bezierEdit.test.ts +0 -85
- package/src/scene/__tests__/blockLayers.test.ts +0 -57
- package/src/scene/__tests__/fitScale.test.ts +0 -19
- package/src/scene/__tests__/sceneIsolation.test.tsx +0 -119
- package/src/scene/__tests__/shapeLayers.test.ts +0 -106
- package/src/scene/__tests__/useSceneHitTest.test.ts +0 -90
- package/src/scene/__tests__/useScenePanZoom.test.ts +0 -130
- package/src/scene/adapters/DiagramAdapter.ts +0 -79
- package/src/scene/adapters/DrawingAdapter.ts +0 -420
- package/src/scene/adapters/LayoutAdapter.ts +0 -313
- package/src/scene/adapters/blockLayers.ts +0 -159
- package/src/scene/commands/SceneCommand.ts +0 -84
- package/src/scene/commands/drawingCommands.ts +0 -318
- package/src/scene/commands/layoutCommands.ts +0 -301
- package/src/scene/fitScale.ts +0 -17
- package/src/scene/hooks/useSceneHitTest.ts +0 -105
- package/src/scene/hooks/useScenePanZoom.ts +0 -154
- package/src/scene/hooks/useSceneSelection.ts +0 -62
- package/src/scene/index.ts +0 -90
- package/src/scene/layers/DiagramEdges.tsx +0 -142
- package/src/scene/layers/edgeGeometry.ts +0 -104
- package/src/scene/layers/nodeCard.tsx +0 -164
- package/src/scene/layers/renderLayer.tsx +0 -70
- package/src/scene/layers/shapeLayers.ts +0 -201
- package/src/scene/paths/bezierEdit.ts +0 -208
- package/src/scene/scene.css +0 -874
- package/src/scene/text/SceneTextOverlay.tsx +0 -161
- package/src/scene/text/sceneTextChannel.ts +0 -46
- package/src/scene/text/sceneTextConfig.ts +0 -31
- package/src/scene/text/sceneTiptap.ts +0 -36
- package/src/scene/text/useSceneTextEditing.ts +0 -39
- package/src/scene/tools/ConnectTool.ts +0 -209
- package/src/scene/tools/DrawingConnectTool.ts +0 -231
- package/src/scene/tools/PathTool.ts +0 -158
- package/src/scene/tools/PlaceTool.ts +0 -47
- package/src/scene/tools/SceneTool.ts +0 -85
- package/src/scene/tools/SelectTool.ts +0 -300
- package/src/scene/tools/ShapeTool.ts +0 -143
- package/src/scene/tools/TextTool.ts +0 -72
- package/src/scene/tools/TokenTool.ts +0 -95
- package/src/scene/tools/createDrawShapeTool.ts +0 -82
- package/src/selectionConversions.ts +0 -155
- package/src/styles/ascii-diagram.css +0 -79
- package/src/styles/ascii-timeline.css +0 -596
- package/src/styles/code-context.css +0 -155
- package/src/styles/code-snippet.css +0 -76
- package/src/styles/diagram.css +0 -183
- package/src/styles/editor.css +0 -7131
- package/src/styles/folder-view.css +0 -210
- package/src/styles/image-edit-affordance.css +0 -201
- package/src/styles/index.css +0 -20
- package/src/styles/mermaid-diagram.css +0 -487
- package/src/styles/tree-view.css +0 -189
- package/src/teleprompter/TeleprompterControls.tsx +0 -218
- package/src/teleprompter/TeleprompterSelfView.tsx +0 -22
- package/src/teleprompter/TeleprompterSurface.tsx +0 -267
- package/src/teleprompter/TeleprompterView.tsx +0 -338
- package/src/teleprompter/canvasRenderer.ts +0 -161
- package/src/teleprompter/floatingWindow.ts +0 -352
- package/src/teleprompter/index.ts +0 -61
- package/src/teleprompter/pcmWorklet.ts +0 -62
- package/src/teleprompter/recording/insertPreamble.ts +0 -80
- package/src/teleprompter/recording/narrationSave.ts +0 -134
- package/src/teleprompter/recording/useNarrationRecorder.ts +0 -367
- package/src/teleprompter/scrollModel.ts +0 -85
- package/src/teleprompter/teleprompterTheme.ts +0 -303
- package/src/teleprompter/types.ts +0 -38
- package/src/teleprompter/useFloatingWindow.ts +0 -74
- package/src/teleprompter/useMicAnalysis.ts +0 -211
- package/src/teleprompter/useTeleprompter.ts +0 -421
- package/src/templateContentPreviewResolver.ts +0 -360
- package/src/timeline/TimelineEditorWidget.tsx +0 -861
- package/src/timeline/TimelineViewExtension.ts +0 -252
- package/src/timeline/__tests__/TimelineEditorWidget.test.tsx +0 -414
- package/src/timeline/__tests__/TimelineViewExtension.test.ts +0 -163
- package/src/timeline/__tests__/timelineCommands.test.ts +0 -359
- package/src/timeline/__tests__/timelineOps.test.ts +0 -277
- package/src/timeline/__tests__/timelinePaste.test.ts +0 -34
- package/src/timeline/timelineCommands.ts +0 -421
- package/src/timeline/timelineData.ts +0 -54
- package/src/timeline/timelineOps.ts +0 -381
- package/src/timeline/timelinePaste.ts +0 -8
- package/src/timelineSource.ts +0 -244
- package/src/tiptap/TiptapAudio.tsx +0 -86
- package/src/tiptap/TiptapVideo.tsx +0 -119
- package/src/tiptap/useResolvedMediaSrc.ts +0 -47
- package/src/tiptapBridge.ts +0 -986
- package/src/tooltipPlacement.ts +0 -13
- package/src/transformStyleId.ts +0 -17
- package/src/transitionCatalog.ts +0 -159
- package/src/treeview/TreeOutlineWidget.tsx +0 -390
- package/src/treeview/TreeViewExtension.ts +0 -250
- package/src/treeview/__tests__/TreeOutlineWidget.test.tsx +0 -156
- package/src/treeview/__tests__/TreeViewExtension.test.ts +0 -122
- package/src/treeview/__tests__/treeOps.test.ts +0 -177
- package/src/treeview/__tests__/treePaste.test.ts +0 -40
- package/src/treeview/__tests__/treeViewCommands.test.ts +0 -179
- package/src/treeview/treeOps.ts +0 -231
- package/src/treeview/treePaste.ts +0 -13
- package/src/treeview/treeViewCommands.ts +0 -99
- package/src/treeview/treeViewData.ts +0 -41
- package/src/types/monaco-shims.d.ts +0 -10
- package/src/useBlockNavigator.ts +0 -153
- package/src/useHeadingLayout.ts +0 -294
- package/src/useMonacoLoader.ts +0 -82
- package/src/useTimelineClock.ts +0 -76
- package/src/utils/collectInlineFontAwesomeCss.ts +0 -69
- package/src/utils/dropUtils.ts +0 -136
- package/src/utils/normalizeMalformedAssetUrl.ts +0 -22
- package/src/writeCanvasSettings.ts +0 -30
- package/src/wysiwygImageUpload.ts +0 -113
package/src/EditorContext.tsx
DELETED
|
@@ -1,1274 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* EditorContext
|
|
3
|
-
*
|
|
4
|
-
* Shared React context that synchronizes state across all three editor views
|
|
5
|
-
* (Raw/Monaco, WYSIWYG/Tiptap, Preview/DocPlayer). When any view modifies the
|
|
6
|
-
* markdown source, the context re-parses and regenerates the MarkdownDocument
|
|
7
|
-
* and Doc so all views stay in sync.
|
|
8
|
-
*/
|
|
9
|
-
|
|
10
|
-
import {
|
|
11
|
-
createContext,
|
|
12
|
-
useContext,
|
|
13
|
-
useState,
|
|
14
|
-
useCallback,
|
|
15
|
-
useMemo,
|
|
16
|
-
useRef,
|
|
17
|
-
useEffect,
|
|
18
|
-
type ReactNode,
|
|
19
|
-
} from 'react';
|
|
20
|
-
import type { Doc, MediaProvider } from '@bendyline/squisq/schemas';
|
|
21
|
-
import type { MarkdownDocument } from '@bendyline/squisq/markdown';
|
|
22
|
-
import { parseMarkdown, stringifyMarkdown } from '@bendyline/squisq/markdown';
|
|
23
|
-
import { markdownToDoc } from '@bendyline/squisq/doc';
|
|
24
|
-
import type { ContentContainer } from '@bendyline/squisq/storage';
|
|
25
|
-
import {
|
|
26
|
-
DocumentVersionManager,
|
|
27
|
-
type PrunePolicy,
|
|
28
|
-
type SaveVersionOptions,
|
|
29
|
-
type SaveVersionResult,
|
|
30
|
-
} from '@bendyline/squisq/versions';
|
|
31
|
-
import type { Editor as TiptapEditor } from '@tiptap/core';
|
|
32
|
-
import type { editor as MonacoEditorNs } from 'monaco-editor';
|
|
33
|
-
import { markdownToTiptap } from './tiptapBridge';
|
|
34
|
-
import { resolveFileKind } from './fileKind';
|
|
35
|
-
import { useBlockNavigator } from './useBlockNavigator';
|
|
36
|
-
import {
|
|
37
|
-
createSceneTextChannel,
|
|
38
|
-
type SceneTextChannel,
|
|
39
|
-
type SceneTextHandle,
|
|
40
|
-
} from './scene/text/sceneTextChannel';
|
|
41
|
-
import {
|
|
42
|
-
readMonacoScrollRatio,
|
|
43
|
-
readWysiwygScrollRatio,
|
|
44
|
-
restoreMonacoScrollRatio,
|
|
45
|
-
restoreWysiwygScrollRatio,
|
|
46
|
-
} from './editorScrollSync';
|
|
47
|
-
|
|
48
|
-
/** Monaco standalone code editor instance type */
|
|
49
|
-
type MonacoEditor = MonacoEditorNs.IStandaloneCodeEditor;
|
|
50
|
-
|
|
51
|
-
/**
|
|
52
|
-
* One candidate returned by a {@link MentionProvider}. Shown in the editor's
|
|
53
|
-
* `@` popover. `id` is the stable identifier (serialized into the mention
|
|
54
|
-
* wire format); `label` is what the reader sees; `scheme` is the namespace
|
|
55
|
-
* (e.g. `'user'`, `'issue'`) written into the markdown as `@[label](scheme:id)`;
|
|
56
|
-
* `description` and `group` are optional hints for richer suggestion UIs.
|
|
57
|
-
*
|
|
58
|
-
* Different candidates in the same result set may carry different schemes —
|
|
59
|
-
* a provider that returns both users and issues, for example, tags each
|
|
60
|
-
* candidate with its own namespace and the editor emits mentions accordingly.
|
|
61
|
-
*/
|
|
62
|
-
export interface MentionCandidate {
|
|
63
|
-
id: string;
|
|
64
|
-
label: string;
|
|
65
|
-
scheme: string;
|
|
66
|
-
description?: string;
|
|
67
|
-
group?: string;
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
/**
|
|
71
|
-
* Looks up mention candidates matching a query. Called as the user types
|
|
72
|
-
* after `@`. The provider is free to do server-side or client-side filtering;
|
|
73
|
-
* the editor only cares that candidates come back in relevance order.
|
|
74
|
-
*/
|
|
75
|
-
export type MentionProvider = (query: string) => Promise<MentionCandidate[]>;
|
|
76
|
-
|
|
77
|
-
/**
|
|
78
|
-
* A document that the link dialog's "Browse documents" picker can offer.
|
|
79
|
-
* `path` is what lands in the markdown URL (typically relative to the
|
|
80
|
-
* current document so `home.md → resume.md` round-trips through file-
|
|
81
|
-
* system serializers). `label` is the human name shown in the list.
|
|
82
|
-
* `description` is an optional secondary line (e.g. workspace folder,
|
|
83
|
-
* last-modified date).
|
|
84
|
-
*/
|
|
85
|
-
export interface DocumentLinkCandidate {
|
|
86
|
-
path: string;
|
|
87
|
-
label: string;
|
|
88
|
-
description?: string;
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
/**
|
|
92
|
-
* Resolves sibling / workspace document candidates for the link dialog.
|
|
93
|
-
* The editor itself has no notion of "neighbors" — hosts that organize
|
|
94
|
-
* docs in a workspace (e.g. docblocks) implement this to power the
|
|
95
|
-
* dialog's document picker. Pass `''` as the query for an initial list
|
|
96
|
-
* (the dialog calls it once on open); subsequent calls narrow by user
|
|
97
|
-
* input.
|
|
98
|
-
*/
|
|
99
|
-
export type DocumentLinkProvider = (query: string) => Promise<DocumentLinkCandidate[]>;
|
|
100
|
-
|
|
101
|
-
// ─── Types ───────────────────────────────────────────────
|
|
102
|
-
|
|
103
|
-
export type EditorView = 'raw' | 'wysiwyg' | 'preview';
|
|
104
|
-
/**
|
|
105
|
-
* Light/dark chrome mode for the editor shell (toolbar, tabs, status bar,
|
|
106
|
-
* side panes). This is the editor's *UI color scheme* — distinct from a
|
|
107
|
-
* Squisq `Theme` object, which styles the rendered document. Renamed from
|
|
108
|
-
* the former `EditorTheme` to remove that ambiguity.
|
|
109
|
-
*/
|
|
110
|
-
export type EditorColorScheme = 'light' | 'dark';
|
|
111
|
-
/**
|
|
112
|
-
* Document layout mode. `'document'` shows the whole markdown document in
|
|
113
|
-
* the active view (the historical behavior). `'block'` is the
|
|
114
|
-
* block-at-a-time view — one heading-defined block on a card at a time,
|
|
115
|
-
* with the editor scoped to just that block. `'timeline'` is block mode plus
|
|
116
|
-
* a horizontal timeline track for editing block durations and media slices.
|
|
117
|
-
* See {@link useBlockNavigator}.
|
|
118
|
-
*/
|
|
119
|
-
export type LayoutMode = 'document' | 'block' | 'timeline';
|
|
120
|
-
/**
|
|
121
|
-
* How much of the active Squisq theme the WYSIWYG editing surface
|
|
122
|
-
* mirrors. `'fonts'` is the historical default — body and heading
|
|
123
|
-
* fonts only. `'fonts-colors'` also borrows the theme canvas / text
|
|
124
|
-
* colors. `'none'` opts out completely.
|
|
125
|
-
*/
|
|
126
|
-
export type ThemeInheritance = 'none' | 'fonts' | 'fonts-colors';
|
|
127
|
-
/**
|
|
128
|
-
* When inline block-template tags are shown in the WYSIWYG surface.
|
|
129
|
-
* `'active'` shows tags for the cursor's block and the block under the pointer.
|
|
130
|
-
*/
|
|
131
|
-
export type BlockTagVisibility = 'none' | 'active' | 'always';
|
|
132
|
-
/**
|
|
133
|
-
* Editor operating mode. `markdown` is the full experience (WYSIWYG +
|
|
134
|
-
* Preview tabs, formatting toolbar). `code` is a Monaco-only view used
|
|
135
|
-
* when the content represents a non-markdown file like `foo.ts`.
|
|
136
|
-
*/
|
|
137
|
-
export type EditorMode = 'markdown' | 'code' | 'image';
|
|
138
|
-
|
|
139
|
-
export interface EditorState {
|
|
140
|
-
/** Raw markdown source string */
|
|
141
|
-
markdownSource: string;
|
|
142
|
-
/** Parsed markdown document (JSON DOM) */
|
|
143
|
-
markdownDoc: MarkdownDocument | null;
|
|
144
|
-
/** Generated Doc (block hierarchy) */
|
|
145
|
-
doc: Doc | null;
|
|
146
|
-
/** Currently active editor view */
|
|
147
|
-
activeView: EditorView;
|
|
148
|
-
/** Parse error, if any */
|
|
149
|
-
parseError: string | null;
|
|
150
|
-
/** Whether a parse is pending */
|
|
151
|
-
isParsing: boolean;
|
|
152
|
-
/** Current light/dark chrome color scheme for the editor shell. */
|
|
153
|
-
colorScheme: EditorColorScheme;
|
|
154
|
-
/** Operating mode — 'markdown' for the full shell, 'code' for Monaco-only. */
|
|
155
|
-
editorMode: EditorMode;
|
|
156
|
-
/** Whether the host-triggered Find toolbar is active. */
|
|
157
|
-
findMode: boolean;
|
|
158
|
-
/** Monaco language ID for the Raw editor. */
|
|
159
|
-
language: string;
|
|
160
|
-
/**
|
|
161
|
-
* Whether the inline preview gutter (per-block card previews next to the
|
|
162
|
-
* WYSIWYG surface) is currently visible. Initialized from the EditorShell
|
|
163
|
-
* `inlinePreview` prop; the View menu in the toolbar can toggle it at
|
|
164
|
-
* runtime.
|
|
165
|
-
*/
|
|
166
|
-
inlinePreviewVisible: boolean;
|
|
167
|
-
/**
|
|
168
|
-
* Whether the bottom status bar is currently visible. Initialized from
|
|
169
|
-
* the EditorShell `showStatusBar` prop (default true); the View menu in
|
|
170
|
-
* the toolbar can toggle it at runtime.
|
|
171
|
-
*/
|
|
172
|
-
statusBarVisible: boolean;
|
|
173
|
-
/**
|
|
174
|
-
* Whether the left-side outline pane is currently visible. Initialized
|
|
175
|
-
* from the EditorShell `outline` prop (default false); the View menu in
|
|
176
|
-
* the toolbar can toggle it at runtime.
|
|
177
|
-
*/
|
|
178
|
-
outlineVisible: boolean;
|
|
179
|
-
/** When inline block-template tags are shown in the WYSIWYG view. */
|
|
180
|
-
blockTagVisibility: BlockTagVisibility;
|
|
181
|
-
/**
|
|
182
|
-
* Whether inline block-template tags can currently be visible.
|
|
183
|
-
* Kept for compatibility; prefer {@link blockTagVisibility}.
|
|
184
|
-
*/
|
|
185
|
-
blockTagsVisible: boolean;
|
|
186
|
-
/**
|
|
187
|
-
* How much of the active Squisq theme the WYSIWYG editing surface should
|
|
188
|
-
* inherit. `'none'` shows the default editor styling, `'fonts'` (the
|
|
189
|
-
* default) matches body and heading fonts only, and `'fonts-colors'`
|
|
190
|
-
* also borrows the theme's canvas / text colors so authors get a
|
|
191
|
-
* closer preview while editing.
|
|
192
|
-
*/
|
|
193
|
-
themeInheritance: ThemeInheritance;
|
|
194
|
-
/**
|
|
195
|
-
* Relative path of an image the user requested to edit, or `null` when
|
|
196
|
-
* no editor is open. Surfaced by `<ImageNodeView>`'s hover affordance
|
|
197
|
-
* and consumed by `<EditorShell>` to render the modal `<ImageEditor>`.
|
|
198
|
-
*/
|
|
199
|
-
imageEditTarget: string | null;
|
|
200
|
-
/**
|
|
201
|
-
* Monotonic counter bumped whenever a managed media asset is rewritten
|
|
202
|
-
* (e.g. after the image-editor modal saves back). Image render paths
|
|
203
|
-
* that cache resolved blob URLs should include this in their effect
|
|
204
|
-
* deps so the new bytes get picked up.
|
|
205
|
-
*/
|
|
206
|
-
mediaRevision: number;
|
|
207
|
-
/**
|
|
208
|
-
* Whether the in-editor media recorder should be available. Defaults
|
|
209
|
-
* to true when a `mediaProvider` is wired; hosts that explicitly
|
|
210
|
-
* don't want the affordance (e.g. read-only embeds, surfaces where
|
|
211
|
-
* camera/screen prompts would be jarring) can pass `false` on the
|
|
212
|
-
* shell.
|
|
213
|
-
*/
|
|
214
|
-
allowRecording: boolean;
|
|
215
|
-
/**
|
|
216
|
-
* Whether the Narrate (teleprompter) display mode is offered under the
|
|
217
|
-
* Use tab. Orthogonal to `allowRecording`: the teleprompter is useful
|
|
218
|
-
* without any capture (reading for external recording software), and a
|
|
219
|
-
* host may allow the recorder modal but not want a prompter surface.
|
|
220
|
-
*/
|
|
221
|
-
allowNarrate: boolean;
|
|
222
|
-
/**
|
|
223
|
-
* Document layout mode. `'document'` (default) edits the whole document;
|
|
224
|
-
* `'block'` activates the block-at-a-time card view. Initialized from the
|
|
225
|
-
* EditorShell `layoutMode` prop; the View menu can toggle it at runtime.
|
|
226
|
-
*/
|
|
227
|
-
layoutMode: LayoutMode;
|
|
228
|
-
/**
|
|
229
|
-
* The markdown the active text editor should bind to: the full source in
|
|
230
|
-
* `'document'` mode, or just the active block's slice in `'block'` mode.
|
|
231
|
-
* Editors read this instead of `markdownSource` so the same surfaces work
|
|
232
|
-
* in both layouts.
|
|
233
|
-
*/
|
|
234
|
-
editorSource: string;
|
|
235
|
-
/** Number of navigable blocks (cards) in the current document. */
|
|
236
|
-
blockCount: number;
|
|
237
|
-
/** Index of the block currently shown on the card (block mode). */
|
|
238
|
-
activeBlockKey: number;
|
|
239
|
-
/** 1-based source line where the active block begins, or null. */
|
|
240
|
-
activeBlockStartLine: number | null;
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
export interface EditorActions {
|
|
244
|
-
/** Set markdown source and trigger re-parse */
|
|
245
|
-
setMarkdownSource: (source: string) => void;
|
|
246
|
-
/**
|
|
247
|
-
* Write through the active editor channel. In `'document'` mode this is
|
|
248
|
-
* `setMarkdownSource`; in `'block'` mode it splices the edited block back
|
|
249
|
-
* into the full document. Editors call this instead of `setMarkdownSource`.
|
|
250
|
-
*/
|
|
251
|
-
setEditorSource: (source: string) => void;
|
|
252
|
-
/** Switch between Document and Block-at-a-time layouts. */
|
|
253
|
-
setLayoutMode: (mode: LayoutMode) => void;
|
|
254
|
-
/** Show a block by index in block mode (clamped to range). */
|
|
255
|
-
goToBlock: (key: number) => void;
|
|
256
|
-
/** Show the block that owns a given 1-based source line (used by the outline). */
|
|
257
|
-
goToBlockByLine: (line: number) => void;
|
|
258
|
-
/** Move the card to the previous block. */
|
|
259
|
-
prevBlock: () => void;
|
|
260
|
-
/** Move the card to the next block. */
|
|
261
|
-
nextBlock: () => void;
|
|
262
|
-
/** Insert a new heading block after the active one and move to it. */
|
|
263
|
-
addBlock: () => void;
|
|
264
|
-
/** Set markdown from a MarkdownDocument (e.g. from WYSIWYG) */
|
|
265
|
-
setMarkdownDoc: (doc: MarkdownDocument) => void;
|
|
266
|
-
/** Switch the active view */
|
|
267
|
-
setActiveView: (view: EditorView) => void;
|
|
268
|
-
/** Enter or leave the host-triggered Find toolbar mode. */
|
|
269
|
-
setFindMode: (active: boolean) => void;
|
|
270
|
-
/** Register / unregister the Tiptap editor instance (called by WysiwygEditor) */
|
|
271
|
-
setTiptapEditor: (editor: TiptapEditor | null) => void;
|
|
272
|
-
/** Register / unregister the Monaco editor instance (called by RawEditor) */
|
|
273
|
-
setMonacoEditor: (editor: MonacoEditor | null) => void;
|
|
274
|
-
/** Set the light/dark chrome color scheme for the editor shell. */
|
|
275
|
-
setColorScheme: (colorScheme: EditorColorScheme) => void;
|
|
276
|
-
/** Show or hide the inline preview gutter at runtime (driven by the View menu). */
|
|
277
|
-
setInlinePreviewVisible: (visible: boolean) => void;
|
|
278
|
-
/** Show or hide the bottom status bar at runtime (driven by the View menu). */
|
|
279
|
-
setStatusBarVisible: (visible: boolean) => void;
|
|
280
|
-
/** Show or hide the left-side outline pane at runtime (driven by the View menu). */
|
|
281
|
-
setOutlineVisible: (visible: boolean) => void;
|
|
282
|
-
/** Show or hide inline block-template tags at runtime (driven by the View menu). */
|
|
283
|
-
setBlockTagsVisible: (visible: boolean) => void;
|
|
284
|
-
/** Choose when inline block-template tags are shown. */
|
|
285
|
-
setBlockTagVisibility: (visibility: BlockTagVisibility) => void;
|
|
286
|
-
/** Change how much of the active Squisq theme the WYSIWYG surface mirrors. */
|
|
287
|
-
setThemeInheritance: (mode: ThemeInheritance) => void;
|
|
288
|
-
/** Insert text at the current cursor position in the active editor */
|
|
289
|
-
insertAtCursor: (text: string) => void;
|
|
290
|
-
/** Replace all editor content with the given text */
|
|
291
|
-
replaceAll: (text: string) => void;
|
|
292
|
-
/**
|
|
293
|
-
* Request the modal image editor open on the given relative media path.
|
|
294
|
-
* The path must resolve through the active `mediaProvider`. No-op when
|
|
295
|
-
* no provider is wired — callers should hide the affordance in that
|
|
296
|
-
* case.
|
|
297
|
-
*/
|
|
298
|
-
openImageEdit: (relativePath: string) => void;
|
|
299
|
-
/** Close the image editor modal without saving. */
|
|
300
|
-
closeImageEdit: () => void;
|
|
301
|
-
/**
|
|
302
|
-
* Bump `mediaRevision`. Called after the image editor writes back to
|
|
303
|
-
* the original media path so dependent `<img>` nodes re-resolve their
|
|
304
|
-
* blob URL.
|
|
305
|
-
*/
|
|
306
|
-
bumpMediaRevision: () => void;
|
|
307
|
-
}
|
|
308
|
-
|
|
309
|
-
export interface EditorContextValue extends EditorState, EditorActions {
|
|
310
|
-
/** The live Tiptap editor instance (null when WYSIWYG is not mounted) */
|
|
311
|
-
tiptapEditor: TiptapEditor | null;
|
|
312
|
-
/** The live Monaco editor instance (null when Raw is not mounted) */
|
|
313
|
-
monacoEditor: MonacoEditor | null;
|
|
314
|
-
/**
|
|
315
|
-
* The focused canvas textbox editor, if any — a small Tiptap instance for
|
|
316
|
-
* a diagram/drawing/layout textbox being edited inline. Published via
|
|
317
|
-
* `sceneTextChannel` (the canvas renders in a detached React root). The
|
|
318
|
-
* top formatting toolbar retargets to this when set. `level` gates which
|
|
319
|
-
* buttons apply (`inline` = marks only; `rich` = headings/lists too).
|
|
320
|
-
*/
|
|
321
|
-
activeSceneText: SceneTextHandle | null;
|
|
322
|
-
/** Instance-owned bridge used by detached scene widget roots. */
|
|
323
|
-
sceneTextChannel: SceneTextChannel;
|
|
324
|
-
/**
|
|
325
|
-
* Workspace-scoped `ContentContainer` for this document — the folder
|
|
326
|
-
* holding the doc, its `_files/` sidecar, sibling documents, and any
|
|
327
|
-
* version snapshots. Drives audio mapping, version history, and
|
|
328
|
-
* sibling-doc reads for the recursive HTML export.
|
|
329
|
-
*/
|
|
330
|
-
workspaceContainer: ContentContainer | null;
|
|
331
|
-
/**
|
|
332
|
-
* Version manager — non-null only when the host opted into versioning
|
|
333
|
-
* (`allowVersioning` + a `workspaceContainer`). Components can call
|
|
334
|
-
* `saveVersion` directly, or render the version-history panel which
|
|
335
|
-
* reads it from here.
|
|
336
|
-
*/
|
|
337
|
-
versioning: DocumentVersionManager | null;
|
|
338
|
-
/**
|
|
339
|
-
* Stamp a new snapshot of the current document. No-op (returns
|
|
340
|
-
* `unchanged`) when content matches the latest version. Always safe
|
|
341
|
-
* to call — when versioning is disabled, returns `no-document`
|
|
342
|
-
* without writing.
|
|
343
|
-
*/
|
|
344
|
-
saveVersion: (options?: SaveVersionOptions) => Promise<SaveVersionResult>;
|
|
345
|
-
/** MediaProvider for resolving image URLs in the WYSIWYG editor */
|
|
346
|
-
mediaProvider: MediaProvider | null;
|
|
347
|
-
/**
|
|
348
|
-
* How pasted/inserted images should be displayed in the WYSIWYG view.
|
|
349
|
-
* `'inline'` (default) lets them flow at natural size up to the editor
|
|
350
|
-
* width; `'thumbnail'` constrains them to a 100×100 box so chat
|
|
351
|
-
* composers and other dense surfaces don't get dominated by a single
|
|
352
|
-
* pasted screenshot. The stored image bytes are unchanged — this is a
|
|
353
|
-
* pure render-time decision.
|
|
354
|
-
*/
|
|
355
|
-
imageDisplayMode: ImageDisplayMode;
|
|
356
|
-
/**
|
|
357
|
-
* Optional provider for `@`-mention suggestions. When set, both the
|
|
358
|
-
* WYSIWYG (Tiptap) and Raw (Monaco) editors show a mention popover as
|
|
359
|
-
* the user types `@<query>`. When unset, `@` is just a literal character.
|
|
360
|
-
*/
|
|
361
|
-
mentionProvider: MentionProvider | null;
|
|
362
|
-
/**
|
|
363
|
-
* Optional provider for sibling-document suggestions in the link
|
|
364
|
-
* dialog. When set, the dialog shows a "Browse documents" picker that
|
|
365
|
-
* lets authors search neighbor docs by name and insert a relative-
|
|
366
|
-
* path link. When unset, the dialog falls back to URL-only.
|
|
367
|
-
*/
|
|
368
|
-
documentLinkProvider: DocumentLinkProvider | null;
|
|
369
|
-
}
|
|
370
|
-
|
|
371
|
-
export type ImageDisplayMode = 'inline' | 'thumbnail';
|
|
372
|
-
|
|
373
|
-
// ─── Context ─────────────────────────────────────────────
|
|
374
|
-
|
|
375
|
-
const EditorContext = createContext<EditorContextValue | null>(null);
|
|
376
|
-
|
|
377
|
-
/**
|
|
378
|
-
* Hook to access the editor context. Must be used within an EditorProvider.
|
|
379
|
-
*/
|
|
380
|
-
// eslint-disable-next-line react-refresh/only-export-components
|
|
381
|
-
export function useEditorContext(): EditorContextValue {
|
|
382
|
-
const ctx = useContext(EditorContext);
|
|
383
|
-
if (!ctx) {
|
|
384
|
-
throw new Error('useEditorContext must be used within an <EditorProvider>');
|
|
385
|
-
}
|
|
386
|
-
return ctx;
|
|
387
|
-
}
|
|
388
|
-
|
|
389
|
-
// ─── Provider ────────────────────────────────────────────
|
|
390
|
-
|
|
391
|
-
export interface EditorProviderProps {
|
|
392
|
-
/** Initial markdown content */
|
|
393
|
-
initialMarkdown?: string;
|
|
394
|
-
/** Initial active view */
|
|
395
|
-
initialView?: EditorView;
|
|
396
|
-
/** Article ID used when generating the Doc */
|
|
397
|
-
articleId?: string;
|
|
398
|
-
/** Light/dark chrome color scheme for the editor shell. */
|
|
399
|
-
colorScheme?: EditorColorScheme;
|
|
400
|
-
/**
|
|
401
|
-
* Workspace-scoped `ContentContainer` for this document — the folder
|
|
402
|
-
* holding the doc, its `_files/` sidecar, sibling documents, and any
|
|
403
|
-
* version snapshots. Required for `allowVersioning` to take effect.
|
|
404
|
-
*/
|
|
405
|
-
workspaceContainer?: ContentContainer | null;
|
|
406
|
-
/**
|
|
407
|
-
* Enable version history. Snapshots are stored at
|
|
408
|
-
* `.versions/<basename>.<timestamp>.md` inside `workspaceContainer`.
|
|
409
|
-
* Auto-save fires after `versioningAutoSaveIdleMs` of idle; hosts can
|
|
410
|
-
* also call `saveVersion()` from the context. Without a
|
|
411
|
-
* `workspaceContainer`, this prop is ignored (and a `console.warn` is
|
|
412
|
-
* emitted).
|
|
413
|
-
*/
|
|
414
|
-
allowVersioning?: boolean;
|
|
415
|
-
/** Override the basename used in version filenames. Defaults to the
|
|
416
|
-
* basename of the container's primary document path. */
|
|
417
|
-
versionBasename?: string;
|
|
418
|
-
/**
|
|
419
|
-
* Prune policy applied after each successful auto-save. Defaults to
|
|
420
|
-
* keeping the last 50 snapshots so the count doesn't grow unbounded.
|
|
421
|
-
*/
|
|
422
|
-
versioningPrunePolicy?: PrunePolicy;
|
|
423
|
-
/**
|
|
424
|
-
* Idle delay (ms) before auto-saving a version. `0` disables auto-save
|
|
425
|
-
* entirely (versions are saved only via host-driven `saveVersion`
|
|
426
|
-
* calls). Default: 5000.
|
|
427
|
-
*/
|
|
428
|
-
versioningAutoSaveIdleMs?: number;
|
|
429
|
-
/**
|
|
430
|
-
* Notified after each `saveVersion` attempt — both successful saves
|
|
431
|
-
* (`reason: 'saved'`) and skips (`'unchanged'`, `'no-document'`,
|
|
432
|
-
* `'empty'`). Useful for hosts that want a "Last saved" indicator.
|
|
433
|
-
*/
|
|
434
|
-
onSaveVersion?: (result: SaveVersionResult) => void;
|
|
435
|
-
/** MediaProvider for resolving image URLs */
|
|
436
|
-
mediaProvider?: MediaProvider | null;
|
|
437
|
-
/** Display mode for images in the WYSIWYG view. Defaults to `'inline'`. */
|
|
438
|
-
imageDisplayMode?: ImageDisplayMode;
|
|
439
|
-
/**
|
|
440
|
-
* Async provider for `@`-mention suggestions. Omit to disable mentions
|
|
441
|
-
* entirely — typing `@` becomes just a literal character again.
|
|
442
|
-
*/
|
|
443
|
-
mentionProvider?: MentionProvider | null;
|
|
444
|
-
/**
|
|
445
|
-
* Async provider for sibling-document suggestions in the link dialog.
|
|
446
|
-
* Omit to fall back to URL-only link insertion.
|
|
447
|
-
*/
|
|
448
|
-
documentLinkProvider?: DocumentLinkProvider | null;
|
|
449
|
-
/**
|
|
450
|
-
* Whether the in-editor media recorder is available in the toolbar.
|
|
451
|
-
* Defaults to true. Set to false to suppress the recorder affordance
|
|
452
|
-
* even when a `mediaProvider` is wired (e.g. read-only embeds,
|
|
453
|
-
* surfaces where camera/screen prompts would be jarring).
|
|
454
|
-
*/
|
|
455
|
-
allowRecording?: boolean;
|
|
456
|
-
/**
|
|
457
|
-
* Whether the Narrate (teleprompter) display mode is offered under the
|
|
458
|
-
* Use tab. Defaults to true. When false the mode button is hidden and a
|
|
459
|
-
* frontmatter-forced `display-mode: narrate` clamps back to video.
|
|
460
|
-
*/
|
|
461
|
-
allowNarrate?: boolean;
|
|
462
|
-
/**
|
|
463
|
-
* File name (e.g. `foo.ts`) or bare extension — used to pick a Monaco
|
|
464
|
-
* language and decide between markdown vs. code mode.
|
|
465
|
-
*/
|
|
466
|
-
fileName?: string;
|
|
467
|
-
/** Explicit Monaco language ID — wins over the fileName-derived one. */
|
|
468
|
-
language?: string;
|
|
469
|
-
/**
|
|
470
|
-
* Controlled Find-mode state. No trigger button is rendered by default;
|
|
471
|
-
* hosts can set this prop or call `setFindMode` from editor context.
|
|
472
|
-
*/
|
|
473
|
-
findMode?: boolean;
|
|
474
|
-
/** Notified when Find mode requests a state change (for example, its X button). */
|
|
475
|
-
onFindModeChange?: (active: boolean) => void;
|
|
476
|
-
/**
|
|
477
|
-
* Initial visibility of the inline preview gutter. Defaults to false.
|
|
478
|
-
* The toolbar's View menu can toggle it at runtime.
|
|
479
|
-
*/
|
|
480
|
-
inlinePreview?: boolean;
|
|
481
|
-
/**
|
|
482
|
-
* Initial visibility of the bottom status bar. Defaults to true.
|
|
483
|
-
* The toolbar's View menu can toggle it at runtime.
|
|
484
|
-
*/
|
|
485
|
-
showStatusBar?: boolean;
|
|
486
|
-
/**
|
|
487
|
-
* Initial visibility of the left-side outline pane. Defaults to false.
|
|
488
|
-
* The toolbar's View menu can toggle it at runtime.
|
|
489
|
-
*/
|
|
490
|
-
outline?: boolean;
|
|
491
|
-
/**
|
|
492
|
-
* Legacy initial visibility of inline block-template tags on headings.
|
|
493
|
-
* `true` maps to always visible and `false` maps to hidden. When omitted,
|
|
494
|
-
* {@link blockTagVisibility} defaults to `'active'`.
|
|
495
|
-
*/
|
|
496
|
-
blockTags?: boolean;
|
|
497
|
-
/**
|
|
498
|
-
* Initial block-tag visibility mode. When set, this takes precedence over
|
|
499
|
-
* the legacy boolean {@link blockTags} prop. Defaults to `'active'`.
|
|
500
|
-
*/
|
|
501
|
-
blockTagVisibility?: BlockTagVisibility;
|
|
502
|
-
/**
|
|
503
|
-
* Initial value for how much of the active Squisq theme the WYSIWYG
|
|
504
|
-
* editing surface should mirror. Defaults to `'fonts'` — the
|
|
505
|
-
* historical behavior of inheriting body / heading fonts only. The
|
|
506
|
-
* toolbar's View menu can change it at runtime.
|
|
507
|
-
*/
|
|
508
|
-
themeInheritance?: ThemeInheritance;
|
|
509
|
-
/**
|
|
510
|
-
* Initial layout mode. Defaults to `'document'` (whole-document editing).
|
|
511
|
-
* `'block'` boots into the block-at-a-time card view. The toolbar's View
|
|
512
|
-
* menu can toggle it at runtime.
|
|
513
|
-
*/
|
|
514
|
-
layoutMode?: LayoutMode;
|
|
515
|
-
/**
|
|
516
|
-
* Bundled view preferences — a serializable JSON blob covering all
|
|
517
|
-
* runtime-toggleable view options. When provided, individual values
|
|
518
|
-
* here override the matching individual props (`inlinePreview`,
|
|
519
|
-
* `showStatusBar`, `outline`, `blockTagVisibility`, `blockTags`). Hosts
|
|
520
|
-
* wiring this up typically load the blob from their own preferences storage
|
|
521
|
-
* and pair it with {@link onViewPreferencesChange}.
|
|
522
|
-
*/
|
|
523
|
-
viewPreferences?: ViewPreferences;
|
|
524
|
-
/**
|
|
525
|
-
* Notified after each user-driven toggle in the View menu (or any
|
|
526
|
-
* programmatic call to the corresponding context setters). The
|
|
527
|
-
* argument is a full snapshot — hosts can persist it as-is.
|
|
528
|
-
* Not called when {@link viewPreferences} is changed externally.
|
|
529
|
-
*/
|
|
530
|
-
onViewPreferencesChange?: (prefs: ViewPreferences) => void;
|
|
531
|
-
children: ReactNode;
|
|
532
|
-
}
|
|
533
|
-
|
|
534
|
-
/**
|
|
535
|
-
* Serializable bundle of all runtime-toggleable view preferences for
|
|
536
|
-
* the editor shell. Hosts can persist this verbatim (e.g. to
|
|
537
|
-
* localStorage) and pass it back via {@link EditorProviderProps.viewPreferences}
|
|
538
|
-
* to restore the user's last view configuration.
|
|
539
|
-
*/
|
|
540
|
-
export interface ViewPreferences {
|
|
541
|
-
/** Whether the left-side outline pane is visible. */
|
|
542
|
-
outline?: boolean;
|
|
543
|
-
/** Whether the inline preview gutter (per-block cards) is visible. */
|
|
544
|
-
inlinePreview?: boolean;
|
|
545
|
-
/** Whether the bottom status bar is visible. */
|
|
546
|
-
showStatusBar?: boolean;
|
|
547
|
-
/** Whether inline block-template tags on headings are visible. */
|
|
548
|
-
blockTags?: boolean;
|
|
549
|
-
/** When inline block-template tags are shown. Takes precedence over `blockTags`. */
|
|
550
|
-
blockTagVisibility?: BlockTagVisibility;
|
|
551
|
-
/** How much of the active Squisq theme the WYSIWYG surface mirrors. */
|
|
552
|
-
themeInheritance?: ThemeInheritance;
|
|
553
|
-
/** Document vs. block-at-a-time layout. */
|
|
554
|
-
layoutMode?: LayoutMode;
|
|
555
|
-
}
|
|
556
|
-
|
|
557
|
-
/**
|
|
558
|
-
* Provides shared editor state to all child components.
|
|
559
|
-
* Automatically parses markdown and generates a Doc whenever the source changes.
|
|
560
|
-
*/
|
|
561
|
-
const DEFAULT_PRUNE_POLICY: PrunePolicy = { type: 'keep-last-n', n: 50 };
|
|
562
|
-
const DEFAULT_AUTOSAVE_IDLE_MS = 5_000;
|
|
563
|
-
|
|
564
|
-
function normalizeBlockTagVisibility(
|
|
565
|
-
value: BlockTagVisibility | boolean | undefined,
|
|
566
|
-
): BlockTagVisibility {
|
|
567
|
-
if (value === false || value === 'none') return 'none';
|
|
568
|
-
if (value === undefined || value === 'active') return 'active';
|
|
569
|
-
return 'always';
|
|
570
|
-
}
|
|
571
|
-
|
|
572
|
-
export function EditorProvider({
|
|
573
|
-
initialMarkdown = '',
|
|
574
|
-
initialView = 'raw',
|
|
575
|
-
articleId = 'untitled',
|
|
576
|
-
colorScheme: initialColorScheme = 'light',
|
|
577
|
-
workspaceContainer = null,
|
|
578
|
-
allowVersioning = false,
|
|
579
|
-
versionBasename,
|
|
580
|
-
versioningPrunePolicy = DEFAULT_PRUNE_POLICY,
|
|
581
|
-
versioningAutoSaveIdleMs = DEFAULT_AUTOSAVE_IDLE_MS,
|
|
582
|
-
onSaveVersion,
|
|
583
|
-
mediaProvider = null,
|
|
584
|
-
imageDisplayMode = 'inline',
|
|
585
|
-
mentionProvider = null,
|
|
586
|
-
documentLinkProvider = null,
|
|
587
|
-
allowRecording = true,
|
|
588
|
-
allowNarrate = true,
|
|
589
|
-
fileName,
|
|
590
|
-
language,
|
|
591
|
-
findMode: controlledFindMode,
|
|
592
|
-
onFindModeChange,
|
|
593
|
-
inlinePreview = false,
|
|
594
|
-
showStatusBar = true,
|
|
595
|
-
outline = false,
|
|
596
|
-
blockTags,
|
|
597
|
-
blockTagVisibility: initialBlockTagVisibility,
|
|
598
|
-
themeInheritance = 'fonts',
|
|
599
|
-
layoutMode = 'document',
|
|
600
|
-
viewPreferences,
|
|
601
|
-
onViewPreferencesChange,
|
|
602
|
-
children,
|
|
603
|
-
}: EditorProviderProps) {
|
|
604
|
-
// Resolve effective initial values: bundled `viewPreferences` wins over
|
|
605
|
-
// individual props when both are passed. Individual props remain valid
|
|
606
|
-
// for hosts that haven't migrated to the bundled API.
|
|
607
|
-
const effectiveInlinePreview = viewPreferences?.inlinePreview ?? inlinePreview;
|
|
608
|
-
const effectiveShowStatusBar = viewPreferences?.showStatusBar ?? showStatusBar;
|
|
609
|
-
const effectiveOutline = viewPreferences?.outline ?? outline;
|
|
610
|
-
const effectiveBlockTagVisibility = normalizeBlockTagVisibility(
|
|
611
|
-
viewPreferences?.blockTagVisibility ??
|
|
612
|
-
viewPreferences?.blockTags ??
|
|
613
|
-
initialBlockTagVisibility ??
|
|
614
|
-
blockTags,
|
|
615
|
-
);
|
|
616
|
-
const effectiveThemeInheritance = viewPreferences?.themeInheritance ?? themeInheritance;
|
|
617
|
-
const effectiveLayoutMode = viewPreferences?.layoutMode ?? layoutMode;
|
|
618
|
-
// Resolve once per provider mount. Changing fileName/language after mount
|
|
619
|
-
// would require recreating the Monaco model anyway, so treat it as static.
|
|
620
|
-
const { mode: editorMode, language: resolvedLanguage } = useMemo(
|
|
621
|
-
() => resolveFileKind(fileName, language),
|
|
622
|
-
[fileName, language],
|
|
623
|
-
);
|
|
624
|
-
// In code mode, WYSIWYG and Preview aren't rendered — force the starting
|
|
625
|
-
// view to 'raw' so we don't boot into an unmounted surface. Image mode
|
|
626
|
-
// has no text-editing surface at all; keep the same fallback so that any
|
|
627
|
-
// host that switches into image mode doesn't end up in a stale view id.
|
|
628
|
-
const [markdownSource, setMarkdownSourceRaw] = useState(initialMarkdown);
|
|
629
|
-
const [markdownDoc, setMarkdownDocState] = useState<MarkdownDocument | null>(null);
|
|
630
|
-
const [doc, setDoc] = useState<Doc | null>(null);
|
|
631
|
-
const [activeView, setActiveViewRaw] = useState<EditorView>(
|
|
632
|
-
editorMode === 'markdown' ? initialView : 'raw',
|
|
633
|
-
);
|
|
634
|
-
const [uncontrolledFindMode, setUncontrolledFindMode] = useState(false);
|
|
635
|
-
const findMode = editorMode !== 'image' && (controlledFindMode ?? uncontrolledFindMode);
|
|
636
|
-
const setFindMode = useCallback(
|
|
637
|
-
(active: boolean) => {
|
|
638
|
-
if (editorMode === 'image') return;
|
|
639
|
-
if (controlledFindMode === undefined) setUncontrolledFindMode(active);
|
|
640
|
-
onFindModeChange?.(active);
|
|
641
|
-
},
|
|
642
|
-
[controlledFindMode, editorMode, onFindModeChange],
|
|
643
|
-
);
|
|
644
|
-
const activeViewRef = useRef(activeView);
|
|
645
|
-
activeViewRef.current = activeView;
|
|
646
|
-
const tiptapEditorRef = useRef<TiptapEditor | null>(null);
|
|
647
|
-
const monacoEditorRef = useRef<MonacoEditor | null>(null);
|
|
648
|
-
const pendingEditorScrollRef = useRef<{ view: 'raw' | 'wysiwyg'; ratio: number } | null>(null);
|
|
649
|
-
const setActiveView = useCallback(
|
|
650
|
-
(view: EditorView) => {
|
|
651
|
-
// In code mode only the raw view is valid. In image mode no text view
|
|
652
|
-
// is valid at all — ignore any switch attempt.
|
|
653
|
-
if (editorMode === 'code' && view !== 'raw') return;
|
|
654
|
-
if (editorMode === 'image') return;
|
|
655
|
-
|
|
656
|
-
const currentView = activeViewRef.current;
|
|
657
|
-
if (view === currentView) return;
|
|
658
|
-
|
|
659
|
-
// Write and Source are separate editor instances and are unmounted as
|
|
660
|
-
// their tabs are hidden. Capture a proportional document position
|
|
661
|
-
// before the current instance disappears, then restore it after the
|
|
662
|
-
// destination has mounted and completed layout.
|
|
663
|
-
let ratio: number | null = null;
|
|
664
|
-
let targetView: 'raw' | 'wysiwyg' | null = null;
|
|
665
|
-
if (currentView === 'raw' && view === 'wysiwyg' && monacoEditorRef.current) {
|
|
666
|
-
ratio = readMonacoScrollRatio(monacoEditorRef.current);
|
|
667
|
-
targetView = 'wysiwyg';
|
|
668
|
-
} else if (currentView === 'wysiwyg' && view === 'raw' && tiptapEditorRef.current) {
|
|
669
|
-
ratio = readWysiwygScrollRatio(tiptapEditorRef.current);
|
|
670
|
-
targetView = 'raw';
|
|
671
|
-
}
|
|
672
|
-
pendingEditorScrollRef.current =
|
|
673
|
-
ratio === null || targetView === null ? null : { view: targetView, ratio };
|
|
674
|
-
setActiveViewRaw(view);
|
|
675
|
-
},
|
|
676
|
-
[editorMode],
|
|
677
|
-
);
|
|
678
|
-
const [parseError, setParseError] = useState<string | null>(null);
|
|
679
|
-
const [isParsing, setIsParsing] = useState(false);
|
|
680
|
-
const [colorScheme, setColorScheme] = useState<EditorColorScheme>(initialColorScheme);
|
|
681
|
-
const [inlinePreviewVisible, setInlinePreviewVisibleRaw] =
|
|
682
|
-
useState<boolean>(effectiveInlinePreview);
|
|
683
|
-
// Sync visibility when the host changes the prop (e.g., toggle from outside).
|
|
684
|
-
useEffect(() => {
|
|
685
|
-
if (viewPreferences?.inlinePreview === undefined) setInlinePreviewVisibleRaw(inlinePreview);
|
|
686
|
-
}, [inlinePreview, viewPreferences?.inlinePreview]);
|
|
687
|
-
const [statusBarVisible, setStatusBarVisibleRaw] = useState<boolean>(effectiveShowStatusBar);
|
|
688
|
-
useEffect(() => {
|
|
689
|
-
if (viewPreferences?.showStatusBar === undefined) setStatusBarVisibleRaw(showStatusBar);
|
|
690
|
-
}, [showStatusBar, viewPreferences?.showStatusBar]);
|
|
691
|
-
const [outlineVisible, setOutlineVisibleRaw] = useState<boolean>(effectiveOutline);
|
|
692
|
-
useEffect(() => {
|
|
693
|
-
if (viewPreferences?.outline === undefined) setOutlineVisibleRaw(outline);
|
|
694
|
-
}, [outline, viewPreferences?.outline]);
|
|
695
|
-
const [blockTagVisibilityState, setBlockTagVisibilityRaw] = useState<BlockTagVisibility>(
|
|
696
|
-
effectiveBlockTagVisibility,
|
|
697
|
-
);
|
|
698
|
-
const blockTagsVisible = blockTagVisibilityState !== 'none';
|
|
699
|
-
useEffect(() => {
|
|
700
|
-
if (
|
|
701
|
-
viewPreferences?.blockTagVisibility === undefined &&
|
|
702
|
-
viewPreferences?.blockTags === undefined
|
|
703
|
-
) {
|
|
704
|
-
setBlockTagVisibilityRaw(normalizeBlockTagVisibility(initialBlockTagVisibility ?? blockTags));
|
|
705
|
-
}
|
|
706
|
-
}, [blockTags, initialBlockTagVisibility, viewPreferences]);
|
|
707
|
-
const [themeInheritanceState, setThemeInheritanceRaw] =
|
|
708
|
-
useState<ThemeInheritance>(effectiveThemeInheritance);
|
|
709
|
-
useEffect(() => {
|
|
710
|
-
if (viewPreferences?.themeInheritance === undefined) {
|
|
711
|
-
setThemeInheritanceRaw(themeInheritance);
|
|
712
|
-
}
|
|
713
|
-
}, [themeInheritance, viewPreferences?.themeInheritance]);
|
|
714
|
-
const [layoutModeState, setLayoutModeRaw] = useState<LayoutMode>(effectiveLayoutMode);
|
|
715
|
-
useEffect(() => {
|
|
716
|
-
if (viewPreferences?.layoutMode === undefined) setLayoutModeRaw(layoutMode);
|
|
717
|
-
}, [layoutMode, viewPreferences?.layoutMode]);
|
|
718
|
-
const [imageEditTarget, setImageEditTarget] = useState<string | null>(null);
|
|
719
|
-
const [mediaRevision, setMediaRevision] = useState(0);
|
|
720
|
-
const openImageEdit = useCallback((relativePath: string) => {
|
|
721
|
-
setImageEditTarget(relativePath);
|
|
722
|
-
}, []);
|
|
723
|
-
const closeImageEdit = useCallback(() => {
|
|
724
|
-
setImageEditTarget(null);
|
|
725
|
-
}, []);
|
|
726
|
-
const bumpMediaRevision = useCallback(() => {
|
|
727
|
-
setMediaRevision((n) => n + 1);
|
|
728
|
-
}, []);
|
|
729
|
-
|
|
730
|
-
// Sync from the bundled `viewPreferences` prop. Runs in addition to the
|
|
731
|
-
// individual prop syncs above. When both APIs are present, the bundled
|
|
732
|
-
// values are applied here last, keeping `viewPreferences` authoritative.
|
|
733
|
-
useEffect(() => {
|
|
734
|
-
if (!viewPreferences) return;
|
|
735
|
-
if (viewPreferences.inlinePreview !== undefined) {
|
|
736
|
-
setInlinePreviewVisibleRaw(viewPreferences.inlinePreview);
|
|
737
|
-
}
|
|
738
|
-
if (viewPreferences.showStatusBar !== undefined) {
|
|
739
|
-
setStatusBarVisibleRaw(viewPreferences.showStatusBar);
|
|
740
|
-
}
|
|
741
|
-
if (viewPreferences.outline !== undefined) {
|
|
742
|
-
setOutlineVisibleRaw(viewPreferences.outline);
|
|
743
|
-
}
|
|
744
|
-
if (viewPreferences.blockTagVisibility !== undefined) {
|
|
745
|
-
setBlockTagVisibilityRaw(normalizeBlockTagVisibility(viewPreferences.blockTagVisibility));
|
|
746
|
-
} else if (viewPreferences.blockTags !== undefined) {
|
|
747
|
-
setBlockTagVisibilityRaw(normalizeBlockTagVisibility(viewPreferences.blockTags));
|
|
748
|
-
}
|
|
749
|
-
if (viewPreferences.themeInheritance !== undefined) {
|
|
750
|
-
setThemeInheritanceRaw(viewPreferences.themeInheritance);
|
|
751
|
-
}
|
|
752
|
-
if (viewPreferences.layoutMode !== undefined) {
|
|
753
|
-
setLayoutModeRaw(viewPreferences.layoutMode);
|
|
754
|
-
}
|
|
755
|
-
}, [viewPreferences]);
|
|
756
|
-
|
|
757
|
-
// Wrap the view setters so user-driven changes emit a snapshot via
|
|
758
|
-
// `onViewPreferencesChange`. Refs hold the latest values + callback so
|
|
759
|
-
// each wrapper can build a current snapshot without re-creating itself
|
|
760
|
-
// on every state change (the setters are kept referentially stable for
|
|
761
|
-
// the context value's memoization).
|
|
762
|
-
const onViewPreferencesChangeRef = useRef(onViewPreferencesChange);
|
|
763
|
-
onViewPreferencesChangeRef.current = onViewPreferencesChange;
|
|
764
|
-
const inlinePreviewRef = useRef(inlinePreviewVisible);
|
|
765
|
-
inlinePreviewRef.current = inlinePreviewVisible;
|
|
766
|
-
const statusBarRef = useRef(statusBarVisible);
|
|
767
|
-
statusBarRef.current = statusBarVisible;
|
|
768
|
-
const outlineRef = useRef(outlineVisible);
|
|
769
|
-
outlineRef.current = outlineVisible;
|
|
770
|
-
const blockTagVisibilityRef = useRef(blockTagVisibilityState);
|
|
771
|
-
blockTagVisibilityRef.current = blockTagVisibilityState;
|
|
772
|
-
const themeInheritanceRef = useRef(themeInheritanceState);
|
|
773
|
-
themeInheritanceRef.current = themeInheritanceState;
|
|
774
|
-
const layoutModeRef = useRef(layoutModeState);
|
|
775
|
-
layoutModeRef.current = layoutModeState;
|
|
776
|
-
const setInlinePreviewVisible = useCallback((visible: boolean) => {
|
|
777
|
-
setInlinePreviewVisibleRaw(visible);
|
|
778
|
-
onViewPreferencesChangeRef.current?.({
|
|
779
|
-
inlinePreview: visible,
|
|
780
|
-
showStatusBar: statusBarRef.current,
|
|
781
|
-
outline: outlineRef.current,
|
|
782
|
-
blockTags: blockTagVisibilityRef.current !== 'none',
|
|
783
|
-
blockTagVisibility: blockTagVisibilityRef.current,
|
|
784
|
-
themeInheritance: themeInheritanceRef.current,
|
|
785
|
-
layoutMode: layoutModeRef.current,
|
|
786
|
-
});
|
|
787
|
-
}, []);
|
|
788
|
-
const setStatusBarVisible = useCallback((visible: boolean) => {
|
|
789
|
-
setStatusBarVisibleRaw(visible);
|
|
790
|
-
onViewPreferencesChangeRef.current?.({
|
|
791
|
-
inlinePreview: inlinePreviewRef.current,
|
|
792
|
-
showStatusBar: visible,
|
|
793
|
-
outline: outlineRef.current,
|
|
794
|
-
blockTags: blockTagVisibilityRef.current !== 'none',
|
|
795
|
-
blockTagVisibility: blockTagVisibilityRef.current,
|
|
796
|
-
themeInheritance: themeInheritanceRef.current,
|
|
797
|
-
layoutMode: layoutModeRef.current,
|
|
798
|
-
});
|
|
799
|
-
}, []);
|
|
800
|
-
const setOutlineVisible = useCallback((visible: boolean) => {
|
|
801
|
-
setOutlineVisibleRaw(visible);
|
|
802
|
-
onViewPreferencesChangeRef.current?.({
|
|
803
|
-
inlinePreview: inlinePreviewRef.current,
|
|
804
|
-
showStatusBar: statusBarRef.current,
|
|
805
|
-
outline: visible,
|
|
806
|
-
blockTags: blockTagVisibilityRef.current !== 'none',
|
|
807
|
-
blockTagVisibility: blockTagVisibilityRef.current,
|
|
808
|
-
themeInheritance: themeInheritanceRef.current,
|
|
809
|
-
layoutMode: layoutModeRef.current,
|
|
810
|
-
});
|
|
811
|
-
}, []);
|
|
812
|
-
const setBlockTagVisibility = useCallback((visibility: BlockTagVisibility) => {
|
|
813
|
-
setBlockTagVisibilityRaw(visibility);
|
|
814
|
-
onViewPreferencesChangeRef.current?.({
|
|
815
|
-
inlinePreview: inlinePreviewRef.current,
|
|
816
|
-
showStatusBar: statusBarRef.current,
|
|
817
|
-
outline: outlineRef.current,
|
|
818
|
-
blockTags: visibility !== 'none',
|
|
819
|
-
blockTagVisibility: visibility,
|
|
820
|
-
themeInheritance: themeInheritanceRef.current,
|
|
821
|
-
layoutMode: layoutModeRef.current,
|
|
822
|
-
});
|
|
823
|
-
}, []);
|
|
824
|
-
const setBlockTagsVisible = useCallback(
|
|
825
|
-
(visible: boolean) => setBlockTagVisibility(visible ? 'always' : 'none'),
|
|
826
|
-
[setBlockTagVisibility],
|
|
827
|
-
);
|
|
828
|
-
const setThemeInheritance = useCallback((mode: ThemeInheritance) => {
|
|
829
|
-
setThemeInheritanceRaw(mode);
|
|
830
|
-
onViewPreferencesChangeRef.current?.({
|
|
831
|
-
inlinePreview: inlinePreviewRef.current,
|
|
832
|
-
showStatusBar: statusBarRef.current,
|
|
833
|
-
outline: outlineRef.current,
|
|
834
|
-
blockTags: blockTagVisibilityRef.current !== 'none',
|
|
835
|
-
blockTagVisibility: blockTagVisibilityRef.current,
|
|
836
|
-
themeInheritance: mode,
|
|
837
|
-
layoutMode: layoutModeRef.current,
|
|
838
|
-
});
|
|
839
|
-
}, []);
|
|
840
|
-
const setLayoutMode = useCallback((mode: LayoutMode) => {
|
|
841
|
-
setLayoutModeRaw(mode);
|
|
842
|
-
onViewPreferencesChangeRef.current?.({
|
|
843
|
-
inlinePreview: inlinePreviewRef.current,
|
|
844
|
-
showStatusBar: statusBarRef.current,
|
|
845
|
-
outline: outlineRef.current,
|
|
846
|
-
blockTags: blockTagVisibilityRef.current !== 'none',
|
|
847
|
-
blockTagVisibility: blockTagVisibilityRef.current,
|
|
848
|
-
themeInheritance: themeInheritanceRef.current,
|
|
849
|
-
layoutMode: mode,
|
|
850
|
-
});
|
|
851
|
-
}, []);
|
|
852
|
-
const [tiptapEditor, setTiptapEditorRaw] = useState<TiptapEditor | null>(null);
|
|
853
|
-
const [monacoEditor, setMonacoEditorRaw] = useState<MonacoEditor | null>(null);
|
|
854
|
-
const setTiptapEditor = useCallback((editor: TiptapEditor | null) => {
|
|
855
|
-
tiptapEditorRef.current = editor;
|
|
856
|
-
setTiptapEditorRaw(editor);
|
|
857
|
-
}, []);
|
|
858
|
-
const setMonacoEditor = useCallback((editor: MonacoEditor | null) => {
|
|
859
|
-
monacoEditorRef.current = editor;
|
|
860
|
-
setMonacoEditorRaw(editor);
|
|
861
|
-
}, []);
|
|
862
|
-
|
|
863
|
-
useEffect(() => {
|
|
864
|
-
const pending = pendingEditorScrollRef.current;
|
|
865
|
-
if (!pending || pending.view !== activeView) return;
|
|
866
|
-
|
|
867
|
-
const restore =
|
|
868
|
-
activeView === 'raw' && monacoEditor
|
|
869
|
-
? () => restoreMonacoScrollRatio(monacoEditor, pending.ratio)
|
|
870
|
-
: activeView === 'wysiwyg' && tiptapEditor
|
|
871
|
-
? () => restoreWysiwygScrollRatio(tiptapEditor, pending.ratio)
|
|
872
|
-
: null;
|
|
873
|
-
if (!restore) return;
|
|
874
|
-
|
|
875
|
-
let cancelled = false;
|
|
876
|
-
const apply = () => {
|
|
877
|
-
if (cancelled) return;
|
|
878
|
-
restore();
|
|
879
|
-
if (pendingEditorScrollRef.current === pending) pendingEditorScrollRef.current = null;
|
|
880
|
-
};
|
|
881
|
-
|
|
882
|
-
if (typeof requestAnimationFrame === 'function') {
|
|
883
|
-
const frame = requestAnimationFrame(apply);
|
|
884
|
-
return () => {
|
|
885
|
-
cancelled = true;
|
|
886
|
-
cancelAnimationFrame(frame);
|
|
887
|
-
};
|
|
888
|
-
}
|
|
889
|
-
|
|
890
|
-
const timeout = setTimeout(apply, 0);
|
|
891
|
-
return () => {
|
|
892
|
-
cancelled = true;
|
|
893
|
-
clearTimeout(timeout);
|
|
894
|
-
};
|
|
895
|
-
}, [activeView, monacoEditor, tiptapEditor]);
|
|
896
|
-
// Mirror the focused canvas textbox editor (published by the inline
|
|
897
|
-
// SceneTextOverlay through a module channel, since the canvas renders in
|
|
898
|
-
// a detached React root outside this provider) so the toolbar can target it.
|
|
899
|
-
const [activeSceneText, setActiveSceneText] = useState<SceneTextHandle | null>(null);
|
|
900
|
-
const sceneTextChannel = useMemo(createSceneTextChannel, []);
|
|
901
|
-
useEffect(() => sceneTextChannel.subscribe(setActiveSceneText), [sceneTextChannel]);
|
|
902
|
-
|
|
903
|
-
const articleIdRef = useRef(articleId);
|
|
904
|
-
articleIdRef.current = articleId;
|
|
905
|
-
|
|
906
|
-
// Sync color scheme when prop changes
|
|
907
|
-
useEffect(() => {
|
|
908
|
-
setColorScheme(initialColorScheme);
|
|
909
|
-
}, [initialColorScheme]);
|
|
910
|
-
|
|
911
|
-
// Debounced parse on markdown source change
|
|
912
|
-
const parseTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);
|
|
913
|
-
const skipInitialDebouncedParseRef = useRef(Boolean(initialMarkdown));
|
|
914
|
-
|
|
915
|
-
const doParse = useCallback((source: string) => {
|
|
916
|
-
setIsParsing(true);
|
|
917
|
-
try {
|
|
918
|
-
const parsed = parseMarkdown(source);
|
|
919
|
-
setMarkdownDocState(parsed);
|
|
920
|
-
setParseError(null);
|
|
921
|
-
|
|
922
|
-
// Generate Doc from parsed markdown
|
|
923
|
-
try {
|
|
924
|
-
const generatedDoc = markdownToDoc(parsed, {
|
|
925
|
-
articleId: articleIdRef.current,
|
|
926
|
-
});
|
|
927
|
-
setDoc(generatedDoc);
|
|
928
|
-
} catch (docErr: unknown) {
|
|
929
|
-
// Doc generation can fail but markdown parse succeeded
|
|
930
|
-
setDoc(null);
|
|
931
|
-
console.warn('Doc generation failed:', docErr instanceof Error ? docErr.message : docErr);
|
|
932
|
-
}
|
|
933
|
-
} catch (err: unknown) {
|
|
934
|
-
setParseError(err instanceof Error ? err.message : 'Parse error');
|
|
935
|
-
setMarkdownDocState(null);
|
|
936
|
-
setDoc(null);
|
|
937
|
-
} finally {
|
|
938
|
-
setIsParsing(false);
|
|
939
|
-
}
|
|
940
|
-
}, []);
|
|
941
|
-
|
|
942
|
-
// Parse on source changes with debounce. Skipped in code/image mode —
|
|
943
|
-
// the WYSIWYG/Preview surfaces that consume markdownDoc/doc aren't
|
|
944
|
-
// mounted, so there's nothing to feed and no reason to run the markdown
|
|
945
|
-
// parser on TypeScript / JSON / a binary image asset.
|
|
946
|
-
useEffect(() => {
|
|
947
|
-
if (editorMode !== 'markdown') return;
|
|
948
|
-
if (skipInitialDebouncedParseRef.current) {
|
|
949
|
-
skipInitialDebouncedParseRef.current = false;
|
|
950
|
-
return;
|
|
951
|
-
}
|
|
952
|
-
if (parseTimeoutRef.current) {
|
|
953
|
-
clearTimeout(parseTimeoutRef.current);
|
|
954
|
-
}
|
|
955
|
-
setIsParsing(true);
|
|
956
|
-
parseTimeoutRef.current = setTimeout(() => {
|
|
957
|
-
doParse(markdownSource);
|
|
958
|
-
}, 150);
|
|
959
|
-
return () => {
|
|
960
|
-
if (parseTimeoutRef.current) {
|
|
961
|
-
clearTimeout(parseTimeoutRef.current);
|
|
962
|
-
}
|
|
963
|
-
};
|
|
964
|
-
}, [markdownSource, doParse, editorMode]);
|
|
965
|
-
|
|
966
|
-
// Initial parse
|
|
967
|
-
useEffect(() => {
|
|
968
|
-
if (editorMode !== 'markdown') return;
|
|
969
|
-
if (initialMarkdown) {
|
|
970
|
-
doParse(initialMarkdown);
|
|
971
|
-
}
|
|
972
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
973
|
-
}, []);
|
|
974
|
-
|
|
975
|
-
const setMarkdownSource = useCallback((source: string) => {
|
|
976
|
-
setMarkdownSourceRaw(source);
|
|
977
|
-
}, []);
|
|
978
|
-
|
|
979
|
-
// Block-at-a-time navigation. In 'document' mode the channel passes
|
|
980
|
-
// through to the full source; in 'block' mode `editorSource` is the active
|
|
981
|
-
// block's slice and `setEditorSource` splices edits back in. Gated to
|
|
982
|
-
// markdown mode — code/image surfaces always edit the whole file.
|
|
983
|
-
const blockNav = useBlockNavigator(markdownSource, setMarkdownSource, {
|
|
984
|
-
enabled:
|
|
985
|
-
(layoutModeState === 'block' || layoutModeState === 'timeline') && editorMode === 'markdown',
|
|
986
|
-
});
|
|
987
|
-
const {
|
|
988
|
-
editorSource,
|
|
989
|
-
setEditorSource,
|
|
990
|
-
blockCount,
|
|
991
|
-
activeBlockKey,
|
|
992
|
-
activeBlockStartLine,
|
|
993
|
-
goToBlock,
|
|
994
|
-
goToBlockByLine,
|
|
995
|
-
prevBlock,
|
|
996
|
-
nextBlock,
|
|
997
|
-
addBlock,
|
|
998
|
-
} = blockNav;
|
|
999
|
-
|
|
1000
|
-
const insertAtCursor = useCallback(
|
|
1001
|
-
(text: string) => {
|
|
1002
|
-
if (activeView === 'wysiwyg' && tiptapEditor) {
|
|
1003
|
-
// Insert as HTML so formatting is preserved
|
|
1004
|
-
const html = markdownToTiptap(text);
|
|
1005
|
-
tiptapEditor.chain().focus().insertContent(html).run();
|
|
1006
|
-
} else if (activeView === 'raw' && monacoEditor) {
|
|
1007
|
-
const position = monacoEditor.getPosition();
|
|
1008
|
-
if (position) {
|
|
1009
|
-
const model = monacoEditor.getModel();
|
|
1010
|
-
if (model) {
|
|
1011
|
-
const range = {
|
|
1012
|
-
startLineNumber: position.lineNumber,
|
|
1013
|
-
startColumn: position.column,
|
|
1014
|
-
endLineNumber: position.lineNumber,
|
|
1015
|
-
endColumn: position.column,
|
|
1016
|
-
};
|
|
1017
|
-
monacoEditor.executeEdits('drop', [{ range, text }]);
|
|
1018
|
-
}
|
|
1019
|
-
} else {
|
|
1020
|
-
// No cursor — append
|
|
1021
|
-
setMarkdownSourceRaw((prev) => prev + '\n\n' + text);
|
|
1022
|
-
}
|
|
1023
|
-
} else {
|
|
1024
|
-
// Preview or no editor — append to end
|
|
1025
|
-
setMarkdownSourceRaw((prev) => prev + '\n\n' + text);
|
|
1026
|
-
}
|
|
1027
|
-
},
|
|
1028
|
-
[activeView, tiptapEditor, monacoEditor],
|
|
1029
|
-
);
|
|
1030
|
-
|
|
1031
|
-
const replaceAll = useCallback(
|
|
1032
|
-
(text: string) => {
|
|
1033
|
-
setMarkdownSourceRaw(text);
|
|
1034
|
-
|
|
1035
|
-
// Push to editors if mounted
|
|
1036
|
-
if (tiptapEditor) {
|
|
1037
|
-
const html = markdownToTiptap(text);
|
|
1038
|
-
tiptapEditor.commands.setContent(html);
|
|
1039
|
-
}
|
|
1040
|
-
if (monacoEditor) {
|
|
1041
|
-
monacoEditor.setValue(text);
|
|
1042
|
-
}
|
|
1043
|
-
},
|
|
1044
|
-
[tiptapEditor, monacoEditor],
|
|
1045
|
-
);
|
|
1046
|
-
|
|
1047
|
-
// ── Versioning ─────────────────────────────────────────
|
|
1048
|
-
// Build a manager only when versioning is opted in *and* a workspace
|
|
1049
|
-
// container exists. A versioning request without one is a misconfiguration
|
|
1050
|
-
// — warn once so it surfaces in dev without breaking the editor.
|
|
1051
|
-
const versioningWarnedRef = useRef(false);
|
|
1052
|
-
useEffect(() => {
|
|
1053
|
-
if (allowVersioning && !workspaceContainer && !versioningWarnedRef.current) {
|
|
1054
|
-
console.warn(
|
|
1055
|
-
'[squisq-editor] allowVersioning requires a `workspaceContainer` prop; versioning is disabled.',
|
|
1056
|
-
);
|
|
1057
|
-
versioningWarnedRef.current = true;
|
|
1058
|
-
}
|
|
1059
|
-
}, [allowVersioning, workspaceContainer]);
|
|
1060
|
-
|
|
1061
|
-
const versioning = useMemo<DocumentVersionManager | null>(() => {
|
|
1062
|
-
if (!allowVersioning || !workspaceContainer) return null;
|
|
1063
|
-
return new DocumentVersionManager(workspaceContainer, { basename: versionBasename });
|
|
1064
|
-
}, [allowVersioning, workspaceContainer, versionBasename]);
|
|
1065
|
-
|
|
1066
|
-
const onSaveVersionRef = useRef(onSaveVersion);
|
|
1067
|
-
onSaveVersionRef.current = onSaveVersion;
|
|
1068
|
-
const prunePolicyRef = useRef(versioningPrunePolicy);
|
|
1069
|
-
prunePolicyRef.current = versioningPrunePolicy;
|
|
1070
|
-
|
|
1071
|
-
const saveVersion = useCallback(
|
|
1072
|
-
async (options?: SaveVersionOptions): Promise<SaveVersionResult> => {
|
|
1073
|
-
if (!versioning) {
|
|
1074
|
-
const skipped: SaveVersionResult = { saved: false, version: null, reason: 'no-document' };
|
|
1075
|
-
onSaveVersionRef.current?.(skipped);
|
|
1076
|
-
return skipped;
|
|
1077
|
-
}
|
|
1078
|
-
const result = await versioning.saveVersion(options);
|
|
1079
|
-
onSaveVersionRef.current?.(result);
|
|
1080
|
-
if (result.saved) {
|
|
1081
|
-
// Fire-and-forget prune. Failures here shouldn't block the save.
|
|
1082
|
-
versioning.pruneVersions(prunePolicyRef.current).catch((err: unknown) => {
|
|
1083
|
-
console.warn(
|
|
1084
|
-
'[squisq-editor] pruneVersions failed:',
|
|
1085
|
-
err instanceof Error ? err.message : err,
|
|
1086
|
-
);
|
|
1087
|
-
});
|
|
1088
|
-
}
|
|
1089
|
-
return result;
|
|
1090
|
-
},
|
|
1091
|
-
[versioning],
|
|
1092
|
-
);
|
|
1093
|
-
|
|
1094
|
-
// Auto-save: stamp a new snapshot after `versioningAutoSaveIdleMs` of
|
|
1095
|
-
// idle. The "only save if different" check inside `saveVersion` makes
|
|
1096
|
-
// most ticks no-ops, so this is cheap. Disabled when the idle delay is
|
|
1097
|
-
// 0 or versioning isn't active.
|
|
1098
|
-
//
|
|
1099
|
-
// We pass the live `markdownSource` explicitly so saveVersion never has
|
|
1100
|
-
// to fall back to `container.readDocument()`. That fallback would fail
|
|
1101
|
-
// in setups where the markdown file lives outside the versioning
|
|
1102
|
-
// container's scope (e.g. DocBlocks, where the container points at
|
|
1103
|
-
// `<basename>_files/` while the doc itself lives in the parent
|
|
1104
|
-
// directory). Using the editor's live state also ensures the snapshot
|
|
1105
|
-
// captures the most recent edit even if the host's autosave to the
|
|
1106
|
-
// container hasn't flushed yet.
|
|
1107
|
-
useEffect(() => {
|
|
1108
|
-
if (!versioning) return;
|
|
1109
|
-
if (versioningAutoSaveIdleMs <= 0) return;
|
|
1110
|
-
const timer = setTimeout(() => {
|
|
1111
|
-
saveVersion({ content: markdownSource }).catch((err: unknown) => {
|
|
1112
|
-
console.warn(
|
|
1113
|
-
'[squisq-editor] auto-save version failed:',
|
|
1114
|
-
err instanceof Error ? err.message : err,
|
|
1115
|
-
);
|
|
1116
|
-
});
|
|
1117
|
-
}, versioningAutoSaveIdleMs);
|
|
1118
|
-
return () => clearTimeout(timer);
|
|
1119
|
-
}, [markdownSource, versioning, versioningAutoSaveIdleMs, saveVersion]);
|
|
1120
|
-
|
|
1121
|
-
const setMarkdownDoc = useCallback((newDoc: MarkdownDocument) => {
|
|
1122
|
-
setMarkdownDocState(newDoc);
|
|
1123
|
-
// Stringify to update the raw source
|
|
1124
|
-
try {
|
|
1125
|
-
const newSource = stringifyMarkdown(newDoc);
|
|
1126
|
-
setMarkdownSourceRaw(newSource);
|
|
1127
|
-
setParseError(null);
|
|
1128
|
-
|
|
1129
|
-
// Generate Doc
|
|
1130
|
-
try {
|
|
1131
|
-
const generatedDoc = markdownToDoc(newDoc, {
|
|
1132
|
-
articleId: articleIdRef.current,
|
|
1133
|
-
});
|
|
1134
|
-
setDoc(generatedDoc);
|
|
1135
|
-
} catch (docErr: unknown) {
|
|
1136
|
-
setDoc(null);
|
|
1137
|
-
console.warn('Doc generation failed:', docErr instanceof Error ? docErr.message : docErr);
|
|
1138
|
-
}
|
|
1139
|
-
} catch (err: unknown) {
|
|
1140
|
-
setParseError(err instanceof Error ? err.message : 'Stringify error');
|
|
1141
|
-
}
|
|
1142
|
-
}, []);
|
|
1143
|
-
|
|
1144
|
-
const value = useMemo<EditorContextValue>(
|
|
1145
|
-
() => ({
|
|
1146
|
-
markdownSource,
|
|
1147
|
-
markdownDoc,
|
|
1148
|
-
doc,
|
|
1149
|
-
activeView,
|
|
1150
|
-
parseError,
|
|
1151
|
-
isParsing,
|
|
1152
|
-
colorScheme,
|
|
1153
|
-
editorMode,
|
|
1154
|
-
findMode,
|
|
1155
|
-
language: resolvedLanguage,
|
|
1156
|
-
inlinePreviewVisible,
|
|
1157
|
-
statusBarVisible,
|
|
1158
|
-
outlineVisible,
|
|
1159
|
-
blockTagVisibility: blockTagVisibilityState,
|
|
1160
|
-
blockTagsVisible,
|
|
1161
|
-
themeInheritance: themeInheritanceState,
|
|
1162
|
-
layoutMode: layoutModeState,
|
|
1163
|
-
editorSource,
|
|
1164
|
-
blockCount,
|
|
1165
|
-
activeBlockKey,
|
|
1166
|
-
activeBlockStartLine,
|
|
1167
|
-
imageEditTarget,
|
|
1168
|
-
mediaRevision,
|
|
1169
|
-
allowRecording,
|
|
1170
|
-
allowNarrate,
|
|
1171
|
-
tiptapEditor,
|
|
1172
|
-
monacoEditor,
|
|
1173
|
-
activeSceneText,
|
|
1174
|
-
sceneTextChannel,
|
|
1175
|
-
workspaceContainer,
|
|
1176
|
-
versioning,
|
|
1177
|
-
saveVersion,
|
|
1178
|
-
mediaProvider,
|
|
1179
|
-
imageDisplayMode,
|
|
1180
|
-
mentionProvider,
|
|
1181
|
-
documentLinkProvider,
|
|
1182
|
-
setMarkdownSource,
|
|
1183
|
-
setEditorSource,
|
|
1184
|
-
setLayoutMode,
|
|
1185
|
-
goToBlock,
|
|
1186
|
-
goToBlockByLine,
|
|
1187
|
-
prevBlock,
|
|
1188
|
-
nextBlock,
|
|
1189
|
-
addBlock,
|
|
1190
|
-
setMarkdownDoc,
|
|
1191
|
-
setActiveView,
|
|
1192
|
-
setFindMode,
|
|
1193
|
-
setTiptapEditor,
|
|
1194
|
-
setMonacoEditor,
|
|
1195
|
-
setColorScheme,
|
|
1196
|
-
setInlinePreviewVisible,
|
|
1197
|
-
setStatusBarVisible,
|
|
1198
|
-
setOutlineVisible,
|
|
1199
|
-
setBlockTagVisibility,
|
|
1200
|
-
setBlockTagsVisible,
|
|
1201
|
-
setThemeInheritance,
|
|
1202
|
-
insertAtCursor,
|
|
1203
|
-
replaceAll,
|
|
1204
|
-
openImageEdit,
|
|
1205
|
-
closeImageEdit,
|
|
1206
|
-
bumpMediaRevision,
|
|
1207
|
-
}),
|
|
1208
|
-
[
|
|
1209
|
-
markdownSource,
|
|
1210
|
-
markdownDoc,
|
|
1211
|
-
doc,
|
|
1212
|
-
activeView,
|
|
1213
|
-
parseError,
|
|
1214
|
-
isParsing,
|
|
1215
|
-
colorScheme,
|
|
1216
|
-
editorMode,
|
|
1217
|
-
findMode,
|
|
1218
|
-
resolvedLanguage,
|
|
1219
|
-
inlinePreviewVisible,
|
|
1220
|
-
statusBarVisible,
|
|
1221
|
-
outlineVisible,
|
|
1222
|
-
blockTagVisibilityState,
|
|
1223
|
-
blockTagsVisible,
|
|
1224
|
-
themeInheritanceState,
|
|
1225
|
-
layoutModeState,
|
|
1226
|
-
editorSource,
|
|
1227
|
-
blockCount,
|
|
1228
|
-
activeBlockKey,
|
|
1229
|
-
activeBlockStartLine,
|
|
1230
|
-
tiptapEditor,
|
|
1231
|
-
monacoEditor,
|
|
1232
|
-
activeSceneText,
|
|
1233
|
-
sceneTextChannel,
|
|
1234
|
-
workspaceContainer,
|
|
1235
|
-
versioning,
|
|
1236
|
-
saveVersion,
|
|
1237
|
-
mediaProvider,
|
|
1238
|
-
imageDisplayMode,
|
|
1239
|
-
mentionProvider,
|
|
1240
|
-
documentLinkProvider,
|
|
1241
|
-
setMarkdownSource,
|
|
1242
|
-
setEditorSource,
|
|
1243
|
-
setLayoutMode,
|
|
1244
|
-
goToBlock,
|
|
1245
|
-
goToBlockByLine,
|
|
1246
|
-
prevBlock,
|
|
1247
|
-
nextBlock,
|
|
1248
|
-
addBlock,
|
|
1249
|
-
setMarkdownDoc,
|
|
1250
|
-
setActiveView,
|
|
1251
|
-
setFindMode,
|
|
1252
|
-
setTiptapEditor,
|
|
1253
|
-
setMonacoEditor,
|
|
1254
|
-
setColorScheme,
|
|
1255
|
-
setInlinePreviewVisible,
|
|
1256
|
-
setStatusBarVisible,
|
|
1257
|
-
setOutlineVisible,
|
|
1258
|
-
setBlockTagVisibility,
|
|
1259
|
-
setBlockTagsVisible,
|
|
1260
|
-
setThemeInheritance,
|
|
1261
|
-
insertAtCursor,
|
|
1262
|
-
replaceAll,
|
|
1263
|
-
imageEditTarget,
|
|
1264
|
-
mediaRevision,
|
|
1265
|
-
allowRecording,
|
|
1266
|
-
allowNarrate,
|
|
1267
|
-
openImageEdit,
|
|
1268
|
-
closeImageEdit,
|
|
1269
|
-
bumpMediaRevision,
|
|
1270
|
-
],
|
|
1271
|
-
);
|
|
1272
|
-
|
|
1273
|
-
return <EditorContext.Provider value={value}>{children}</EditorContext.Provider>;
|
|
1274
|
-
}
|