@bendyline/squisq-editor-react 2.1.0 → 2.3.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/chunk-54UGTQBO.js +862 -0
- package/dist/chunk-5JMHFAVW.js +2408 -0
- package/dist/chunk-5Q4JN4I5.js +132 -0
- package/dist/chunk-6VDYKI3L.js +49 -0
- package/dist/chunk-GS7QWYFT.js +9 -0
- package/dist/chunk-MJJK7YQB.js +949 -0
- package/dist/chunk-NITZVAXL.js +986 -0
- package/dist/chunk-TRCKHRBS.js +35234 -0
- package/dist/chunk-V44VP242.js +3256 -0
- package/dist/image-editor/index.d.ts +223 -0
- package/dist/image-editor/index.js +15 -0
- package/dist/index.d.ts +2060 -4040
- package/dist/index.js +496 -40014
- package/dist/json-editor/index.d.ts +27 -0
- package/dist/json-editor/index.js +7 -0
- package/dist/monaco.d.ts +1 -1
- package/dist/monaco.js +1 -2
- package/dist/recorder/index.d.ts +407 -0
- package/dist/recorder/index.js +43 -0
- package/dist/shell/index.d.ts +9 -0
- package/dist/shell/index.js +22 -0
- package/dist/shell-BxSCBm4H.d.ts +1064 -0
- package/dist/styles/index.css +598 -161
- package/dist/teleprompter/index.d.ts +497 -0
- package/dist/teleprompter/index.js +57 -0
- package/package.json +34 -8
- 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
|
@@ -1,381 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Pure edits over the authored ASCII timeline model.
|
|
3
|
-
*
|
|
4
|
-
* The code fence remains the source of truth. Canvas coordinates are
|
|
5
|
-
* normalized to the same global, shared scale used by
|
|
6
|
-
* `asciiTimelineToTemplateData`; commands render the edited model back into
|
|
7
|
-
* canonical fence text after applying one of these operations.
|
|
8
|
-
*/
|
|
9
|
-
|
|
10
|
-
import type {
|
|
11
|
-
AsciiTimeline,
|
|
12
|
-
AsciiTimelineEvent,
|
|
13
|
-
AsciiTimelineMarker,
|
|
14
|
-
AsciiTimelineSide,
|
|
15
|
-
AsciiTimelineTrack,
|
|
16
|
-
} from '@bendyline/squisq/doc';
|
|
17
|
-
|
|
18
|
-
const STRUCTURAL_MARKERS = /[●○◉◆◇•]+/gu;
|
|
19
|
-
const LEADING_RAILS = /^[─━═╌╍┄┅┈┉-]+/u;
|
|
20
|
-
const COORDINATE_PRECISION = 1_000;
|
|
21
|
-
const COLUMN_EPSILON = 1 / COORDINATE_PRECISION / 2;
|
|
22
|
-
|
|
23
|
-
export interface TimelineEventPatch {
|
|
24
|
-
label?: string;
|
|
25
|
-
/** `null` clears the optional description. */
|
|
26
|
-
description?: string | null;
|
|
27
|
-
side?: AsciiTimelineSide;
|
|
28
|
-
/** `null` restores the label side/default placement. */
|
|
29
|
-
descriptionSide?: AsciiTimelineSide | null;
|
|
30
|
-
callout?: boolean;
|
|
31
|
-
marker?: AsciiTimelineMarker;
|
|
32
|
-
/** Normalized position on the global timeline scale. */
|
|
33
|
-
position?: number;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
export interface AddTimelineEventOptions {
|
|
37
|
-
id?: string;
|
|
38
|
-
label?: string;
|
|
39
|
-
description?: string;
|
|
40
|
-
side?: AsciiTimelineSide;
|
|
41
|
-
descriptionSide?: AsciiTimelineSide;
|
|
42
|
-
callout?: boolean;
|
|
43
|
-
marker?: AsciiTimelineMarker;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
export interface AddTimelineEventResult {
|
|
47
|
-
timeline: AsciiTimeline;
|
|
48
|
-
eventId: string;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
export interface AddTimelineTrackOptions {
|
|
52
|
-
id?: string;
|
|
53
|
-
label?: string;
|
|
54
|
-
eventId?: string;
|
|
55
|
-
eventLabel?: string;
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
export interface AddTimelineTrackResult {
|
|
59
|
-
timeline: AsciiTimeline;
|
|
60
|
-
trackId: string;
|
|
61
|
-
eventId: string;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
/**
|
|
65
|
-
* Normalize editable one-line prose exactly as the canonical core renderer
|
|
66
|
-
* does. This prevents the verified command path from accepting a value that
|
|
67
|
-
* is silently reinterpreted as timeline syntax on its first render.
|
|
68
|
-
*/
|
|
69
|
-
export function sanitizeTimelineText(value: string): string {
|
|
70
|
-
const normalized = value
|
|
71
|
-
.normalize('NFC')
|
|
72
|
-
.replace(/[\r\n\t]+/g, ' ')
|
|
73
|
-
.replace(STRUCTURAL_MARKERS, '·')
|
|
74
|
-
.replace(/\*/g, '∗')
|
|
75
|
-
.replace(/::/g, '∶')
|
|
76
|
-
.replace(/\s+/g, ' ')
|
|
77
|
-
.trim();
|
|
78
|
-
|
|
79
|
-
return normalized.replace(LEADING_RAILS, (run) =>
|
|
80
|
-
Array.from(run, (character) => (character === '-' ? '−' : '–')).join(''),
|
|
81
|
-
);
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
/** Return a globally unique, renderer-safe event id. */
|
|
85
|
-
export function nextTimelineEventId(timeline: AsciiTimeline, base = 'event'): string {
|
|
86
|
-
const used = new Set(timeline.tracks.flatMap((track) => track.events.map((event) => event.id)));
|
|
87
|
-
const safeBase = safeId(base, 'event');
|
|
88
|
-
if (!used.has(safeBase)) return safeBase;
|
|
89
|
-
let suffix = 2;
|
|
90
|
-
while (used.has(`${safeBase}-${suffix}`)) suffix++;
|
|
91
|
-
return `${safeBase}-${suffix}`;
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
/** Return a renderer-safe id that is unique among timeline tracks. */
|
|
95
|
-
export function nextTimelineTrackId(timeline: AsciiTimeline, base = 'track'): string {
|
|
96
|
-
const used = new Set(timeline.tracks.map((track) => track.id));
|
|
97
|
-
const safeBase = safeId(base, 'track');
|
|
98
|
-
if (!used.has(safeBase)) return safeBase;
|
|
99
|
-
let suffix = 2;
|
|
100
|
-
while (used.has(`${safeBase}-${suffix}`)) suffix++;
|
|
101
|
-
return `${safeBase}-${suffix}`;
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
/**
|
|
105
|
-
* Add a representable track with one starter point. Empty tracks are not part
|
|
106
|
-
* of the authored ASCII grammar, so line creation and its first point are one
|
|
107
|
-
* atomic operation/undo step.
|
|
108
|
-
*/
|
|
109
|
-
export function addTimelineTrackOp(
|
|
110
|
-
timeline: AsciiTimeline,
|
|
111
|
-
options: AddTimelineTrackOptions = {},
|
|
112
|
-
): AddTimelineTrackResult {
|
|
113
|
-
const label = sanitizeTimelineText(options.label ?? '') || 'New line';
|
|
114
|
-
const eventLabel = sanitizeTimelineText(options.eventLabel ?? '') || 'New event';
|
|
115
|
-
const trackId = nextTimelineTrackId(timeline, options.id ?? label);
|
|
116
|
-
const eventId = nextTimelineEventId(timeline, options.eventId ?? eventLabel);
|
|
117
|
-
const { start, span } = globalBounds(timeline);
|
|
118
|
-
const column = Math.round((start + span / 2) * COORDINATE_PRECISION) / COORDINATE_PRECISION;
|
|
119
|
-
const row = timeline.tracks.reduce((maximum, track) => Math.max(maximum, track.row), -1) + 1;
|
|
120
|
-
const track: AsciiTimelineTrack = {
|
|
121
|
-
id: trackId,
|
|
122
|
-
label,
|
|
123
|
-
row,
|
|
124
|
-
startColumn: start,
|
|
125
|
-
endColumn: start + span,
|
|
126
|
-
events: [
|
|
127
|
-
{
|
|
128
|
-
id: eventId,
|
|
129
|
-
label: eventLabel,
|
|
130
|
-
column,
|
|
131
|
-
side: 'above',
|
|
132
|
-
marker: 'filled',
|
|
133
|
-
},
|
|
134
|
-
],
|
|
135
|
-
};
|
|
136
|
-
|
|
137
|
-
return {
|
|
138
|
-
timeline: {
|
|
139
|
-
...timeline,
|
|
140
|
-
tracks: [...timeline.tracks, track],
|
|
141
|
-
width: Math.max(timeline.width, Math.ceil(track.endColumn) + 1),
|
|
142
|
-
height: Math.max(timeline.height, row + 1),
|
|
143
|
-
},
|
|
144
|
-
trackId,
|
|
145
|
-
eventId,
|
|
146
|
-
};
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
/** Rename a track without changing its stable id, points, or branches. */
|
|
150
|
-
export function updateTimelineTrackOp(
|
|
151
|
-
timeline: AsciiTimeline,
|
|
152
|
-
trackId: string,
|
|
153
|
-
label: string,
|
|
154
|
-
): AsciiTimeline {
|
|
155
|
-
const track = timeline.tracks.find((candidate) => candidate.id === trackId);
|
|
156
|
-
const nextLabel = sanitizeTimelineText(label);
|
|
157
|
-
if (!track || !nextLabel || track.label === nextLabel) return timeline;
|
|
158
|
-
return {
|
|
159
|
-
...timeline,
|
|
160
|
-
tracks: timeline.tracks.map((candidate) =>
|
|
161
|
-
candidate.id === trackId ? { ...candidate, label: nextLabel } : candidate,
|
|
162
|
-
),
|
|
163
|
-
};
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
/**
|
|
167
|
-
* Remove a whole track, including branches incident to any of its points.
|
|
168
|
-
* Keep the last track so the source fence remains representable and mounted.
|
|
169
|
-
*/
|
|
170
|
-
export function removeTimelineTrackOp(timeline: AsciiTimeline, trackId: string): AsciiTimeline {
|
|
171
|
-
if (timeline.tracks.length <= 1) return timeline;
|
|
172
|
-
const track = timeline.tracks.find((candidate) => candidate.id === trackId);
|
|
173
|
-
if (!track) return timeline;
|
|
174
|
-
const removedEventIds = new Set(track.events.map((event) => event.id));
|
|
175
|
-
return {
|
|
176
|
-
...timeline,
|
|
177
|
-
tracks: timeline.tracks.filter((candidate) => candidate.id !== trackId),
|
|
178
|
-
links: timeline.links.filter(
|
|
179
|
-
(link) => !removedEventIds.has(link.source) && !removedEventIds.has(link.target),
|
|
180
|
-
),
|
|
181
|
-
};
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
/**
|
|
185
|
-
* Add a visible point to `trackId` at a normalized global rail position.
|
|
186
|
-
* Returns the stable id so the canvas can select/focus the new point after
|
|
187
|
-
* the fence rewrite.
|
|
188
|
-
*/
|
|
189
|
-
export function addTimelineEventOp(
|
|
190
|
-
timeline: AsciiTimeline,
|
|
191
|
-
trackId: string,
|
|
192
|
-
position: number,
|
|
193
|
-
options: AddTimelineEventOptions = {},
|
|
194
|
-
): AddTimelineEventResult | null {
|
|
195
|
-
if (!Number.isFinite(position)) return null;
|
|
196
|
-
const track = timeline.tracks.find((candidate) => candidate.id === trackId);
|
|
197
|
-
if (!track) return null;
|
|
198
|
-
|
|
199
|
-
const column = columnAtPosition(timeline, position);
|
|
200
|
-
// Two coincident markers cannot be independently hit on the canvas. A
|
|
201
|
-
// marker click should select the existing point rather than adding atop it.
|
|
202
|
-
if (track.events.some((event) => Math.abs(event.column - column) <= COLUMN_EPSILON)) return null;
|
|
203
|
-
|
|
204
|
-
const label = sanitizeTimelineText(options.label ?? '') || 'New event';
|
|
205
|
-
const description = sanitizeTimelineText(options.description ?? '');
|
|
206
|
-
const eventId = nextTimelineEventId(timeline, options.id ?? label);
|
|
207
|
-
const insertionIndex = track.events.filter((event) => event.column < column).length;
|
|
208
|
-
const side = options.side ?? (insertionIndex % 2 === 0 ? 'above' : 'below');
|
|
209
|
-
const event: AsciiTimelineEvent = {
|
|
210
|
-
id: eventId,
|
|
211
|
-
label,
|
|
212
|
-
...(description ? { description } : {}),
|
|
213
|
-
column,
|
|
214
|
-
side,
|
|
215
|
-
...(description && options.descriptionSide ? { descriptionSide: options.descriptionSide } : {}),
|
|
216
|
-
...(options.callout === false ? { callout: false } : {}),
|
|
217
|
-
marker: options.marker ?? 'filled',
|
|
218
|
-
};
|
|
219
|
-
|
|
220
|
-
const events = [...track.events, event].sort(
|
|
221
|
-
(left, right) => left.column - right.column || left.id.localeCompare(right.id),
|
|
222
|
-
);
|
|
223
|
-
const tracks = timeline.tracks.map((candidate) =>
|
|
224
|
-
candidate.id === trackId ? { ...candidate, events } : candidate,
|
|
225
|
-
);
|
|
226
|
-
// ASCII art has no distinct diamond glyph. Promote the authored vocabulary
|
|
227
|
-
// when the inspector explicitly requests one rather than silently folding
|
|
228
|
-
// the user's choice back to a filled point in the renderer.
|
|
229
|
-
const style = options.marker === 'diamond' ? 'unicode' : timeline.style;
|
|
230
|
-
return { timeline: { ...timeline, tracks, style }, eventId };
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
/** Update one point without changing its stable id or branch endpoints. */
|
|
234
|
-
export function updateTimelineEventOp(
|
|
235
|
-
timeline: AsciiTimeline,
|
|
236
|
-
eventId: string,
|
|
237
|
-
patch: TimelineEventPatch,
|
|
238
|
-
): AsciiTimeline {
|
|
239
|
-
const location = findEvent(timeline, eventId);
|
|
240
|
-
if (!location || Object.keys(patch).length === 0) return timeline;
|
|
241
|
-
|
|
242
|
-
const hasLabel = Object.prototype.hasOwnProperty.call(patch, 'label');
|
|
243
|
-
const hasDescription = Object.prototype.hasOwnProperty.call(patch, 'description');
|
|
244
|
-
const hasDescriptionSide = Object.prototype.hasOwnProperty.call(patch, 'descriptionSide');
|
|
245
|
-
const hasPosition = Object.prototype.hasOwnProperty.call(patch, 'position');
|
|
246
|
-
const label = hasLabel ? sanitizeTimelineText(patch.label ?? '') : location.event.label;
|
|
247
|
-
if (!label) return timeline;
|
|
248
|
-
|
|
249
|
-
let description = hasDescription
|
|
250
|
-
? sanitizeTimelineText(patch.description ?? '')
|
|
251
|
-
: location.event.description;
|
|
252
|
-
if (!description) description = undefined;
|
|
253
|
-
|
|
254
|
-
let column = location.event.column;
|
|
255
|
-
if (hasPosition) {
|
|
256
|
-
if (!Number.isFinite(patch.position)) return timeline;
|
|
257
|
-
column = columnAtPosition(timeline, patch.position as number);
|
|
258
|
-
const collision = location.track.events.some(
|
|
259
|
-
(event) => event.id !== eventId && Math.abs(event.column - column) <= COLUMN_EPSILON,
|
|
260
|
-
);
|
|
261
|
-
if (collision) return timeline;
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
const editedText = hasLabel || hasDescription;
|
|
265
|
-
const callout =
|
|
266
|
-
patch.callout !== undefined
|
|
267
|
-
? patch.callout
|
|
268
|
-
: editedText && location.event.callout === false
|
|
269
|
-
? true
|
|
270
|
-
: location.event.callout;
|
|
271
|
-
const descriptionSide = !description
|
|
272
|
-
? undefined
|
|
273
|
-
: hasDescriptionSide
|
|
274
|
-
? (patch.descriptionSide ?? undefined)
|
|
275
|
-
: location.event.descriptionSide;
|
|
276
|
-
|
|
277
|
-
const nextEvent: AsciiTimelineEvent = {
|
|
278
|
-
...location.event,
|
|
279
|
-
label,
|
|
280
|
-
column,
|
|
281
|
-
...(description ? { description } : {}),
|
|
282
|
-
...(patch.side ? { side: patch.side } : {}),
|
|
283
|
-
...(descriptionSide ? { descriptionSide } : {}),
|
|
284
|
-
...(callout !== undefined ? { callout } : {}),
|
|
285
|
-
...(patch.marker ? { marker: patch.marker } : {}),
|
|
286
|
-
};
|
|
287
|
-
if (!description) delete nextEvent.description;
|
|
288
|
-
if (!descriptionSide) delete nextEvent.descriptionSide;
|
|
289
|
-
|
|
290
|
-
if (eventsEqual(location.event, nextEvent)) return timeline;
|
|
291
|
-
const events = location.track.events
|
|
292
|
-
.map((event) => (event.id === eventId ? nextEvent : event))
|
|
293
|
-
.sort((left, right) => left.column - right.column || left.id.localeCompare(right.id));
|
|
294
|
-
// A point authored exactly on a track boundary anchors that edge. Let the
|
|
295
|
-
// old scale remain stable throughout a drag preview, then move/reassign the
|
|
296
|
-
// boundary in the single drop transaction. Crossing the next point hands
|
|
297
|
-
// the edge to that point; moving any point outside a shorter track expands
|
|
298
|
-
// the track so every marker remains inside its authored bounds.
|
|
299
|
-
const movedPosition = hasPosition && nextEvent.column !== location.event.column;
|
|
300
|
-
const anchoredStart =
|
|
301
|
-
movedPosition && Math.abs(location.event.column - location.track.startColumn) <= COLUMN_EPSILON;
|
|
302
|
-
const anchoredEnd =
|
|
303
|
-
movedPosition && Math.abs(location.event.column - location.track.endColumn) <= COLUMN_EPSILON;
|
|
304
|
-
const eventStart = Math.min(...events.map((event) => event.column));
|
|
305
|
-
const eventEnd = Math.max(...events.map((event) => event.column));
|
|
306
|
-
const startColumn = anchoredStart ? eventStart : Math.min(location.track.startColumn, eventStart);
|
|
307
|
-
const endColumn = anchoredEnd ? eventEnd : Math.max(location.track.endColumn, eventEnd);
|
|
308
|
-
const tracks = timeline.tracks.map((track) =>
|
|
309
|
-
track.id === location.track.id ? { ...track, startColumn, endColumn, events } : track,
|
|
310
|
-
);
|
|
311
|
-
const style = patch.marker === 'diamond' ? 'unicode' : timeline.style;
|
|
312
|
-
return { ...timeline, tracks, style };
|
|
313
|
-
}
|
|
314
|
-
|
|
315
|
-
/**
|
|
316
|
-
* Remove a point and every incident branch. Empty tracks are removed because
|
|
317
|
-
* the canonical renderer cannot represent them. The last point in the whole
|
|
318
|
-
* timeline is retained so an edit cannot erase its own source fence/widget.
|
|
319
|
-
*/
|
|
320
|
-
export function removeTimelineEventOp(timeline: AsciiTimeline, eventId: string): AsciiTimeline {
|
|
321
|
-
const location = findEvent(timeline, eventId);
|
|
322
|
-
if (!location) return timeline;
|
|
323
|
-
const eventCount = timeline.tracks.reduce((sum, track) => sum + track.events.length, 0);
|
|
324
|
-
if (eventCount <= 1) return timeline;
|
|
325
|
-
|
|
326
|
-
const tracks = timeline.tracks
|
|
327
|
-
.map((track) =>
|
|
328
|
-
track.id === location.track.id
|
|
329
|
-
? { ...track, events: track.events.filter((event) => event.id !== eventId) }
|
|
330
|
-
: track,
|
|
331
|
-
)
|
|
332
|
-
.filter((track) => track.events.length > 0);
|
|
333
|
-
const links = timeline.links.filter((link) => link.source !== eventId && link.target !== eventId);
|
|
334
|
-
return { ...timeline, tracks, links };
|
|
335
|
-
}
|
|
336
|
-
|
|
337
|
-
function globalBounds(timeline: AsciiTimeline): { start: number; span: number } {
|
|
338
|
-
if (timeline.tracks.length === 0) return { start: 0, span: 1 };
|
|
339
|
-
const start = Math.min(...timeline.tracks.map((track) => track.startColumn));
|
|
340
|
-
const end = Math.max(...timeline.tracks.map((track) => track.endColumn));
|
|
341
|
-
return { start, span: Math.max(1, end - start) };
|
|
342
|
-
}
|
|
343
|
-
|
|
344
|
-
function columnAtPosition(timeline: AsciiTimeline, position: number): number {
|
|
345
|
-
const { start, span } = globalBounds(timeline);
|
|
346
|
-
const clamped = Math.max(0, Math.min(1, position));
|
|
347
|
-
return Math.round((start + clamped * span) * COORDINATE_PRECISION) / COORDINATE_PRECISION;
|
|
348
|
-
}
|
|
349
|
-
|
|
350
|
-
function safeId(value: string, fallback: string): string {
|
|
351
|
-
return (
|
|
352
|
-
sanitizeTimelineText(value)
|
|
353
|
-
.toLowerCase()
|
|
354
|
-
.replace(/[^a-z0-9_.~-]+/g, '-')
|
|
355
|
-
.replace(/^-+|-+$/g, '') || fallback
|
|
356
|
-
);
|
|
357
|
-
}
|
|
358
|
-
|
|
359
|
-
function findEvent(
|
|
360
|
-
timeline: AsciiTimeline,
|
|
361
|
-
eventId: string,
|
|
362
|
-
): { track: AsciiTimeline['tracks'][number]; event: AsciiTimelineEvent } | null {
|
|
363
|
-
for (const track of timeline.tracks) {
|
|
364
|
-
const event = track.events.find((candidate) => candidate.id === eventId);
|
|
365
|
-
if (event) return { track, event };
|
|
366
|
-
}
|
|
367
|
-
return null;
|
|
368
|
-
}
|
|
369
|
-
|
|
370
|
-
function eventsEqual(left: AsciiTimelineEvent, right: AsciiTimelineEvent): boolean {
|
|
371
|
-
return (
|
|
372
|
-
left.id === right.id &&
|
|
373
|
-
left.label === right.label &&
|
|
374
|
-
left.description === right.description &&
|
|
375
|
-
left.column === right.column &&
|
|
376
|
-
left.side === right.side &&
|
|
377
|
-
left.descriptionSide === right.descriptionSide &&
|
|
378
|
-
left.callout === right.callout &&
|
|
379
|
-
left.marker === right.marker
|
|
380
|
-
);
|
|
381
|
-
}
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
/** Paste gate for bare, high-confidence Unicode timeline art. */
|
|
2
|
-
|
|
3
|
-
import { detectAsciiTimeline } from '@bendyline/squisq/doc';
|
|
4
|
-
|
|
5
|
-
export function shouldPasteAsTimelineFence(text: string): boolean {
|
|
6
|
-
if (!text.trim() || /^\s*```/m.test(text)) return false;
|
|
7
|
-
return detectAsciiTimeline(text).isTimeline;
|
|
8
|
-
}
|
package/src/timelineSource.ts
DELETED
|
@@ -1,244 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* timelineSource
|
|
3
|
-
*
|
|
4
|
-
* Line-level markdown rewrites for the timeline editor: set a block's
|
|
5
|
-
* `duration` on its heading's Pandoc attribute block, and patch a media
|
|
6
|
-
* clip's `startAt` / `clipStart` / `clipEnd` / `spillover` on its `{[audio …]}`
|
|
7
|
-
* / `{[video …]}` annotation line. Both preserve everything else on the line
|
|
8
|
-
* (template annotations, ids, classes, other params) by reusing the shared
|
|
9
|
-
* tokenizers rather than regex-replacing values.
|
|
10
|
-
*/
|
|
11
|
-
|
|
12
|
-
import {
|
|
13
|
-
matchTrailingPandocAttr,
|
|
14
|
-
matchTrailingTemplateAnnotation,
|
|
15
|
-
parsePandocAttrTokens,
|
|
16
|
-
serializePandocAttributes,
|
|
17
|
-
tokenizeAttrTokens,
|
|
18
|
-
splitKeyValueToken,
|
|
19
|
-
quoteAttrValue,
|
|
20
|
-
} from '@bendyline/squisq/markdown';
|
|
21
|
-
|
|
22
|
-
/** Format a seconds value compactly: integers bare, else up to 2 decimals. */
|
|
23
|
-
export function formatSeconds(seconds: number): string {
|
|
24
|
-
const clamped = Math.max(0, seconds);
|
|
25
|
-
if (Number.isInteger(clamped)) return String(clamped);
|
|
26
|
-
return String(Math.round(clamped * 100) / 100);
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
/**
|
|
30
|
-
* Set/insert a `duration` on the heading at 1-based `line`, written in the
|
|
31
|
-
* squisq-native squiggly form — `{[duration=<seconds>]}` on its own, or
|
|
32
|
-
* folded into an existing `{[template …]}` annotation. Preserves any `{#id}`,
|
|
33
|
-
* classes, and other Pandoc params, and migrates a legacy Pandoc
|
|
34
|
-
* `{duration=…}` to the squiggly form (dropping the stale Pandoc key so the
|
|
35
|
-
* two can't disagree). Returns the new full source, or null when the line
|
|
36
|
-
* isn't an ATX heading.
|
|
37
|
-
*/
|
|
38
|
-
export function setBlockDurationInSource(
|
|
39
|
-
source: string,
|
|
40
|
-
line: number,
|
|
41
|
-
seconds: number,
|
|
42
|
-
): string | null {
|
|
43
|
-
const lines = source.split('\n');
|
|
44
|
-
const idx = line - 1;
|
|
45
|
-
if (idx < 0 || idx >= lines.length) return null;
|
|
46
|
-
const original = lines[idx];
|
|
47
|
-
if (!/^#{1,6}\s/.test(original)) return null;
|
|
48
|
-
|
|
49
|
-
const value = formatSeconds(seconds);
|
|
50
|
-
|
|
51
|
-
// Peel the trailing `{[…]}` annotation (if any) off the line; we'll fold
|
|
52
|
-
// `duration` into it. Canonical heading order is `{#pandoc} {[squiggly]}`,
|
|
53
|
-
// so the squiggly annotation is the last `{…}` on the line.
|
|
54
|
-
let rest = original;
|
|
55
|
-
let annotationInner: string | null = null;
|
|
56
|
-
const tpl = matchTrailingTemplateAnnotation(rest);
|
|
57
|
-
if (tpl) {
|
|
58
|
-
annotationInner = tpl.inner.trim();
|
|
59
|
-
rest = rest.slice(0, tpl.index).replace(/\s+$/, '');
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
// Then peel a trailing Pandoc `{…}` block; drop its `duration` so a legacy
|
|
63
|
-
// value can't shadow the squiggly one we're writing. Keep the rest (id,
|
|
64
|
-
// classes, other params).
|
|
65
|
-
let pandocSerialized: string | null = null;
|
|
66
|
-
const pa = matchTrailingPandocAttr(rest);
|
|
67
|
-
if (pa) {
|
|
68
|
-
const attrs = parsePandocAttrTokens(pa.inner.trim());
|
|
69
|
-
if (attrs.params) {
|
|
70
|
-
delete attrs.params.duration;
|
|
71
|
-
if (Object.keys(attrs.params).length === 0) delete attrs.params;
|
|
72
|
-
}
|
|
73
|
-
pandocSerialized = serializePandocAttributes(attrs);
|
|
74
|
-
rest = rest.slice(0, pa.index).replace(/\s+$/, '');
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
const annotation = setTemplateParam(annotationInner, 'duration', value);
|
|
78
|
-
const parts = [rest];
|
|
79
|
-
if (pandocSerialized) parts.push(pandocSerialized);
|
|
80
|
-
parts.push(annotation);
|
|
81
|
-
lines[idx] = parts.join(' ');
|
|
82
|
-
return lines.join('\n');
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
/**
|
|
86
|
-
* Build a `{[…]}` annotation from an existing inner string (or null for a
|
|
87
|
-
* fresh one), setting `key=value` and preserving the template name plus any
|
|
88
|
-
* other params in their original order.
|
|
89
|
-
*/
|
|
90
|
-
function setTemplateParam(inner: string | null, key: string, value: string): string {
|
|
91
|
-
const tokens = inner ? tokenizeAttrTokens(inner) : [];
|
|
92
|
-
const firstIsParam = tokens.length > 0 && tokens[0].indexOf('=') > 0;
|
|
93
|
-
const template = firstIsParam || tokens.length === 0 ? undefined : tokens[0];
|
|
94
|
-
|
|
95
|
-
const params: Record<string, string> = {};
|
|
96
|
-
const order: string[] = [];
|
|
97
|
-
for (let i = template ? 1 : 0; i < tokens.length; i++) {
|
|
98
|
-
const kv = splitKeyValueToken(tokens[i]);
|
|
99
|
-
if (kv) {
|
|
100
|
-
if (!(kv.key in params)) order.push(kv.key);
|
|
101
|
-
params[kv.key] = kv.value;
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
if (!(key in params)) order.push(key);
|
|
105
|
-
params[key] = value;
|
|
106
|
-
|
|
107
|
-
const parts = template ? [template] : [];
|
|
108
|
-
for (const k of order) parts.push(`${k}=${quoteAttrValue(params[k])}`);
|
|
109
|
-
return `{[${parts.join(' ')}]}`;
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
/** A patch to a media clip; numeric values are seconds, `null` removes the key. */
|
|
113
|
-
export interface MediaClipPatch {
|
|
114
|
-
startAt?: number | null;
|
|
115
|
-
clipStart?: number | null;
|
|
116
|
-
clipEnd?: number | null;
|
|
117
|
-
spillover?: boolean | null;
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
/**
|
|
121
|
-
* Patch the `{[audio …]}` / `{[video …]}` annotation at 1-based `line`.
|
|
122
|
-
* Preserves the template name and any params not in the patch. Returns the
|
|
123
|
-
* new full source, or null when the line isn't a media annotation.
|
|
124
|
-
*/
|
|
125
|
-
export function setMediaClipInSource(
|
|
126
|
-
source: string,
|
|
127
|
-
line: number,
|
|
128
|
-
patch: MediaClipPatch,
|
|
129
|
-
): string | null {
|
|
130
|
-
const lines = source.split('\n');
|
|
131
|
-
const idx = line - 1;
|
|
132
|
-
if (idx < 0 || idx >= lines.length) return null;
|
|
133
|
-
const original = lines[idx];
|
|
134
|
-
const m = matchTrailingTemplateAnnotation(original);
|
|
135
|
-
if (!m) return null;
|
|
136
|
-
|
|
137
|
-
const tokens = tokenizeAttrTokens(m.inner.trim());
|
|
138
|
-
const firstIsParam = tokens.length > 0 && tokens[0].indexOf('=') > 0;
|
|
139
|
-
const template = firstIsParam || tokens.length === 0 ? undefined : tokens[0];
|
|
140
|
-
if (!template || !['audio', 'video', 'media'].includes(template)) return null;
|
|
141
|
-
|
|
142
|
-
const params: Record<string, string> = {};
|
|
143
|
-
const order: string[] = [];
|
|
144
|
-
for (let i = 1; i < tokens.length; i++) {
|
|
145
|
-
const kv = splitKeyValueToken(tokens[i]);
|
|
146
|
-
if (kv) {
|
|
147
|
-
if (!(kv.key in params)) order.push(kv.key);
|
|
148
|
-
params[kv.key] = kv.value;
|
|
149
|
-
}
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
const apply = (key: string, value: string | null) => {
|
|
153
|
-
if (value === null) {
|
|
154
|
-
delete params[key];
|
|
155
|
-
const i = order.indexOf(key);
|
|
156
|
-
if (i >= 0) order.splice(i, 1);
|
|
157
|
-
} else {
|
|
158
|
-
if (!(key in params)) order.push(key);
|
|
159
|
-
params[key] = value;
|
|
160
|
-
}
|
|
161
|
-
};
|
|
162
|
-
if (patch.startAt !== undefined)
|
|
163
|
-
apply('startAt', patch.startAt == null ? null : formatSeconds(patch.startAt));
|
|
164
|
-
if (patch.clipStart !== undefined)
|
|
165
|
-
apply('clipStart', patch.clipStart == null ? null : formatSeconds(patch.clipStart));
|
|
166
|
-
if (patch.clipEnd !== undefined)
|
|
167
|
-
apply('clipEnd', patch.clipEnd == null ? null : formatSeconds(patch.clipEnd));
|
|
168
|
-
if (patch.spillover !== undefined) apply('spillover', patch.spillover ? 'true' : null);
|
|
169
|
-
|
|
170
|
-
const parts = [template, ...order.map((k) => `${k}=${quoteAttrValue(params[k])}`)];
|
|
171
|
-
const annotation = `{[${parts.join(' ')}]}`;
|
|
172
|
-
const prefix = original.slice(0, m.index).replace(/\s+$/, '');
|
|
173
|
-
lines[idx] = prefix ? `${prefix} ${annotation}` : annotation;
|
|
174
|
-
return lines.join('\n');
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
/** A timed media clip's identity for (re)writing its annotation. */
|
|
178
|
-
export interface ClipSpec {
|
|
179
|
-
kind: 'audio' | 'video';
|
|
180
|
-
src: string;
|
|
181
|
-
clipStart?: number;
|
|
182
|
-
clipEnd?: number;
|
|
183
|
-
spillover?: boolean;
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
/** Build a `{[audio|video src=… startAt=… clipEnd=…]}` annotation string. */
|
|
187
|
-
export function buildClipAnnotation(spec: ClipSpec, startAt: number): string {
|
|
188
|
-
const parts: string[] = [spec.kind, `src=${quoteAttrValue(spec.src)}`];
|
|
189
|
-
if (startAt > 0) parts.push(`startAt=${formatSeconds(startAt)}`);
|
|
190
|
-
if (spec.clipStart != null && spec.clipStart > 0) {
|
|
191
|
-
parts.push(`clipStart=${formatSeconds(spec.clipStart)}`);
|
|
192
|
-
}
|
|
193
|
-
if (spec.clipEnd != null) parts.push(`clipEnd=${formatSeconds(spec.clipEnd)}`);
|
|
194
|
-
if (spec.spillover) parts.push('spillover=true');
|
|
195
|
-
return `{[${parts.join(' ')}]}`;
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
/** Index of the heading line at/above `idx` (−1 for the preamble). */
|
|
199
|
-
function headingIndexAbove(lines: string[], idx: number): number {
|
|
200
|
-
for (let i = Math.min(idx, lines.length - 1); i >= 0; i--) {
|
|
201
|
-
if (/^#{1,6}\s/.test(lines[i])) return i;
|
|
202
|
-
}
|
|
203
|
-
return -1;
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
/**
|
|
207
|
-
* Place a timed clip into the block whose heading is at 1-based
|
|
208
|
-
* `targetHeadingLine`, written as a `{[…]}` annotation. The clip's current
|
|
209
|
-
* authoring line at 1-based `fromLine` (a media embed or an existing
|
|
210
|
-
* annotation) is removed; when the target is the same block the line is
|
|
211
|
-
* rewritten in place. Returns the new source, or null on bad input.
|
|
212
|
-
*
|
|
213
|
-
* This is what lets a clip be dragged from one block into another on the
|
|
214
|
-
* timeline: its representation relocates to the target block's body.
|
|
215
|
-
*/
|
|
216
|
-
export function placeClipInBlock(
|
|
217
|
-
source: string,
|
|
218
|
-
fromLine: number,
|
|
219
|
-
targetHeadingLine: number,
|
|
220
|
-
spec: ClipSpec,
|
|
221
|
-
startAt: number,
|
|
222
|
-
): string | null {
|
|
223
|
-
const lines = source.split('\n');
|
|
224
|
-
const fromIdx = fromLine - 1;
|
|
225
|
-
if (fromIdx < 0 || fromIdx >= lines.length) return null;
|
|
226
|
-
const targetIdx0 = targetHeadingLine - 1;
|
|
227
|
-
if (targetIdx0 < 0 || targetIdx0 >= lines.length) return null;
|
|
228
|
-
|
|
229
|
-
const annotation = buildClipAnnotation(spec, Math.max(0, startAt));
|
|
230
|
-
|
|
231
|
-
// Same block → rewrite the line in place (preserves position).
|
|
232
|
-
if (headingIndexAbove(lines, fromIdx) === targetIdx0) {
|
|
233
|
-
lines[fromIdx] = annotation;
|
|
234
|
-
return lines.join('\n');
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
// Cross-block → remove the source line and insert into the target block,
|
|
238
|
-
// right after its heading on its own paragraph.
|
|
239
|
-
lines.splice(fromIdx, 1);
|
|
240
|
-
let targetIdx = targetIdx0;
|
|
241
|
-
if (fromIdx < targetIdx) targetIdx -= 1;
|
|
242
|
-
lines.splice(targetIdx + 1, 0, '', annotation);
|
|
243
|
-
return lines.join('\n');
|
|
244
|
-
}
|