@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/fileKind.ts
DELETED
|
@@ -1,158 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* fileKind
|
|
3
|
-
*
|
|
4
|
-
* Maps a file name (or bare extension) to a Monaco language ID and decides
|
|
5
|
-
* whether the editor shell should operate in markdown mode (full WYSIWYG +
|
|
6
|
-
* Preview experience) or code mode (Monaco-only view with formatting
|
|
7
|
-
* buttons hidden).
|
|
8
|
-
*
|
|
9
|
-
* The mapping favors common web / systems languages that Monaco ships with
|
|
10
|
-
* out of the box. Unknown extensions fall back to markdown mode so the
|
|
11
|
-
* existing UX remains the default for anything we don't recognize.
|
|
12
|
-
*/
|
|
13
|
-
|
|
14
|
-
export interface FileKind {
|
|
15
|
-
/**
|
|
16
|
-
* 'markdown' keeps the full editor (WYSIWYG + Preview tabs); 'code' is
|
|
17
|
-
* Monaco-only; 'image' renders a dedicated image viewer with no text
|
|
18
|
-
* editing surface.
|
|
19
|
-
*/
|
|
20
|
-
mode: 'markdown' | 'code' | 'image';
|
|
21
|
-
/** Monaco language ID — passed to `<Editor defaultLanguage={...} />`. */
|
|
22
|
-
language: string;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
/**
|
|
26
|
-
* Extension → Monaco language ID. Keys are lowercase, no leading dot.
|
|
27
|
-
* Extend as needed; unknown extensions fall back to markdown mode.
|
|
28
|
-
*/
|
|
29
|
-
const EXT_TO_LANGUAGE: Record<string, string> = {
|
|
30
|
-
md: 'markdown',
|
|
31
|
-
markdown: 'markdown',
|
|
32
|
-
mdown: 'markdown',
|
|
33
|
-
txt: 'plaintext',
|
|
34
|
-
ts: 'typescript',
|
|
35
|
-
tsx: 'typescript',
|
|
36
|
-
js: 'javascript',
|
|
37
|
-
jsx: 'javascript',
|
|
38
|
-
mjs: 'javascript',
|
|
39
|
-
cjs: 'javascript',
|
|
40
|
-
json: 'json',
|
|
41
|
-
jsonc: 'json',
|
|
42
|
-
html: 'html',
|
|
43
|
-
htm: 'html',
|
|
44
|
-
css: 'css',
|
|
45
|
-
scss: 'scss',
|
|
46
|
-
less: 'less',
|
|
47
|
-
py: 'python',
|
|
48
|
-
go: 'go',
|
|
49
|
-
rs: 'rust',
|
|
50
|
-
rb: 'ruby',
|
|
51
|
-
java: 'java',
|
|
52
|
-
c: 'c',
|
|
53
|
-
h: 'c',
|
|
54
|
-
cpp: 'cpp',
|
|
55
|
-
hpp: 'cpp',
|
|
56
|
-
cc: 'cpp',
|
|
57
|
-
cs: 'csharp',
|
|
58
|
-
php: 'php',
|
|
59
|
-
sh: 'shell',
|
|
60
|
-
bash: 'shell',
|
|
61
|
-
zsh: 'shell',
|
|
62
|
-
yml: 'yaml',
|
|
63
|
-
yaml: 'yaml',
|
|
64
|
-
toml: 'ini',
|
|
65
|
-
ini: 'ini',
|
|
66
|
-
xml: 'xml',
|
|
67
|
-
svg: 'xml',
|
|
68
|
-
sql: 'sql',
|
|
69
|
-
lua: 'lua',
|
|
70
|
-
swift: 'swift',
|
|
71
|
-
kt: 'kotlin',
|
|
72
|
-
kts: 'kotlin',
|
|
73
|
-
dockerfile: 'dockerfile',
|
|
74
|
-
png: 'image',
|
|
75
|
-
jpg: 'image',
|
|
76
|
-
jpeg: 'image',
|
|
77
|
-
gif: 'image',
|
|
78
|
-
webp: 'image',
|
|
79
|
-
bmp: 'image',
|
|
80
|
-
ico: 'image',
|
|
81
|
-
avif: 'image',
|
|
82
|
-
};
|
|
83
|
-
|
|
84
|
-
/**
|
|
85
|
-
* Languages that keep the full markdown shell (WYSIWYG + Preview). Anything
|
|
86
|
-
* outside this set is treated as code, except for languages in
|
|
87
|
-
* `IMAGE_MODE_LANGUAGES`.
|
|
88
|
-
*/
|
|
89
|
-
const MARKDOWN_MODE_LANGUAGES = new Set(['markdown', 'plaintext']);
|
|
90
|
-
|
|
91
|
-
/**
|
|
92
|
-
* Languages that switch the shell into image-viewer mode — no text editing
|
|
93
|
-
* surface, no markdown chrome.
|
|
94
|
-
*/
|
|
95
|
-
const IMAGE_MODE_LANGUAGES = new Set(['image']);
|
|
96
|
-
|
|
97
|
-
/**
|
|
98
|
-
* Pull the lowercase extension (no leading dot) from a file name or bare
|
|
99
|
-
* extension string. Returns null when none is discernible.
|
|
100
|
-
*
|
|
101
|
-
* Examples:
|
|
102
|
-
* "foo.ts" → "ts"
|
|
103
|
-
* "foo.tar.gz" → "gz"
|
|
104
|
-
* ".ts" → "ts"
|
|
105
|
-
* "ts" → "ts"
|
|
106
|
-
* "Dockerfile" → "dockerfile" (full name match for extensionless files)
|
|
107
|
-
* "" → null
|
|
108
|
-
*/
|
|
109
|
-
function extractExtension(fileName: string): string | null {
|
|
110
|
-
const trimmed = fileName.trim();
|
|
111
|
-
if (!trimmed) return null;
|
|
112
|
-
|
|
113
|
-
// Strip any leading path — take only the basename.
|
|
114
|
-
const base = trimmed.replace(/^.*[/\\]/, '');
|
|
115
|
-
if (!base) return null;
|
|
116
|
-
|
|
117
|
-
const dotIdx = base.lastIndexOf('.');
|
|
118
|
-
if (dotIdx === -1) {
|
|
119
|
-
// No dot — could still be a recognized bare name (Dockerfile) or a bare
|
|
120
|
-
// extension passed by a caller like "ts". Lower-case and return.
|
|
121
|
-
return base.toLowerCase();
|
|
122
|
-
}
|
|
123
|
-
if (dotIdx === base.length - 1) return null; // Trailing dot, no extension.
|
|
124
|
-
return base.slice(dotIdx + 1).toLowerCase();
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
/**
|
|
128
|
-
* Detect a Monaco language ID from a file name. Returns null when the
|
|
129
|
-
* extension (or bare name) is not in the mapping.
|
|
130
|
-
*/
|
|
131
|
-
export function detectLanguageFromFileName(fileName: string): string | null {
|
|
132
|
-
const ext = extractExtension(fileName);
|
|
133
|
-
if (!ext) return null;
|
|
134
|
-
return EXT_TO_LANGUAGE[ext] ?? null;
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
/**
|
|
138
|
-
* Resolve the editor mode + Monaco language for a given file. The explicit
|
|
139
|
-
* `language` argument, if provided, wins over any detection from
|
|
140
|
-
* `fileName`. When nothing matches, falls back to markdown mode.
|
|
141
|
-
*/
|
|
142
|
-
export function resolveFileKind(fileName?: string, language?: string): FileKind {
|
|
143
|
-
const resolvedLanguage = language ?? (fileName ? detectLanguageFromFileName(fileName) : null);
|
|
144
|
-
|
|
145
|
-
if (!resolvedLanguage) {
|
|
146
|
-
return { mode: 'markdown', language: 'markdown' };
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
let mode: FileKind['mode'];
|
|
150
|
-
if (IMAGE_MODE_LANGUAGES.has(resolvedLanguage)) {
|
|
151
|
-
mode = 'image';
|
|
152
|
-
} else if (MARKDOWN_MODE_LANGUAGES.has(resolvedLanguage)) {
|
|
153
|
-
mode = 'markdown';
|
|
154
|
-
} else {
|
|
155
|
-
mode = 'code';
|
|
156
|
-
}
|
|
157
|
-
return { mode, language: resolvedLanguage };
|
|
158
|
-
}
|
|
@@ -1,81 +0,0 @@
|
|
|
1
|
-
import { Extension, type Editor } from '@tiptap/core';
|
|
2
|
-
import { Plugin, PluginKey } from '@tiptap/pm/state';
|
|
3
|
-
import { Decoration, DecorationSet } from '@tiptap/pm/view';
|
|
4
|
-
import { findProseMirrorMatches, normalizeFindIndex } from './findModel';
|
|
5
|
-
|
|
6
|
-
interface FindHighlightState {
|
|
7
|
-
query: string;
|
|
8
|
-
selectedIndex: number;
|
|
9
|
-
decorations: DecorationSet;
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
interface FindHighlightMeta {
|
|
13
|
-
query: string;
|
|
14
|
-
selectedIndex: number;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
const FIND_HIGHLIGHT_KEY = new PluginKey<FindHighlightState>('squisq-find-highlight');
|
|
18
|
-
|
|
19
|
-
function buildState(
|
|
20
|
-
doc: Parameters<typeof findProseMirrorMatches>[0],
|
|
21
|
-
query: string,
|
|
22
|
-
selectedIndex: number,
|
|
23
|
-
): FindHighlightState {
|
|
24
|
-
const matches = findProseMirrorMatches(doc, query);
|
|
25
|
-
const selected = normalizeFindIndex(selectedIndex, matches.length);
|
|
26
|
-
const decorations = matches.map((match, index) =>
|
|
27
|
-
Decoration.inline(match.from, match.to, {
|
|
28
|
-
class:
|
|
29
|
-
index === selected ? 'squisq-find-match squisq-find-match--selected' : 'squisq-find-match',
|
|
30
|
-
}),
|
|
31
|
-
);
|
|
32
|
-
return {
|
|
33
|
-
query,
|
|
34
|
-
selectedIndex: selected,
|
|
35
|
-
decorations: DecorationSet.create(doc, decorations),
|
|
36
|
-
};
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
/** ProseMirror decorations used by the shell's host-triggered Find mode. */
|
|
40
|
-
export const FindHighlightExtension = Extension.create({
|
|
41
|
-
name: 'squisqFindHighlight',
|
|
42
|
-
|
|
43
|
-
addProseMirrorPlugins() {
|
|
44
|
-
return [
|
|
45
|
-
new Plugin<FindHighlightState>({
|
|
46
|
-
key: FIND_HIGHLIGHT_KEY,
|
|
47
|
-
state: {
|
|
48
|
-
init: (_, state) => buildState(state.doc, '', 0),
|
|
49
|
-
apply: (transaction, previous, _oldState, newState) => {
|
|
50
|
-
const meta = transaction.getMeta(FIND_HIGHLIGHT_KEY) as FindHighlightMeta | undefined;
|
|
51
|
-
if (!meta && !transaction.docChanged) return previous;
|
|
52
|
-
return buildState(
|
|
53
|
-
newState.doc,
|
|
54
|
-
meta?.query ?? previous.query,
|
|
55
|
-
meta?.selectedIndex ?? previous.selectedIndex,
|
|
56
|
-
);
|
|
57
|
-
},
|
|
58
|
-
},
|
|
59
|
-
props: {
|
|
60
|
-
decorations: (state) => FIND_HIGHLIGHT_KEY.getState(state)?.decorations ?? null,
|
|
61
|
-
},
|
|
62
|
-
}),
|
|
63
|
-
];
|
|
64
|
-
},
|
|
65
|
-
});
|
|
66
|
-
|
|
67
|
-
/** Update WYSIWYG highlights and return the current number of matches. */
|
|
68
|
-
export function updateTiptapFindHighlights(
|
|
69
|
-
editor: Editor,
|
|
70
|
-
query: string,
|
|
71
|
-
selectedIndex: number,
|
|
72
|
-
): number {
|
|
73
|
-
const matches = findProseMirrorMatches(editor.state.doc, query);
|
|
74
|
-
editor.view.dispatch(
|
|
75
|
-
editor.state.tr.setMeta(FIND_HIGHLIGHT_KEY, {
|
|
76
|
-
query,
|
|
77
|
-
selectedIndex: normalizeFindIndex(selectedIndex, matches.length),
|
|
78
|
-
} satisfies FindHighlightMeta),
|
|
79
|
-
);
|
|
80
|
-
return matches.length;
|
|
81
|
-
}
|
package/src/find/FindToolbar.tsx
DELETED
|
@@ -1,314 +0,0 @@
|
|
|
1
|
-
import { useCallback, useEffect, useId, useRef, useState } from 'react';
|
|
2
|
-
import type { editor as MonacoEditorNs } from 'monaco-editor';
|
|
3
|
-
import { useEditorContext } from '../EditorContext';
|
|
4
|
-
import { Icon } from '../Icon';
|
|
5
|
-
import { updateTiptapFindHighlights } from './FindHighlightExtension';
|
|
6
|
-
import { findTextMatches, normalizeFindIndex } from './findModel';
|
|
7
|
-
|
|
8
|
-
interface HighlightRegistryLike {
|
|
9
|
-
set(name: string, highlight: unknown): void;
|
|
10
|
-
delete(name: string): boolean;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
interface HighlightConstructorLike {
|
|
14
|
-
new (...ranges: Range[]): unknown;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
interface PreviewHighlightResult {
|
|
18
|
-
count: number;
|
|
19
|
-
cleanup: () => void;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
export interface FindToolbarProps {
|
|
23
|
-
onClose: () => void;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
/** Search controls shown immediately to the right of the Write/Source/Use tabs. */
|
|
27
|
-
export function FindToolbar({ onClose }: FindToolbarProps) {
|
|
28
|
-
const { activeView, markdownSource, tiptapEditor, monacoEditor } = useEditorContext();
|
|
29
|
-
const [query, setQuery] = useState('');
|
|
30
|
-
const [selectedIndex, setSelectedIndex] = useState(0);
|
|
31
|
-
const [matchCount, setMatchCount] = useState(0);
|
|
32
|
-
const inputRef = useRef<HTMLInputElement>(null);
|
|
33
|
-
const reactId = useId().replace(/[^a-zA-Z0-9_-]/g, '');
|
|
34
|
-
const previewHighlightName = `squisq-find-${reactId}`;
|
|
35
|
-
const previewSelectedName = `${previewHighlightName}-selected`;
|
|
36
|
-
|
|
37
|
-
useEffect(() => {
|
|
38
|
-
inputRef.current?.focus();
|
|
39
|
-
}, []);
|
|
40
|
-
|
|
41
|
-
useEffect(() => {
|
|
42
|
-
const style = document.createElement('style');
|
|
43
|
-
style.dataset.squisqFindHighlight = reactId;
|
|
44
|
-
style.textContent = `::highlight(${previewHighlightName}) { background: #fde68a; color: inherit; } ::highlight(${previewSelectedName}) { background: #f59e0b; color: #111827; }`;
|
|
45
|
-
document.head.append(style);
|
|
46
|
-
return () => style.remove();
|
|
47
|
-
}, [previewHighlightName, previewSelectedName, reactId]);
|
|
48
|
-
|
|
49
|
-
useEffect(() => {
|
|
50
|
-
let cleanup = () => {};
|
|
51
|
-
let count = 0;
|
|
52
|
-
|
|
53
|
-
if (activeView === 'raw' && monacoEditor) {
|
|
54
|
-
const model = monacoEditor.getModel();
|
|
55
|
-
const matches = model
|
|
56
|
-
? model.findMatches(query.trim(), false, false, false, null, false)
|
|
57
|
-
: [];
|
|
58
|
-
count = query.trim() ? matches.length : 0;
|
|
59
|
-
const selected = normalizeFindIndex(selectedIndex, count);
|
|
60
|
-
const decorations = monacoEditor.createDecorationsCollection(
|
|
61
|
-
matches.slice(0, count).map((match, index) => ({
|
|
62
|
-
range: match.range,
|
|
63
|
-
options: monacoDecorationOptions(index === selected),
|
|
64
|
-
})),
|
|
65
|
-
);
|
|
66
|
-
if (count > 0) monacoEditor.revealRangeInCenter(matches[selected].range);
|
|
67
|
-
cleanup = () => decorations.clear();
|
|
68
|
-
} else if (activeView === 'wysiwyg' && tiptapEditor && !tiptapEditor.isDestroyed) {
|
|
69
|
-
count = updateTiptapFindHighlights(tiptapEditor, query, selectedIndex);
|
|
70
|
-
const selected = tiptapEditor.view.dom.querySelector<HTMLElement>(
|
|
71
|
-
'.squisq-find-match--selected',
|
|
72
|
-
);
|
|
73
|
-
selected?.scrollIntoView?.({ block: 'center', inline: 'nearest' });
|
|
74
|
-
cleanup = () => {
|
|
75
|
-
if (!tiptapEditor.isDestroyed) updateTiptapFindHighlights(tiptapEditor, '', 0);
|
|
76
|
-
};
|
|
77
|
-
} else if (activeView === 'preview') {
|
|
78
|
-
const root = inputRef.current
|
|
79
|
-
?.closest('.squisq-editor-shell')
|
|
80
|
-
?.querySelector<HTMLElement>('[data-testid="preview-panel"]');
|
|
81
|
-
if (root) {
|
|
82
|
-
const result = applyPreviewHighlights(
|
|
83
|
-
root,
|
|
84
|
-
query,
|
|
85
|
-
selectedIndex,
|
|
86
|
-
previewHighlightName,
|
|
87
|
-
previewSelectedName,
|
|
88
|
-
);
|
|
89
|
-
count = result.count;
|
|
90
|
-
cleanup = result.cleanup;
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
setMatchCount(count);
|
|
95
|
-
setSelectedIndex((current) => normalizeFindIndex(current, count));
|
|
96
|
-
return cleanup;
|
|
97
|
-
}, [
|
|
98
|
-
activeView,
|
|
99
|
-
markdownSource,
|
|
100
|
-
monacoEditor,
|
|
101
|
-
previewHighlightName,
|
|
102
|
-
previewSelectedName,
|
|
103
|
-
query,
|
|
104
|
-
selectedIndex,
|
|
105
|
-
tiptapEditor,
|
|
106
|
-
]);
|
|
107
|
-
|
|
108
|
-
const moveSelection = useCallback(
|
|
109
|
-
(delta: number) => {
|
|
110
|
-
if (matchCount === 0) return;
|
|
111
|
-
setSelectedIndex((current) => normalizeFindIndex(current + delta, matchCount));
|
|
112
|
-
},
|
|
113
|
-
[matchCount],
|
|
114
|
-
);
|
|
115
|
-
|
|
116
|
-
const resultLabel =
|
|
117
|
-
query.trim() === ''
|
|
118
|
-
? '0 of 0'
|
|
119
|
-
: matchCount === 0
|
|
120
|
-
? 'No results'
|
|
121
|
-
: `${normalizeFindIndex(selectedIndex, matchCount) + 1} of ${matchCount}`;
|
|
122
|
-
|
|
123
|
-
return (
|
|
124
|
-
<div className="squisq-find-toolbar" role="search" aria-label="Find in document">
|
|
125
|
-
<div className="squisq-find-field">
|
|
126
|
-
<Icon icon="fa-solid fa-magnifying-glass" />
|
|
127
|
-
<input
|
|
128
|
-
ref={inputRef}
|
|
129
|
-
type="search"
|
|
130
|
-
value={query}
|
|
131
|
-
className="squisq-find-input"
|
|
132
|
-
aria-label="Find in document"
|
|
133
|
-
placeholder="Find in document"
|
|
134
|
-
autoComplete="off"
|
|
135
|
-
spellCheck={false}
|
|
136
|
-
onChange={(event) => {
|
|
137
|
-
setQuery(event.target.value);
|
|
138
|
-
setSelectedIndex(0);
|
|
139
|
-
}}
|
|
140
|
-
onKeyDown={(event) => {
|
|
141
|
-
if (event.key === 'Enter') {
|
|
142
|
-
event.preventDefault();
|
|
143
|
-
moveSelection(event.shiftKey ? -1 : 1);
|
|
144
|
-
} else if (event.key === 'Escape') {
|
|
145
|
-
event.preventDefault();
|
|
146
|
-
onClose();
|
|
147
|
-
}
|
|
148
|
-
}}
|
|
149
|
-
/>
|
|
150
|
-
<span className="squisq-find-count" aria-live="polite" aria-atomic="true">
|
|
151
|
-
{resultLabel}
|
|
152
|
-
</span>
|
|
153
|
-
</div>
|
|
154
|
-
<button
|
|
155
|
-
type="button"
|
|
156
|
-
className="squisq-find-button"
|
|
157
|
-
aria-label="Previous match"
|
|
158
|
-
data-tooltip="Previous match (Shift+Enter)"
|
|
159
|
-
disabled={matchCount === 0}
|
|
160
|
-
onClick={() => moveSelection(-1)}
|
|
161
|
-
>
|
|
162
|
-
<Icon icon="fa-solid fa-chevron-up" />
|
|
163
|
-
</button>
|
|
164
|
-
<button
|
|
165
|
-
type="button"
|
|
166
|
-
className="squisq-find-button"
|
|
167
|
-
aria-label="Next match"
|
|
168
|
-
data-tooltip="Next match (Enter)"
|
|
169
|
-
disabled={matchCount === 0}
|
|
170
|
-
onClick={() => moveSelection(1)}
|
|
171
|
-
>
|
|
172
|
-
<Icon icon="fa-solid fa-chevron-down" />
|
|
173
|
-
</button>
|
|
174
|
-
<button
|
|
175
|
-
type="button"
|
|
176
|
-
className="squisq-find-button squisq-find-close"
|
|
177
|
-
aria-label="Close find"
|
|
178
|
-
data-tooltip="Close find (Esc)"
|
|
179
|
-
onClick={onClose}
|
|
180
|
-
>
|
|
181
|
-
<Icon icon="fa-solid fa-xmark" />
|
|
182
|
-
</button>
|
|
183
|
-
</div>
|
|
184
|
-
);
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
function monacoDecorationOptions(selected: boolean): MonacoEditorNs.IModelDecorationOptions {
|
|
188
|
-
return {
|
|
189
|
-
inlineClassName: selected
|
|
190
|
-
? 'squisq-find-match squisq-find-match--selected'
|
|
191
|
-
: 'squisq-find-match',
|
|
192
|
-
};
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
function applyPreviewHighlights(
|
|
196
|
-
root: HTMLElement,
|
|
197
|
-
query: string,
|
|
198
|
-
selectedIndex: number,
|
|
199
|
-
highlightName: string,
|
|
200
|
-
selectedName: string,
|
|
201
|
-
): PreviewHighlightResult {
|
|
202
|
-
const ranges = collectTextRanges(root, query);
|
|
203
|
-
const selected = normalizeFindIndex(selectedIndex, ranges.length);
|
|
204
|
-
const css = globalThis.CSS as (typeof CSS & { highlights?: HighlightRegistryLike }) | undefined;
|
|
205
|
-
const HighlightConstructor = (
|
|
206
|
-
globalThis as typeof globalThis & {
|
|
207
|
-
Highlight?: HighlightConstructorLike;
|
|
208
|
-
}
|
|
209
|
-
).Highlight;
|
|
210
|
-
|
|
211
|
-
if (css?.highlights && HighlightConstructor) {
|
|
212
|
-
css.highlights.set(highlightName, new HighlightConstructor(...ranges));
|
|
213
|
-
if (ranges.length > 0) {
|
|
214
|
-
css.highlights.set(selectedName, new HighlightConstructor(ranges[selected]));
|
|
215
|
-
scrollRangeIntoView(ranges[selected]);
|
|
216
|
-
} else {
|
|
217
|
-
css.highlights.delete(selectedName);
|
|
218
|
-
}
|
|
219
|
-
return {
|
|
220
|
-
count: ranges.length,
|
|
221
|
-
cleanup: () => {
|
|
222
|
-
css.highlights?.delete(highlightName);
|
|
223
|
-
css.highlights?.delete(selectedName);
|
|
224
|
-
},
|
|
225
|
-
};
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
// CSS Custom Highlight is broadly available in current browsers. Keep a
|
|
229
|
-
// DOM-mark fallback for older embedded webviews; Preview is read-only, and
|
|
230
|
-
// every inserted mark is unwrapped when the query changes or Find closes.
|
|
231
|
-
const marks = markPreviewRanges(ranges, selected);
|
|
232
|
-
marks
|
|
233
|
-
.find((mark) => mark.classList.contains('squisq-find-match--selected'))
|
|
234
|
-
?.scrollIntoView?.({
|
|
235
|
-
block: 'center',
|
|
236
|
-
inline: 'nearest',
|
|
237
|
-
});
|
|
238
|
-
return {
|
|
239
|
-
count: ranges.length,
|
|
240
|
-
cleanup: () => {
|
|
241
|
-
const parents = new Set<Node>();
|
|
242
|
-
for (const mark of marks) {
|
|
243
|
-
const parent = mark.parentNode;
|
|
244
|
-
if (!parent) continue;
|
|
245
|
-
parents.add(parent);
|
|
246
|
-
mark.replaceWith(...Array.from(mark.childNodes));
|
|
247
|
-
}
|
|
248
|
-
parents.forEach((parent) => parent.normalize());
|
|
249
|
-
},
|
|
250
|
-
};
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
function collectTextRanges(root: HTMLElement, query: string): Range[] {
|
|
254
|
-
if (!query.trim()) return [];
|
|
255
|
-
const doc = root.ownerDocument;
|
|
256
|
-
const showText = doc.defaultView?.NodeFilter.SHOW_TEXT ?? 4;
|
|
257
|
-
const walker = doc.createTreeWalker(root, showText);
|
|
258
|
-
const ranges: Range[] = [];
|
|
259
|
-
let current = walker.nextNode();
|
|
260
|
-
while (current) {
|
|
261
|
-
const text = current as Text;
|
|
262
|
-
const parent = text.parentElement;
|
|
263
|
-
if (
|
|
264
|
-
parent &&
|
|
265
|
-
!parent.closest(
|
|
266
|
-
'button, input, textarea, select, script, style, [aria-hidden="true"], [data-squisq-find-ignore]',
|
|
267
|
-
)
|
|
268
|
-
) {
|
|
269
|
-
for (const match of findTextMatches(text.data, query)) {
|
|
270
|
-
const range = doc.createRange();
|
|
271
|
-
range.setStart(text, match.from);
|
|
272
|
-
range.setEnd(text, match.to);
|
|
273
|
-
ranges.push(range);
|
|
274
|
-
}
|
|
275
|
-
}
|
|
276
|
-
current = walker.nextNode();
|
|
277
|
-
}
|
|
278
|
-
return ranges;
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
function scrollRangeIntoView(range: Range): void {
|
|
282
|
-
const element =
|
|
283
|
-
range.startContainer instanceof Element
|
|
284
|
-
? range.startContainer
|
|
285
|
-
: range.startContainer.parentElement;
|
|
286
|
-
(element as HTMLElement | null)?.scrollIntoView?.({ block: 'center', inline: 'nearest' });
|
|
287
|
-
}
|
|
288
|
-
|
|
289
|
-
function markPreviewRanges(ranges: Range[], selectedIndex: number): HTMLElement[] {
|
|
290
|
-
const marks: HTMLElement[] = [];
|
|
291
|
-
// Work backwards so splitting a text node does not invalidate the offsets
|
|
292
|
-
// of an earlier match in that same node.
|
|
293
|
-
[...ranges]
|
|
294
|
-
.map((range, index) => ({ range, index }))
|
|
295
|
-
.reverse()
|
|
296
|
-
.forEach(({ range, index }) => {
|
|
297
|
-
const text = range.startContainer;
|
|
298
|
-
if (!(text instanceof Text) || text !== range.endContainer || !text.parentNode) return;
|
|
299
|
-
const after = text.splitText(range.endOffset);
|
|
300
|
-
const matched = text.splitText(range.startOffset);
|
|
301
|
-
const mark = text.ownerDocument.createElement('mark');
|
|
302
|
-
mark.className =
|
|
303
|
-
index === selectedIndex
|
|
304
|
-
? 'squisq-find-match squisq-find-match--selected'
|
|
305
|
-
: 'squisq-find-match';
|
|
306
|
-
matched.replaceWith(mark);
|
|
307
|
-
mark.append(matched);
|
|
308
|
-
marks.push(mark);
|
|
309
|
-
// Keep the tail referenced so aggressive DOM implementations do not
|
|
310
|
-
// discard it before the next reverse-ordered range is processed.
|
|
311
|
-
void after;
|
|
312
|
-
});
|
|
313
|
-
return marks;
|
|
314
|
-
}
|
package/src/find/findModel.ts
DELETED
|
@@ -1,77 +0,0 @@
|
|
|
1
|
-
import type { Node as ProseMirrorNode } from '@tiptap/pm/model';
|
|
2
|
-
|
|
3
|
-
export interface FindTextMatch {
|
|
4
|
-
from: number;
|
|
5
|
-
to: number;
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* Find non-overlapping, case-insensitive literal matches. Offsets are UTF-16
|
|
10
|
-
* code-unit offsets, matching DOM Range, Monaco, and ProseMirror positions.
|
|
11
|
-
*/
|
|
12
|
-
export function findTextMatches(text: string, query: string): FindTextMatch[] {
|
|
13
|
-
const needle = query.trim();
|
|
14
|
-
if (!needle || !text) return [];
|
|
15
|
-
|
|
16
|
-
const matcher = new RegExp(escapeRegExp(needle), 'giu');
|
|
17
|
-
const matches: FindTextMatch[] = [];
|
|
18
|
-
let match: RegExpExecArray | null;
|
|
19
|
-
while ((match = matcher.exec(text)) !== null) {
|
|
20
|
-
matches.push({ from: match.index, to: match.index + match[0].length });
|
|
21
|
-
// Literal non-empty queries always advance, but retain this guard so a
|
|
22
|
-
// future matcher change cannot create an infinite loop.
|
|
23
|
-
if (match[0].length === 0) matcher.lastIndex += 1;
|
|
24
|
-
}
|
|
25
|
-
return matches;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
export function normalizeFindIndex(index: number, count: number): number {
|
|
29
|
-
if (count <= 0) return 0;
|
|
30
|
-
return ((index % count) + count) % count;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
/**
|
|
34
|
-
* Resolve visible text matches to ProseMirror document positions. Text from
|
|
35
|
-
* adjacent marked spans in one text block is searched as one run, so a query
|
|
36
|
-
* can cross a bold/italic boundary without matching across separate blocks.
|
|
37
|
-
*/
|
|
38
|
-
export function findProseMirrorMatches(doc: ProseMirrorNode, query: string): FindTextMatch[] {
|
|
39
|
-
const matches: FindTextMatch[] = [];
|
|
40
|
-
|
|
41
|
-
doc.descendants((node, blockPos) => {
|
|
42
|
-
if (!node.isTextblock) return true;
|
|
43
|
-
|
|
44
|
-
const spans: Array<FindTextMatch & { docFrom: number }> = [];
|
|
45
|
-
let text = '';
|
|
46
|
-
node.descendants((child, offset) => {
|
|
47
|
-
if (child.isText && child.text) {
|
|
48
|
-
const from = text.length;
|
|
49
|
-
text += child.text;
|
|
50
|
-
spans.push({ from, to: text.length, docFrom: blockPos + 1 + offset });
|
|
51
|
-
} else if (child.isInline) {
|
|
52
|
-
// Prevent a phrase from matching invisibly across an atom or hard break.
|
|
53
|
-
text += '\u0000';
|
|
54
|
-
}
|
|
55
|
-
return true;
|
|
56
|
-
});
|
|
57
|
-
|
|
58
|
-
for (const match of findTextMatches(text, query)) {
|
|
59
|
-
const startSpan = spans.find((span) => match.from >= span.from && match.from < span.to);
|
|
60
|
-
const endOffset = match.to - 1;
|
|
61
|
-
const endSpan = spans.find((span) => endOffset >= span.from && endOffset < span.to);
|
|
62
|
-
if (!startSpan || !endSpan) continue;
|
|
63
|
-
matches.push({
|
|
64
|
-
from: startSpan.docFrom + match.from - startSpan.from,
|
|
65
|
-
to: endSpan.docFrom + match.to - endSpan.from,
|
|
66
|
-
});
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
return false;
|
|
70
|
-
});
|
|
71
|
-
|
|
72
|
-
return matches;
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
function escapeRegExp(value: string): string {
|
|
76
|
-
return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
77
|
-
}
|
package/src/frontmatter.ts
DELETED
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* frontmatter
|
|
3
|
-
*
|
|
4
|
-
* Small helpers for the YAML frontmatter block at the top of a markdown
|
|
5
|
-
* document. The WYSIWYG editor hides frontmatter from its editing surface
|
|
6
|
-
* (and re-prepends it on save), and the block-at-a-time slicer
|
|
7
|
-
* (`blockRange.ts`) needs to know where the body starts so it never folds
|
|
8
|
-
* frontmatter into a block slice. Both share this one definition.
|
|
9
|
-
*/
|
|
10
|
-
|
|
11
|
-
/** Regex matching a YAML frontmatter block at the start of the document. */
|
|
12
|
-
export const FRONTMATTER_RE = /^---\r?\n([\s\S]*?)\r?\n---\r?\n?/;
|
|
13
|
-
|
|
14
|
-
/** Strip YAML frontmatter from markdown, returning both parts. */
|
|
15
|
-
export function stripFrontmatter(md: string): { body: string; frontmatter: string } {
|
|
16
|
-
const m = md.match(FRONTMATTER_RE);
|
|
17
|
-
if (!m) return { body: md, frontmatter: '' };
|
|
18
|
-
return { body: md.slice(m[0].length), frontmatter: m[0] };
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
/**
|
|
22
|
-
* Character offset where the document body begins — i.e. the length of the
|
|
23
|
-
* leading frontmatter block (including its trailing newline), or 0 when the
|
|
24
|
-
* source has no frontmatter.
|
|
25
|
-
*/
|
|
26
|
-
export function frontmatterEndOffset(source: string): number {
|
|
27
|
-
const m = source.match(FRONTMATTER_RE);
|
|
28
|
-
return m ? m[0].length : 0;
|
|
29
|
-
}
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
/** Canonical and legacy keys for frontmatter settings managed by the editor. */
|
|
2
|
-
export const FRONTMATTER_SETTING_KEYS = {
|
|
3
|
-
theme: { canonical: 'squisq-theme', legacy: ['themeId', 'theme'] as const },
|
|
4
|
-
transform: { canonical: 'squisq-transform', legacy: 'transform-style' as const },
|
|
5
|
-
captions: { canonical: 'squisq-captions', legacy: 'caption-style' as const },
|
|
6
|
-
coverSlide: { canonical: 'squisq-cover-slide', legacy: 'cover-slide' as const },
|
|
7
|
-
} as const;
|
|
8
|
-
|
|
9
|
-
/** Runtime defaults whose equivalent frontmatter entries can be omitted. */
|
|
10
|
-
export const FRONTMATTER_SETTING_DEFAULTS = {
|
|
11
|
-
theme: 'standard',
|
|
12
|
-
transform: '',
|
|
13
|
-
captions: 'standard',
|
|
14
|
-
coverSlide: true,
|
|
15
|
-
} as const;
|
|
16
|
-
|
|
17
|
-
/** Return `null` when a setting matches its runtime default so writers remove it. */
|
|
18
|
-
export function omitFrontmatterDefault<T extends string | number | boolean>(
|
|
19
|
-
value: T,
|
|
20
|
-
defaultValue: T,
|
|
21
|
-
): T | null {
|
|
22
|
-
return value === defaultValue ? null : value;
|
|
23
|
-
}
|