@datalayer/lexical-loro 1.0.2 → 1.0.5
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/README.md +2 -2
- package/lib/App.d.ts +2 -0
- package/lib/App.js +141 -0
- package/lib/Editor.d.ts +2 -0
- package/lib/Editor.js +116 -0
- package/lib/Settings.d.ts +2 -0
- package/lib/Settings.js +57 -0
- package/lib/appSettings.d.ts +36 -0
- package/lib/appSettings.js +48 -0
- package/lib/collab/loro/Bindings.d.ts +41 -0
- package/lib/collab/loro/Bindings.js +99 -0
- package/lib/collab/loro/Debug.d.ts +33 -0
- package/lib/collab/loro/Debug.js +452 -0
- package/lib/collab/loro/LexicalCollaborationContext.d.ts +19 -0
- package/lib/collab/loro/LexicalCollaborationContext.js +52 -0
- package/lib/collab/loro/LexicalCollaborationPlugin.d.ts +29 -0
- package/lib/collab/loro/LexicalCollaborationPlugin.js +111 -0
- package/lib/collab/loro/State.d.ts +53 -0
- package/lib/collab/loro/State.js +94 -0
- package/lib/collab/loro/components/LoroCollaborationUI.d.ts +13 -0
- package/lib/collab/loro/components/LoroCollaborationUI.js +9 -0
- package/lib/collab/loro/components/LoroCollaborators.d.ts +8 -0
- package/lib/collab/loro/components/LoroCollaborators.js +90 -0
- package/lib/collab/loro/components/index.d.ts +2 -0
- package/lib/collab/loro/components/index.js +6 -0
- package/lib/collab/loro/index.d.ts +6 -0
- package/lib/collab/loro/index.js +10 -0
- package/lib/collab/loro/integrators/BaseIntegrator.d.ts +14 -0
- package/lib/collab/loro/integrators/BaseIntegrator.js +5 -0
- package/lib/collab/loro/integrators/CounterIntegrator.d.ts +23 -0
- package/lib/collab/loro/integrators/CounterIntegrator.js +44 -0
- package/lib/collab/loro/integrators/ListIntegrator.d.ts +23 -0
- package/lib/collab/loro/integrators/ListIntegrator.js +53 -0
- package/lib/collab/loro/integrators/MapIntegrator.d.ts +24 -0
- package/lib/collab/loro/integrators/MapIntegrator.js +235 -0
- package/lib/collab/loro/integrators/TextIntegrator.d.ts +25 -0
- package/lib/collab/loro/integrators/TextIntegrator.js +55 -0
- package/lib/collab/loro/integrators/TreeIntegrator.d.ts +41 -0
- package/lib/collab/loro/integrators/TreeIntegrator.js +293 -0
- package/lib/collab/loro/nodes/NodeFactory.d.ts +15 -0
- package/lib/collab/loro/nodes/NodeFactory.js +80 -0
- package/lib/collab/loro/nodes/NodesMapper.d.ts +120 -0
- package/lib/collab/loro/nodes/NodesMapper.js +289 -0
- package/lib/collab/loro/propagators/DecoratorNodePropagator.d.ts +60 -0
- package/lib/collab/loro/propagators/DecoratorNodePropagator.js +306 -0
- package/lib/collab/loro/propagators/ElementNodePropagator.d.ts +62 -0
- package/lib/collab/loro/propagators/ElementNodePropagator.js +393 -0
- package/lib/collab/loro/propagators/LineBreakNodePropagator.d.ts +57 -0
- package/lib/collab/loro/propagators/LineBreakNodePropagator.js +200 -0
- package/lib/collab/loro/propagators/RootNodePropagator.d.ts +55 -0
- package/lib/collab/loro/propagators/RootNodePropagator.js +174 -0
- package/lib/collab/loro/propagators/TextNodePropagator.d.ts +60 -0
- package/lib/collab/loro/propagators/TextNodePropagator.js +450 -0
- package/lib/collab/loro/propagators/index.d.ts +49 -0
- package/lib/collab/loro/propagators/index.js +30 -0
- package/lib/collab/loro/provider/websocket.d.ts +117 -0
- package/lib/collab/loro/provider/websocket.js +946 -0
- package/lib/collab/loro/servers/index.d.ts +0 -0
- package/lib/collab/loro/servers/index.js +4 -0
- package/lib/collab/loro/servers/ws/callback.d.ts +5 -0
- package/lib/collab/loro/servers/ws/callback.js +89 -0
- package/lib/collab/loro/servers/ws/server.d.ts +2 -0
- package/lib/collab/loro/servers/ws/server.js +29 -0
- package/lib/collab/loro/servers/ws/utils.d.ts +40 -0
- package/lib/collab/loro/servers/ws/utils.js +517 -0
- package/lib/collab/loro/sync/SyncCursors.d.ts +32 -0
- package/lib/collab/loro/sync/SyncCursors.js +501 -0
- package/lib/collab/loro/sync/SyncLexicalToLoro.d.ts +4 -0
- package/lib/collab/loro/sync/SyncLexicalToLoro.js +152 -0
- package/lib/collab/loro/sync/SyncLoroToLexical.d.ts +5 -0
- package/lib/collab/loro/sync/SyncLoroToLexical.js +106 -0
- package/lib/collab/loro/types/LexicalNodeData.d.ts +32 -0
- package/lib/collab/loro/types/LexicalNodeData.js +75 -0
- package/lib/collab/loro/useCollaboration.d.ts +12 -0
- package/lib/collab/loro/useCollaboration.js +405 -0
- package/lib/collab/loro/utils/InitialContent.d.ts +64 -0
- package/lib/collab/loro/utils/InitialContent.js +113 -0
- package/lib/collab/loro/utils/Invariant.d.ts +15 -0
- package/lib/collab/loro/utils/Invariant.js +24 -0
- package/lib/collab/loro/utils/LexicalToLoro.d.ts +18 -0
- package/lib/collab/loro/utils/LexicalToLoro.js +100 -0
- package/lib/collab/loro/utils/Utils.d.ts +44 -0
- package/lib/collab/loro/utils/Utils.js +157 -0
- package/lib/collab/loro/wsProvider.d.ts +8 -0
- package/lib/collab/loro/wsProvider.js +52 -0
- package/lib/collab/utils/invariant.d.ts +1 -0
- package/lib/collab/utils/invariant.js +15 -0
- package/lib/collab/utils/simpleDiffWithCursor.d.ts +5 -0
- package/lib/collab/utils/simpleDiffWithCursor.js +35 -0
- package/lib/collab/yjs/Bindings.d.ts +23 -0
- package/lib/collab/yjs/Bindings.js +26 -0
- package/lib/collab/yjs/Debug.d.ts +23 -0
- package/lib/collab/yjs/Debug.js +213 -0
- package/lib/collab/yjs/LexicalCollaborationContext.d.ts +10 -0
- package/lib/collab/yjs/LexicalCollaborationContext.js +37 -0
- package/lib/collab/yjs/LexicalCollaborationPlugin.d.ts +21 -0
- package/lib/collab/yjs/LexicalCollaborationPlugin.js +63 -0
- package/lib/collab/yjs/State.d.ts +51 -0
- package/lib/collab/yjs/State.js +35 -0
- package/lib/collab/yjs/nodes/AnyCollabNode.d.ts +5 -0
- package/lib/collab/yjs/nodes/AnyCollabNode.js +1 -0
- package/lib/collab/yjs/nodes/CollabDecoratorNode.d.ts +22 -0
- package/lib/collab/yjs/nodes/CollabDecoratorNode.js +64 -0
- package/lib/collab/yjs/nodes/CollabElementNode.d.ts +40 -0
- package/lib/collab/yjs/nodes/CollabElementNode.js +462 -0
- package/lib/collab/yjs/nodes/CollabLineBreakNode.d.ts +19 -0
- package/lib/collab/yjs/nodes/CollabLineBreakNode.js +44 -0
- package/lib/collab/yjs/nodes/CollabTextNode.d.ts +25 -0
- package/lib/collab/yjs/nodes/CollabTextNode.js +103 -0
- package/lib/collab/yjs/provider/websocket.d.ts +88 -0
- package/lib/collab/yjs/provider/websocket.js +415 -0
- package/lib/collab/yjs/servers/index.d.ts +0 -0
- package/lib/collab/yjs/servers/index.js +0 -0
- package/lib/collab/yjs/servers/ws/callback.d.ts +5 -0
- package/lib/collab/yjs/servers/ws/callback.js +72 -0
- package/lib/collab/yjs/servers/ws/server.d.ts +2 -0
- package/lib/collab/yjs/servers/ws/server.js +25 -0
- package/lib/collab/yjs/servers/ws/utils.d.ts +49 -0
- package/lib/collab/yjs/servers/ws/utils.js +284 -0
- package/lib/collab/yjs/sync/SyncCursors.d.ts +39 -0
- package/lib/collab/yjs/sync/SyncCursors.js +351 -0
- package/lib/collab/yjs/sync/SyncEditorStates.d.ts +10 -0
- package/lib/collab/yjs/sync/SyncEditorStates.js +200 -0
- package/lib/collab/yjs/useCollaboration.d.ts +12 -0
- package/lib/collab/yjs/useCollaboration.js +255 -0
- package/lib/collab/yjs/utils/Utils.d.ts +25 -0
- package/lib/collab/yjs/utils/Utils.js +402 -0
- package/lib/collab/yjs/wsProvider.d.ts +3 -0
- package/lib/collab/yjs/wsProvider.js +21 -0
- package/lib/commenting/index.d.ts +41 -0
- package/lib/commenting/index.js +328 -0
- package/lib/context/FlashMessageContext.d.ts +7 -0
- package/lib/context/FlashMessageContext.js +24 -0
- package/lib/context/SettingsContext.d.ts +12 -0
- package/lib/context/SettingsContext.js +38 -0
- package/lib/context/SharedHistoryContext.d.ts +11 -0
- package/lib/context/SharedHistoryContext.js +11 -0
- package/lib/context/ToolbarContext.d.ts +65 -0
- package/lib/context/ToolbarContext.js +84 -0
- package/lib/demo.d.ts +12 -0
- package/lib/demo.js +45 -0
- package/lib/hooks/useFlashMessage.d.ts +2 -0
- package/lib/hooks/useFlashMessage.js +8 -0
- package/lib/hooks/useModal.d.ts +5 -0
- package/lib/hooks/useModal.js +26 -0
- package/lib/hooks/useReport.d.ts +1 -0
- package/lib/hooks/useReport.js +50 -0
- package/lib/index.d.ts +1 -0
- package/lib/index.js +5 -0
- package/lib/nodes/AutocompleteNode.d.ts +27 -0
- package/lib/nodes/AutocompleteNode.js +60 -0
- package/lib/nodes/CounterComponent.d.ts +6 -0
- package/lib/nodes/CounterComponent.js +137 -0
- package/lib/nodes/CounterNode.d.ts +23 -0
- package/lib/nodes/CounterNode.js +47 -0
- package/lib/nodes/DateTimeNode/DateTimeComponent.d.ts +8 -0
- package/lib/nodes/DateTimeNode/DateTimeComponent.js +119 -0
- package/lib/nodes/DateTimeNode/DateTimeNode.d.ts +27 -0
- package/lib/nodes/DateTimeNode/DateTimeNode.js +82 -0
- package/lib/nodes/EmojiNode.d.ts +18 -0
- package/lib/nodes/EmojiNode.js +54 -0
- package/lib/nodes/EquationComponent.d.ts +9 -0
- package/lib/nodes/EquationComponent.js +75 -0
- package/lib/nodes/EquationNode.d.ts +26 -0
- package/lib/nodes/EquationNode.js +109 -0
- package/lib/nodes/ExcalidrawNode/ExcalidrawComponent.d.ts +8 -0
- package/lib/nodes/ExcalidrawNode/ExcalidrawComponent.js +110 -0
- package/lib/nodes/ExcalidrawNode/ExcalidrawImage.d.ts +50 -0
- package/lib/nodes/ExcalidrawNode/ExcalidrawImage.js +55 -0
- package/lib/nodes/ExcalidrawNode/index.d.ts +32 -0
- package/lib/nodes/ExcalidrawNode/index.js +117 -0
- package/lib/nodes/FigmaNode.d.ts +20 -0
- package/lib/nodes/FigmaNode.js +52 -0
- package/lib/nodes/ImageComponent.d.ts +16 -0
- package/lib/nodes/ImageComponent.js +272 -0
- package/lib/nodes/ImageNode.d.ts +50 -0
- package/lib/nodes/ImageNode.js +151 -0
- package/lib/nodes/InlineImageNode/InlineImageComponent.d.ts +26 -0
- package/lib/nodes/InlineImageNode/InlineImageComponent.js +161 -0
- package/lib/nodes/InlineImageNode/InlineImageNode.d.ts +59 -0
- package/lib/nodes/InlineImageNode/InlineImageNode.js +162 -0
- package/lib/nodes/KeywordNode.d.ts +14 -0
- package/lib/nodes/KeywordNode.js +37 -0
- package/lib/nodes/LayoutContainerNode.d.ts +24 -0
- package/lib/nodes/LayoutContainerNode.js +95 -0
- package/lib/nodes/LayoutItemNode.d.ts +16 -0
- package/lib/nodes/LayoutItemNode.js +69 -0
- package/lib/nodes/MentionNode.d.ts +20 -0
- package/lib/nodes/MentionNode.js +85 -0
- package/lib/nodes/PageBreakNode/index.d.ts +17 -0
- package/lib/nodes/PageBreakNode/index.js +83 -0
- package/lib/nodes/PlaygroundNodes.d.ts +3 -0
- package/lib/nodes/PlaygroundNodes.js +75 -0
- package/lib/nodes/PollComponent.d.ts +9 -0
- package/lib/nodes/PollComponent.js +85 -0
- package/lib/nodes/PollNode.d.ts +43 -0
- package/lib/nodes/PollNode.js +153 -0
- package/lib/nodes/SpecialTextNode.d.ts +24 -0
- package/lib/nodes/SpecialTextNode.js +54 -0
- package/lib/nodes/StickyComponent.d.ts +10 -0
- package/lib/nodes/StickyComponent.js +162 -0
- package/lib/nodes/StickyNode.d.ts +31 -0
- package/lib/nodes/StickyNode.js +76 -0
- package/lib/nodes/TweetNode.d.ts +21 -0
- package/lib/nodes/TweetNode.js +119 -0
- package/lib/nodes/YouTubeNode.d.ts +22 -0
- package/lib/nodes/YouTubeNode.js +84 -0
- package/lib/plugins/ActionsPlugin/index.d.ts +5 -0
- package/lib/plugins/ActionsPlugin/index.js +168 -0
- package/lib/plugins/AutoEmbedPlugin/index.d.ts +19 -0
- package/lib/plugins/AutoEmbedPlugin/index.js +158 -0
- package/lib/plugins/AutoLinkPlugin/index.d.ts +2 -0
- package/lib/plugins/AutoLinkPlugin/index.js +15 -0
- package/lib/plugins/AutocompletePlugin/index.d.ts +10 -0
- package/lib/plugins/AutocompletePlugin/index.js +2477 -0
- package/lib/plugins/CodeActionMenuPlugin/components/CopyButton/index.d.ts +7 -0
- package/lib/plugins/CodeActionMenuPlugin/components/CopyButton/index.js +46 -0
- package/lib/plugins/CodeActionMenuPlugin/components/PrettierButton/index.d.ts +17 -0
- package/lib/plugins/CodeActionMenuPlugin/components/PrettierButton/index.js +115 -0
- package/lib/plugins/CodeActionMenuPlugin/index.d.ts +5 -0
- package/lib/plugins/CodeActionMenuPlugin/index.js +104 -0
- package/lib/plugins/CodeActionMenuPlugin/utils.d.ts +1 -0
- package/lib/plugins/CodeActionMenuPlugin/utils.js +22 -0
- package/lib/plugins/CodeHighlightPrismPlugin/index.d.ts +2 -0
- package/lib/plugins/CodeHighlightPrismPlugin/index.js +14 -0
- package/lib/plugins/CodeHighlightShikiPlugin/index.d.ts +2 -0
- package/lib/plugins/CodeHighlightShikiPlugin/index.js +14 -0
- package/lib/plugins/CollapsiblePlugin/CollapsibleContainerNode.d.ts +25 -0
- package/lib/plugins/CollapsiblePlugin/CollapsibleContainerNode.js +135 -0
- package/lib/plugins/CollapsiblePlugin/CollapsibleContentNode.d.ts +16 -0
- package/lib/plugins/CollapsiblePlugin/CollapsibleContentNode.js +83 -0
- package/lib/plugins/CollapsiblePlugin/CollapsibleTitleNode.d.ts +16 -0
- package/lib/plugins/CollapsiblePlugin/CollapsibleTitleNode.js +85 -0
- package/lib/plugins/CollapsiblePlugin/CollapsibleUtils.d.ts +2 -0
- package/lib/plugins/CollapsiblePlugin/CollapsibleUtils.js +12 -0
- package/lib/plugins/CollapsiblePlugin/index.d.ts +3 -0
- package/lib/plugins/CollapsiblePlugin/index.js +132 -0
- package/lib/plugins/CommentPlugin/index.d.ts +9 -0
- package/lib/plugins/CommentPlugin/index.js +460 -0
- package/lib/plugins/ComponentPickerPlugin/index.d.ts +2 -0
- package/lib/plugins/ComponentPickerPlugin/index.js +276 -0
- package/lib/plugins/ContextMenuPlugin/index.d.ts +2 -0
- package/lib/plugins/ContextMenuPlugin/index.js +112 -0
- package/lib/plugins/CounterPlugin/index.d.ts +3 -0
- package/lib/plugins/CounterPlugin/index.js +24 -0
- package/lib/plugins/DateTimePlugin/index.d.ts +8 -0
- package/lib/plugins/DateTimePlugin/index.js +28 -0
- package/lib/plugins/DebugPlugin/index.d.ts +3 -0
- package/lib/plugins/DebugPlugin/index.js +275 -0
- package/lib/plugins/DocsPlugin/index.d.ts +2 -0
- package/lib/plugins/DocsPlugin/index.js +4 -0
- package/lib/plugins/DragDropPastePlugin/index.d.ts +1 -0
- package/lib/plugins/DragDropPastePlugin/index.js +37 -0
- package/lib/plugins/DraggableBlockPlugin/index.d.ts +12 -0
- package/lib/plugins/DraggableBlockPlugin/index.js +36 -0
- package/lib/plugins/EmojiPickerPlugin/index.d.ts +1 -0
- package/lib/plugins/EmojiPickerPlugin/index.js +84 -0
- package/lib/plugins/EmojisPlugin/index.d.ts +2 -0
- package/lib/plugins/EmojisPlugin/index.js +56 -0
- package/lib/plugins/EquationsPlugin/index.d.ts +14 -0
- package/lib/plugins/EquationsPlugin/index.js +34 -0
- package/lib/plugins/ExcalidrawPlugin/index.d.ts +5 -0
- package/lib/plugins/ExcalidrawPlugin/index.js +44 -0
- package/lib/plugins/FigmaPlugin/index.d.ts +4 -0
- package/lib/plugins/FigmaPlugin/index.js +24 -0
- package/lib/plugins/FloatingLinkEditorPlugin/index.d.ts +15 -0
- package/lib/plugins/FloatingLinkEditorPlugin/index.js +280 -0
- package/lib/plugins/FloatingTextFormatToolbarPlugin/index.d.ts +7 -0
- package/lib/plugins/FloatingTextFormatToolbarPlugin/index.js +219 -0
- package/lib/plugins/ImagesPlugin/index.d.ts +24 -0
- package/lib/plugins/ImagesPlugin/index.js +195 -0
- package/lib/plugins/InlineImagePlugin/index.d.ts +17 -0
- package/lib/plugins/InlineImagePlugin/index.js +180 -0
- package/lib/plugins/KeywordsPlugin/index.d.ts +2 -0
- package/lib/plugins/KeywordsPlugin/index.js +35 -0
- package/lib/plugins/LayoutPlugin/InsertLayoutDialog.d.ts +6 -0
- package/lib/plugins/LayoutPlugin/InsertLayoutDialog.js +21 -0
- package/lib/plugins/LayoutPlugin/LayoutPlugin.d.ts +7 -0
- package/lib/plugins/LayoutPlugin/LayoutPlugin.js +135 -0
- package/lib/plugins/LinkPlugin/index.d.ts +6 -0
- package/lib/plugins/LinkPlugin/index.js +11 -0
- package/lib/plugins/MarkdownShortcutPlugin/index.d.ts +2 -0
- package/lib/plugins/MarkdownShortcutPlugin/index.js +6 -0
- package/lib/plugins/MarkdownTransformers/index.d.ts +8 -0
- package/lib/plugins/MarkdownTransformers/index.js +238 -0
- package/lib/plugins/MaxLengthPlugin/index.d.ts +3 -0
- package/lib/plugins/MaxLengthPlugin/index.js +41 -0
- package/lib/plugins/MentionsPlugin/index.d.ts +2 -0
- package/lib/plugins/MentionsPlugin/index.js +564 -0
- package/lib/plugins/PageBreakPlugin/index.d.ts +4 -0
- package/lib/plugins/PageBreakPlugin/index.js +31 -0
- package/lib/plugins/PasteLogPlugin/index.d.ts +2 -0
- package/lib/plugins/PasteLogPlugin/index.js +27 -0
- package/lib/plugins/PollPlugin/index.d.ts +8 -0
- package/lib/plugins/PollPlugin/index.js +38 -0
- package/lib/plugins/ShortcutsPlugin/index.d.ts +6 -0
- package/lib/plugins/ShortcutsPlugin/index.js +116 -0
- package/lib/plugins/ShortcutsPlugin/shortcuts.d.ts +59 -0
- package/lib/plugins/ShortcutsPlugin/shortcuts.js +173 -0
- package/lib/plugins/SpecialTextPlugin/index.d.ts +2 -0
- package/lib/plugins/SpecialTextPlugin/index.js +50 -0
- package/lib/plugins/SpeechToTextPlugin/index.d.ts +5 -0
- package/lib/plugins/SpeechToTextPlugin/index.js +86 -0
- package/lib/plugins/StickyPlugin/index.d.ts +2 -0
- package/lib/plugins/StickyPlugin/index.js +16 -0
- package/lib/plugins/TabFocusPlugin/index.d.ts +1 -0
- package/lib/plugins/TabFocusPlugin/index.js +38 -0
- package/lib/plugins/TableActionMenuPlugin/index.d.ts +5 -0
- package/lib/plugins/TableActionMenuPlugin/index.js +492 -0
- package/lib/plugins/TableCellResizer/index.d.ts +3 -0
- package/lib/plugins/TableCellResizer/index.js +297 -0
- package/lib/plugins/TableHoverActionsPlugin/index.d.ts +4 -0
- package/lib/plugins/TableHoverActionsPlugin/index.js +188 -0
- package/lib/plugins/TableOfContentsPlugin/index.d.ts +2 -0
- package/lib/plugins/TableOfContentsPlugin/index.js +116 -0
- package/lib/plugins/TablePlugin.d.ts +31 -0
- package/lib/plugins/TablePlugin.js +63 -0
- package/lib/plugins/TestRecorderPlugin/index.d.ts +3 -0
- package/lib/plugins/TestRecorderPlugin/index.js +346 -0
- package/lib/plugins/ToolbarPlugin/fontSize.d.ts +9 -0
- package/lib/plugins/ToolbarPlugin/fontSize.js +84 -0
- package/lib/plugins/ToolbarPlugin/index.d.ts +9 -0
- package/lib/plugins/ToolbarPlugin/index.js +500 -0
- package/lib/plugins/ToolbarPlugin/utils.d.ts +26 -0
- package/lib/plugins/ToolbarPlugin/utils.js +247 -0
- package/lib/plugins/TreeViewPlugin/index.d.ts +2 -0
- package/lib/plugins/TreeViewPlugin/index.js +7 -0
- package/lib/plugins/TwitterPlugin/index.d.ts +4 -0
- package/lib/plugins/TwitterPlugin/index.js +24 -0
- package/lib/plugins/TypingPerfPlugin/index.d.ts +2 -0
- package/lib/plugins/TypingPerfPlugin/index.js +97 -0
- package/lib/plugins/YouTubePlugin/index.d.ts +4 -0
- package/lib/plugins/YouTubePlugin/index.js +24 -0
- package/lib/server/validation.d.ts +1 -0
- package/lib/server/validation.js +115 -0
- package/lib/setupEnv.d.ts +2 -0
- package/lib/setupEnv.js +29 -0
- package/lib/themes/CommentEditorTheme.d.ts +4 -0
- package/lib/themes/CommentEditorTheme.js +11 -0
- package/lib/themes/PlaygroundEditorTheme.d.ts +4 -0
- package/lib/themes/PlaygroundEditorTheme.js +124 -0
- package/lib/themes/StickyEditorTheme.d.ts +4 -0
- package/lib/themes/StickyEditorTheme.js +11 -0
- package/lib/tyes.dt.d.ts +12 -0
- package/lib/tyes.dt.js +4 -0
- package/lib/ui/Button.d.ts +12 -0
- package/lib/ui/Button.js +6 -0
- package/lib/ui/ColorPicker.d.ts +14 -0
- package/lib/ui/ColorPicker.js +219 -0
- package/lib/ui/ContentEditable.d.ts +9 -0
- package/lib/ui/ContentEditable.js +6 -0
- package/lib/ui/Dialog.d.ts +10 -0
- package/lib/ui/Dialog.js +8 -0
- package/lib/ui/DropDown.d.ts +18 -0
- package/lib/ui/DropDown.js +133 -0
- package/lib/ui/DropdownColorPicker.d.ts +13 -0
- package/lib/ui/DropdownColorPicker.js +6 -0
- package/lib/ui/EquationEditor.d.ts +8 -0
- package/lib/ui/EquationEditor.js +11 -0
- package/lib/ui/ExcalidrawModal.d.ts +42 -0
- package/lib/ui/ExcalidrawModal.js +103 -0
- package/lib/ui/FileInput.d.ts +10 -0
- package/lib/ui/FileInput.js +5 -0
- package/lib/ui/FlashMessage.d.ts +7 -0
- package/lib/ui/FlashMessage.js +6 -0
- package/lib/ui/ImageResizer.d.ts +17 -0
- package/lib/ui/ImageResizer.js +171 -0
- package/lib/ui/KatexEquationAlterer.d.ts +8 -0
- package/lib/ui/KatexEquationAlterer.js +23 -0
- package/lib/ui/KatexRenderer.d.ts +6 -0
- package/lib/ui/KatexRenderer.js +24 -0
- package/lib/ui/Modal.d.ts +9 -0
- package/lib/ui/Modal.js +48 -0
- package/lib/ui/Select.d.ts +8 -0
- package/lib/ui/Select.js +5 -0
- package/lib/ui/Switch.d.ts +8 -0
- package/lib/ui/Switch.js +6 -0
- package/lib/ui/TextInput.d.ts +13 -0
- package/lib/ui/TextInput.js +7 -0
- package/lib/utils/docSerialization.d.ts +3 -0
- package/lib/utils/docSerialization.js +60 -0
- package/lib/utils/emoji-list.d.ts +20 -0
- package/lib/utils/emoji-list.js +16609 -0
- package/lib/utils/getDOMRangeRect.d.ts +8 -0
- package/lib/utils/getDOMRangeRect.js +26 -0
- package/lib/utils/getSelectedNode.d.ts +2 -0
- package/lib/utils/getSelectedNode.js +28 -0
- package/lib/utils/getThemeSelector.d.ts +2 -0
- package/lib/utils/getThemeSelector.js +14 -0
- package/lib/utils/isMobileWidth.d.ts +7 -0
- package/lib/utils/isMobileWidth.js +11 -0
- package/lib/utils/joinClasses.d.ts +1 -0
- package/lib/utils/joinClasses.js +7 -0
- package/lib/utils/setFloatingElemPosition.d.ts +1 -0
- package/lib/utils/setFloatingElemPosition.js +59 -0
- package/lib/utils/setFloatingElemPositionForLinkEditor.d.ts +1 -0
- package/lib/utils/setFloatingElemPositionForLinkEditor.js +36 -0
- package/lib/utils/swipe.d.ts +4 -0
- package/lib/utils/swipe.js +94 -0
- package/lib/utils/url.d.ts +2 -0
- package/lib/utils/url.js +31 -0
- package/package.json +4 -4
|
@@ -0,0 +1,450 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (c) 2025-2026 Datalayer, Inc.
|
|
3
|
+
* Distributed under the terms of the MIT License.
|
|
4
|
+
*/
|
|
5
|
+
import { $createTextNode, $isTextNode } from 'lexical';
|
|
6
|
+
import { getNodeMapper } from '../nodes/NodesMapper';
|
|
7
|
+
import { $diffTextContentAndApplyDelta } from '../utils/Utils';
|
|
8
|
+
import { invariant } from '../utils/Invariant';
|
|
9
|
+
/**
|
|
10
|
+
* Create TextNode in Loro tree
|
|
11
|
+
*/
|
|
12
|
+
export function createTextNodeInLoro(nodeKey, textContent, format, mode, parentId, index, lexicalNodeJSON, // JSON object from exportJSON()
|
|
13
|
+
options) {
|
|
14
|
+
const mapper = getNodeMapper();
|
|
15
|
+
// A text node is always a leaf with a parent element. A missing parent means
|
|
16
|
+
// the parent element was not synced first — surface it instead of creating an
|
|
17
|
+
// orphaned (unrenderable) node.
|
|
18
|
+
invariant(parentId !== undefined, 'createTextNodeInLoro: text node has no parent in Loro tree', { nodeKey });
|
|
19
|
+
// Use mapper to get or create the tree node
|
|
20
|
+
// Note: We can't pass lexicalNode directly due to context issues, but parentId should be sufficient
|
|
21
|
+
const treeNode = mapper.getLoroNodeByLexicalKey(nodeKey, undefined, // don't pass lexicalNode to avoid context issues
|
|
22
|
+
parentId, index);
|
|
23
|
+
// Store complete lexical node data as clean JSON if provided
|
|
24
|
+
if (lexicalNodeJSON) {
|
|
25
|
+
try {
|
|
26
|
+
// Store complete lexical JSON without the key
|
|
27
|
+
if ('key' in lexicalNodeJSON || '__key' in lexicalNodeJSON || 'lexicalKey' in lexicalNodeJSON) {
|
|
28
|
+
const { key, __key, lexicalKey, children, ...cleanedData } = lexicalNodeJSON;
|
|
29
|
+
treeNode.data.set('lexical', cleanedData);
|
|
30
|
+
}
|
|
31
|
+
else {
|
|
32
|
+
const { children, ...cleanedData } = lexicalNodeJSON;
|
|
33
|
+
treeNode.data.set('lexical', cleanedData);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
catch (error) {
|
|
37
|
+
console.warn('Failed to store lexical node JSON for TextNode:', error);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
// Store only essential metadata (elementType for debug panel)
|
|
41
|
+
treeNode.data.set('elementType', 'text');
|
|
42
|
+
treeNode.data.set('createdAt', Date.now());
|
|
43
|
+
// Return the TreeID from the node's ID
|
|
44
|
+
return treeNode.id;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Update TextNode in Loro tree
|
|
48
|
+
*/
|
|
49
|
+
export function updateTextNodeInLoro(nodeKey, newTextContent, format, mode, lexicalNodeJSON, // JSON object from exportJSON()
|
|
50
|
+
options) {
|
|
51
|
+
const mapper = getNodeMapper();
|
|
52
|
+
// Updates must target an existing mapped node. Creating on update can
|
|
53
|
+
// accidentally create inline nodes at root when parent info is unavailable.
|
|
54
|
+
const treeID = mapper.getTreeIDByLexicalKey(nodeKey);
|
|
55
|
+
if (!treeID || !options?.tree.has(treeID)) {
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
const treeNode = options.tree.getNodeByID(treeID);
|
|
59
|
+
if (!treeNode) {
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
// Store complete lexical node data as clean JSON if provided.
|
|
63
|
+
if (lexicalNodeJSON) {
|
|
64
|
+
if ('key' in lexicalNodeJSON || '__key' in lexicalNodeJSON || 'lexicalKey' in lexicalNodeJSON) {
|
|
65
|
+
const { key, __key, lexicalKey, children, ...cleanedData } = lexicalNodeJSON;
|
|
66
|
+
treeNode.data.set('lexical', cleanedData);
|
|
67
|
+
}
|
|
68
|
+
else {
|
|
69
|
+
const { children, ...cleanedData } = lexicalNodeJSON;
|
|
70
|
+
treeNode.data.set('lexical', cleanedData);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
// Update only essential metadata.
|
|
74
|
+
treeNode.data.set('elementType', 'text');
|
|
75
|
+
treeNode.data.set('updatedAt', Date.now());
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Delete TextNode from Loro tree
|
|
79
|
+
*/
|
|
80
|
+
export function deleteTextNodeInLoro(nodeKey, options) {
|
|
81
|
+
const mapper = getNodeMapper();
|
|
82
|
+
mapper.deleteMapping(nodeKey);
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Create TextNode in Lexical from Loro tree data
|
|
86
|
+
*/
|
|
87
|
+
export function createTextNodeFromLoro(treeId, parentNode, index, options) {
|
|
88
|
+
const { tree } = options;
|
|
89
|
+
if (!tree.has(treeId)) {
|
|
90
|
+
return null;
|
|
91
|
+
}
|
|
92
|
+
const treeNode = tree.getNodeByID(treeId);
|
|
93
|
+
if (!treeNode) {
|
|
94
|
+
return null;
|
|
95
|
+
}
|
|
96
|
+
// Get LexicalNodeData (JSON object format only)
|
|
97
|
+
const lexicalData = treeNode.data.get('lexical');
|
|
98
|
+
let textNode;
|
|
99
|
+
if (lexicalData && typeof lexicalData === 'object') {
|
|
100
|
+
try {
|
|
101
|
+
// lexicalData is a direct JSON object, use it to create a TextNode
|
|
102
|
+
const lexicalDataObj = lexicalData;
|
|
103
|
+
const textContent = lexicalDataObj.text || lexicalDataObj.__text || '';
|
|
104
|
+
const format = lexicalDataObj.format || lexicalDataObj.__format || 0;
|
|
105
|
+
const mode = lexicalDataObj.mode || lexicalDataObj.__mode || 0;
|
|
106
|
+
textNode = $createTextNode(textContent);
|
|
107
|
+
textNode.setFormat(format);
|
|
108
|
+
textNode.setMode(mode);
|
|
109
|
+
}
|
|
110
|
+
catch (error) {
|
|
111
|
+
console.warn('Failed to create TextNode from JSON data for TreeID:', treeId, error);
|
|
112
|
+
return null;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
else {
|
|
116
|
+
// No lexical JSON data found - cannot create TextNode
|
|
117
|
+
console.warn('No lexical JSON data found for TextNode TreeID:', treeId);
|
|
118
|
+
return null;
|
|
119
|
+
}
|
|
120
|
+
// Apply mode if present (only for old format)
|
|
121
|
+
if (!lexicalData || typeof lexicalData !== 'string') {
|
|
122
|
+
// const mode = treeNode.data.get('mode');
|
|
123
|
+
// if (mode && typeof textNode.setMode === 'function') {
|
|
124
|
+
// textNode.setMode(mode);
|
|
125
|
+
// }
|
|
126
|
+
}
|
|
127
|
+
// Insert into the parent at the specified index
|
|
128
|
+
if (index !== undefined && index >= 0) {
|
|
129
|
+
parentNode.splice(index, 0, [textNode]);
|
|
130
|
+
}
|
|
131
|
+
else {
|
|
132
|
+
parentNode.append(textNode);
|
|
133
|
+
}
|
|
134
|
+
return textNode;
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Update TextNode in Lexical from Loro tree data
|
|
138
|
+
*/
|
|
139
|
+
export function updateTextNodeFromLoro(treeId, textNode, newParentNode, newIndex, options) {
|
|
140
|
+
const { tree } = options;
|
|
141
|
+
if (!tree.has(treeId)) {
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
144
|
+
const treeNode = tree.getNodeByID(treeId);
|
|
145
|
+
if (!treeNode || treeNode.data.get('nodeType') !== 'text') {
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
// Update text content if it has changed
|
|
149
|
+
const textContent = treeNode.data.get('textContent');
|
|
150
|
+
if (textContent !== undefined && typeof textContent === 'string' &&
|
|
151
|
+
textNode.getTextContent() !== textContent) {
|
|
152
|
+
const currentText = textNode.getTextContent();
|
|
153
|
+
$diffTextContentAndApplyDelta(textNode, textNode.getKey(), currentText, textContent);
|
|
154
|
+
}
|
|
155
|
+
// Update format if it has changed
|
|
156
|
+
const format = treeNode.data.get('format');
|
|
157
|
+
if (format !== undefined && typeof format === 'number') {
|
|
158
|
+
textNode.setFormat(format);
|
|
159
|
+
}
|
|
160
|
+
// Update mode if it has changed (skip for now due to type complexity)
|
|
161
|
+
// const mode = treeNode.data.get('mode');
|
|
162
|
+
// if (mode !== undefined && typeof lexicalNode.setMode === 'function') {
|
|
163
|
+
// lexicalNode.setMode(mode);
|
|
164
|
+
// }
|
|
165
|
+
// If parent or position changed, move the node
|
|
166
|
+
if (newParentNode && newIndex !== undefined) {
|
|
167
|
+
// Remove from current location
|
|
168
|
+
textNode.remove();
|
|
169
|
+
// Insert at new location
|
|
170
|
+
newParentNode.splice(newIndex, 0, [textNode]);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* Delete TextNode from Lexical
|
|
175
|
+
*/
|
|
176
|
+
export function deleteTextNodeFromLoro(treeId, lexicalNode, options) {
|
|
177
|
+
if ($isTextNode(lexicalNode)) {
|
|
178
|
+
lexicalNode.remove();
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* Utility to check if a tree node represents a TextNode
|
|
183
|
+
*/
|
|
184
|
+
export function isTextNodeInTree(treeId, tree) {
|
|
185
|
+
if (!tree.has(treeId)) {
|
|
186
|
+
return false;
|
|
187
|
+
}
|
|
188
|
+
const treeNode = tree.getNodeByID(treeId);
|
|
189
|
+
return treeNode?.data.get('nodeType') === 'text';
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* Get TextNode data from Loro tree
|
|
193
|
+
*/
|
|
194
|
+
export function getTextNodeDataFromTree(treeId, tree) {
|
|
195
|
+
if (!tree.has(treeId)) {
|
|
196
|
+
return null;
|
|
197
|
+
}
|
|
198
|
+
const treeNode = tree.getNodeByID(treeId);
|
|
199
|
+
if (!treeNode || treeNode.data.get('nodeType') !== 'text') {
|
|
200
|
+
return null;
|
|
201
|
+
}
|
|
202
|
+
return {
|
|
203
|
+
nodeType: 'text',
|
|
204
|
+
textContent: treeNode.data.get('textContent'),
|
|
205
|
+
format: treeNode.data.get('format'),
|
|
206
|
+
mode: treeNode.data.get('mode'),
|
|
207
|
+
createdAt: treeNode.data.get('createdAt'),
|
|
208
|
+
lastUpdated: treeNode.data.get('lastUpdated'),
|
|
209
|
+
};
|
|
210
|
+
}
|
|
211
|
+
/**
|
|
212
|
+
* Apply text formatting operations (bold, italic, etc.)
|
|
213
|
+
*/
|
|
214
|
+
export function applyTextFormatInLoro(nodeKey, formatType, apply, options) {
|
|
215
|
+
const mapper = getNodeMapper();
|
|
216
|
+
// Get the existing Loro node from the mapper
|
|
217
|
+
const treeNode = mapper.getLoroNodeByLexicalKey(nodeKey);
|
|
218
|
+
if (!treeNode) {
|
|
219
|
+
return;
|
|
220
|
+
}
|
|
221
|
+
if (!treeNode || treeNode.data.get('nodeType') !== 'text') {
|
|
222
|
+
return;
|
|
223
|
+
}
|
|
224
|
+
// Check if the TreeNode's container is still valid
|
|
225
|
+
try {
|
|
226
|
+
// Try a simple read operation to verify the container is accessible
|
|
227
|
+
treeNode.data.get('format');
|
|
228
|
+
}
|
|
229
|
+
catch (error) {
|
|
230
|
+
console.warn(` TextNode ${nodeKey} container deleted during format check (normal during text operations):`, error.message);
|
|
231
|
+
return;
|
|
232
|
+
}
|
|
233
|
+
let currentFormat = treeNode.data.get('format') || 0;
|
|
234
|
+
const formatNumber = formatType;
|
|
235
|
+
if (apply) {
|
|
236
|
+
// Add the format flag
|
|
237
|
+
currentFormat = currentFormat | formatNumber;
|
|
238
|
+
}
|
|
239
|
+
else {
|
|
240
|
+
// Remove the format flag
|
|
241
|
+
currentFormat = currentFormat & ~formatNumber;
|
|
242
|
+
}
|
|
243
|
+
try {
|
|
244
|
+
treeNode.data.set('format', currentFormat);
|
|
245
|
+
treeNode.data.set('lastUpdated', Date.now());
|
|
246
|
+
}
|
|
247
|
+
catch (error) {
|
|
248
|
+
console.warn(` TextNode ${nodeKey} container deleted during format update (normal during text operations):`, error.message);
|
|
249
|
+
return;
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
/**
|
|
253
|
+
* Main propagate method for TextNode - propagates all mutation types
|
|
254
|
+
*/
|
|
255
|
+
export function propagateTextNode(update, mutation, nodeKey, options) {
|
|
256
|
+
const { tree, peerId } = options;
|
|
257
|
+
switch (mutation) {
|
|
258
|
+
case 'created': {
|
|
259
|
+
const currentNode = update.editorState._nodeMap.get(nodeKey);
|
|
260
|
+
// Check if this node already exists in Loro
|
|
261
|
+
const mapper = getNodeMapper();
|
|
262
|
+
const existingTreeID = mapper.getTreeIDByLexicalKey(nodeKey);
|
|
263
|
+
if (existingTreeID && currentNode && $isTextNode(currentNode)) {
|
|
264
|
+
// Check if the existing text node has the correct parent relationship
|
|
265
|
+
const { currentParentId, expectedParentId } = update.editorState.read(() => {
|
|
266
|
+
const parent = currentNode.getParent();
|
|
267
|
+
const expectedParentId = parent ? mapper.getTreeIDByLexicalKey(parent.getKey()) : undefined;
|
|
268
|
+
// Get the current parent from Loro tree
|
|
269
|
+
let currentParentId;
|
|
270
|
+
try {
|
|
271
|
+
const treeNode = tree.getNodeByID(existingTreeID);
|
|
272
|
+
if (treeNode) {
|
|
273
|
+
const parentNode = treeNode.parent();
|
|
274
|
+
currentParentId = parentNode ? parentNode.id.toString() : undefined;
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
catch (error) {
|
|
278
|
+
console.warn(` Failed to get parent for existing TreeID ${existingTreeID}:`, error);
|
|
279
|
+
}
|
|
280
|
+
return { currentParentId, expectedParentId };
|
|
281
|
+
});
|
|
282
|
+
if (currentParentId === expectedParentId) {
|
|
283
|
+
return; // Skip creation since the parent relationship is correct
|
|
284
|
+
}
|
|
285
|
+
else {
|
|
286
|
+
// Get the actual TreeNode and its ID before deleting
|
|
287
|
+
let actualTreeID = existingTreeID;
|
|
288
|
+
try {
|
|
289
|
+
const treeNode = tree.getNodeByID(existingTreeID);
|
|
290
|
+
if (treeNode) {
|
|
291
|
+
actualTreeID = treeNode.id;
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
catch (error) {
|
|
295
|
+
console.warn(` Could not get TreeNode for ${existingTreeID} during creation:`, error);
|
|
296
|
+
}
|
|
297
|
+
// Clear the old mapping first to avoid conflicts
|
|
298
|
+
mapper.deleteMapping(nodeKey);
|
|
299
|
+
// Then delete the TreeNode from Loro tree
|
|
300
|
+
try {
|
|
301
|
+
if (tree.has(actualTreeID)) {
|
|
302
|
+
tree.delete(actualTreeID);
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
catch (error) {
|
|
306
|
+
console.warn(` Failed to delete TreeNode ${actualTreeID} during creation:`, error);
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
if (currentNode && $isTextNode(currentNode)) {
|
|
311
|
+
// Get parent, positioning, content, and serialized data using editor state context
|
|
312
|
+
const { parent, parentId, index, textContent, format, mode, lexicalNodeJSON } = update.editorState.read(() => {
|
|
313
|
+
const parent = currentNode.getParent();
|
|
314
|
+
// Get parentId from the mapper instead of constructing it manually
|
|
315
|
+
const mapper = getNodeMapper();
|
|
316
|
+
const parentId = parent ? mapper.getTreeIDByLexicalKey(parent.getKey()) : undefined;
|
|
317
|
+
const index = currentNode.getIndexWithinParent();
|
|
318
|
+
const textContent = currentNode.getTextContent();
|
|
319
|
+
const format = currentNode.getFormat();
|
|
320
|
+
const mode = currentNode.getMode();
|
|
321
|
+
// Export node data as JSON object within editor context where node methods are available
|
|
322
|
+
let lexicalNodeJSON = undefined;
|
|
323
|
+
try {
|
|
324
|
+
lexicalNodeJSON = currentNode.exportJSON();
|
|
325
|
+
}
|
|
326
|
+
catch (error) {
|
|
327
|
+
console.warn('Failed to export node JSON in propagateTextNode created:', error);
|
|
328
|
+
}
|
|
329
|
+
return { parent, parentId, index, textContent, format, mode, lexicalNodeJSON };
|
|
330
|
+
});
|
|
331
|
+
createTextNodeInLoro(nodeKey, textContent, format, mode, parentId, index, lexicalNodeJSON, options);
|
|
332
|
+
}
|
|
333
|
+
break;
|
|
334
|
+
}
|
|
335
|
+
case 'updated': {
|
|
336
|
+
const currentNode = update.editorState._nodeMap.get(nodeKey);
|
|
337
|
+
if (currentNode && $isTextNode(currentNode)) {
|
|
338
|
+
// First, check if this TextNode's parent has changed (like during double Enter)
|
|
339
|
+
const mapper = getNodeMapper();
|
|
340
|
+
const existingTreeID = mapper.getTreeIDByLexicalKey(nodeKey);
|
|
341
|
+
if (existingTreeID) {
|
|
342
|
+
const { currentParentId, expectedParentId, parent } = update.editorState.read(() => {
|
|
343
|
+
const parent = currentNode.getParent();
|
|
344
|
+
const expectedParentId = parent ? mapper.getTreeIDByLexicalKey(parent.getKey()) : undefined;
|
|
345
|
+
// Get the current parent from Loro tree
|
|
346
|
+
let currentParentId;
|
|
347
|
+
try {
|
|
348
|
+
const treeNode = tree.getNodeByID(existingTreeID);
|
|
349
|
+
if (treeNode) {
|
|
350
|
+
const parentNode = treeNode.parent();
|
|
351
|
+
currentParentId = parentNode ? parentNode.id.toString() : undefined;
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
catch (error) {
|
|
355
|
+
console.warn(` Failed to get parent for existing TreeID ${existingTreeID} during update:`, error);
|
|
356
|
+
}
|
|
357
|
+
return { currentParentId, expectedParentId, parent };
|
|
358
|
+
});
|
|
359
|
+
if (currentParentId !== expectedParentId) {
|
|
360
|
+
// Get the actual TreeNode and its ID before deleting
|
|
361
|
+
let actualTreeID = existingTreeID;
|
|
362
|
+
try {
|
|
363
|
+
const treeNode = tree.getNodeByID(existingTreeID);
|
|
364
|
+
if (treeNode) {
|
|
365
|
+
actualTreeID = treeNode.id;
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
catch (error) {
|
|
369
|
+
console.warn(` Could not get TreeNode for ${existingTreeID}:`, error);
|
|
370
|
+
}
|
|
371
|
+
// Clear the old mapping first to avoid conflicts
|
|
372
|
+
mapper.deleteMapping(nodeKey);
|
|
373
|
+
// Then delete the TreeNode from Loro tree
|
|
374
|
+
try {
|
|
375
|
+
if (tree.has(actualTreeID)) {
|
|
376
|
+
tree.delete(actualTreeID);
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
catch (error) {
|
|
380
|
+
console.warn(` Failed to delete TreeNode ${actualTreeID} during update:`, error);
|
|
381
|
+
}
|
|
382
|
+
// Now recreate the TextNode with the new parent (treat as "created")
|
|
383
|
+
const { parentId, index, textContent, format, mode, lexicalNodeJSON } = update.editorState.read(() => {
|
|
384
|
+
const parentId = parent ? mapper.getTreeIDByLexicalKey(parent.getKey()) : undefined;
|
|
385
|
+
const index = currentNode.getIndexWithinParent();
|
|
386
|
+
const textContent = currentNode.getTextContent();
|
|
387
|
+
const format = currentNode.getFormat();
|
|
388
|
+
const mode = currentNode.getMode();
|
|
389
|
+
let lexicalNodeJSON = undefined;
|
|
390
|
+
try {
|
|
391
|
+
lexicalNodeJSON = currentNode.exportJSON();
|
|
392
|
+
}
|
|
393
|
+
catch (error) {
|
|
394
|
+
console.warn('Failed to export node JSON during TextNode recreation:', error);
|
|
395
|
+
}
|
|
396
|
+
return { parentId, index, textContent, format, mode, lexicalNodeJSON };
|
|
397
|
+
});
|
|
398
|
+
createTextNodeInLoro(nodeKey, textContent, format, mode, parentId, index, lexicalNodeJSON, options);
|
|
399
|
+
return; // Exit early since we recreated the node
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
// If a text node emits only an "updated" mutation before we have a
|
|
403
|
+
// mapping, treat it as a create so parent/index are explicit.
|
|
404
|
+
if (!existingTreeID) {
|
|
405
|
+
const { parent, parentId, index, textContent, format, mode, lexicalNodeJSON } = update.editorState.read(() => {
|
|
406
|
+
const parent = currentNode.getParent();
|
|
407
|
+
const parentId = parent ? mapper.getTreeIDByLexicalKey(parent.getKey()) : undefined;
|
|
408
|
+
const index = currentNode.getIndexWithinParent();
|
|
409
|
+
const textContent = currentNode.getTextContent();
|
|
410
|
+
const format = currentNode.getFormat();
|
|
411
|
+
const mode = currentNode.getMode();
|
|
412
|
+
let lexicalNodeJSON = undefined;
|
|
413
|
+
try {
|
|
414
|
+
lexicalNodeJSON = currentNode.exportJSON();
|
|
415
|
+
}
|
|
416
|
+
catch (error) {
|
|
417
|
+
console.warn('Failed to export node JSON in propagateTextNode updated(create fallback):', error);
|
|
418
|
+
}
|
|
419
|
+
return { parent, parentId, index, textContent, format, mode, lexicalNodeJSON };
|
|
420
|
+
});
|
|
421
|
+
if (parentId !== undefined) {
|
|
422
|
+
createTextNodeInLoro(nodeKey, textContent, format, mode, parentId, index, lexicalNodeJSON, options);
|
|
423
|
+
}
|
|
424
|
+
return;
|
|
425
|
+
}
|
|
426
|
+
// Normal update case - parent hasn't changed
|
|
427
|
+
const { textContent, format, mode, lexicalNodeJSON } = update.editorState.read(() => {
|
|
428
|
+
const textContent = currentNode.getTextContent();
|
|
429
|
+
const format = currentNode.getFormat();
|
|
430
|
+
const mode = currentNode.getMode();
|
|
431
|
+
// Export node data as JSON object within editor context where node methods are available
|
|
432
|
+
let lexicalNodeJSON = undefined;
|
|
433
|
+
try {
|
|
434
|
+
lexicalNodeJSON = currentNode.exportJSON();
|
|
435
|
+
}
|
|
436
|
+
catch (error) {
|
|
437
|
+
console.warn('Failed to export node JSON in propagateTextNode updated:', error);
|
|
438
|
+
}
|
|
439
|
+
return { textContent, format, mode, lexicalNodeJSON };
|
|
440
|
+
});
|
|
441
|
+
updateTextNodeInLoro(nodeKey, textContent, format, mode, lexicalNodeJSON, options);
|
|
442
|
+
}
|
|
443
|
+
break;
|
|
444
|
+
}
|
|
445
|
+
case 'destroyed': {
|
|
446
|
+
deleteTextNodeInLoro(nodeKey, options);
|
|
447
|
+
break;
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Lexical Node Propagator for Loro Tree Collaboration
|
|
3
|
+
*
|
|
4
|
+
* This module exports mutator functions for all Lexical node types
|
|
5
|
+
* to enable bidirectional synchronization with Loro Tree Loro.
|
|
6
|
+
*/
|
|
7
|
+
import { ElementNode, NodeKey } from 'lexical';
|
|
8
|
+
import { Binding } from '../Bindings';
|
|
9
|
+
import type { LoroTree } from 'loro-crdt';
|
|
10
|
+
export * from './RootNodePropagator';
|
|
11
|
+
export * from './LineBreakNodePropagator';
|
|
12
|
+
export * from './ElementNodePropagator';
|
|
13
|
+
export * from './TextNodePropagator';
|
|
14
|
+
export * from './DecoratorNodePropagator';
|
|
15
|
+
export { propagateRootNode } from './RootNodePropagator';
|
|
16
|
+
export { propagateLineBreakNode } from './LineBreakNodePropagator';
|
|
17
|
+
export { propagateElementNode } from './ElementNodePropagator';
|
|
18
|
+
export { propagateTextNode } from './TextNodePropagator';
|
|
19
|
+
export { propagateDecoratorNode } from './DecoratorNodePropagator';
|
|
20
|
+
/**
|
|
21
|
+
* Type definitions for mutator options
|
|
22
|
+
*/
|
|
23
|
+
export interface BasePropagatorOptions {
|
|
24
|
+
binding: Binding;
|
|
25
|
+
tree: LoroTree;
|
|
26
|
+
peerId: number;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Unified mutator interface for all node types
|
|
30
|
+
*/
|
|
31
|
+
export interface NodePropagator<T = any> {
|
|
32
|
+
create: (nodeKey: NodeKey, ...args: any[]) => string;
|
|
33
|
+
update: (nodeKey: NodeKey, ...args: any[]) => void;
|
|
34
|
+
delete: (nodeKey: NodeKey, options: BasePropagatorOptions) => void;
|
|
35
|
+
createFromLoro: (treeId: string, parentNode: ElementNode, index?: number, options?: BasePropagatorOptions) => T | null;
|
|
36
|
+
updateFromLoro: (treeId: string, lexicalNode: T, newParentNode?: ElementNode, newIndex?: number, options?: BasePropagatorOptions) => void;
|
|
37
|
+
deleteFromLoro: (treeId: string, lexicalNode: T, options?: BasePropagatorOptions) => void;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Node type constants for identifying different Lexical node types in Loro tree
|
|
41
|
+
*/
|
|
42
|
+
export declare const NODE_TYPES: {
|
|
43
|
+
readonly ROOT: "root";
|
|
44
|
+
readonly ELEMENT: "element";
|
|
45
|
+
readonly TEXT: "text";
|
|
46
|
+
readonly LINEBREAK: "linebreak";
|
|
47
|
+
readonly DECORATOR: "decorator";
|
|
48
|
+
};
|
|
49
|
+
export type NodeType = typeof NODE_TYPES[keyof typeof NODE_TYPES];
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (c) 2025-2026 Datalayer, Inc.
|
|
3
|
+
* Distributed under the terms of the MIT License.
|
|
4
|
+
*/
|
|
5
|
+
// Root Node Propagator
|
|
6
|
+
export * from './RootNodePropagator';
|
|
7
|
+
// Line Break Node Propagator
|
|
8
|
+
export * from './LineBreakNodePropagator';
|
|
9
|
+
// Element Node Propagator
|
|
10
|
+
export * from './ElementNodePropagator';
|
|
11
|
+
// Text Node Propagator
|
|
12
|
+
export * from './TextNodePropagator';
|
|
13
|
+
// Decorator Node Propagator
|
|
14
|
+
export * from './DecoratorNodePropagator';
|
|
15
|
+
// Main mutator functions for delegation
|
|
16
|
+
export { propagateRootNode } from './RootNodePropagator';
|
|
17
|
+
export { propagateLineBreakNode } from './LineBreakNodePropagator';
|
|
18
|
+
export { propagateElementNode } from './ElementNodePropagator';
|
|
19
|
+
export { propagateTextNode } from './TextNodePropagator';
|
|
20
|
+
export { propagateDecoratorNode } from './DecoratorNodePropagator';
|
|
21
|
+
/**
|
|
22
|
+
* Node type constants for identifying different Lexical node types in Loro tree
|
|
23
|
+
*/
|
|
24
|
+
export const NODE_TYPES = {
|
|
25
|
+
ROOT: 'root',
|
|
26
|
+
ELEMENT: 'element',
|
|
27
|
+
TEXT: 'text',
|
|
28
|
+
LINEBREAK: 'linebreak',
|
|
29
|
+
DECORATOR: 'decorator',
|
|
30
|
+
};
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import { LoroDoc, EphemeralStore, VersionVector } from 'loro-crdt';
|
|
2
|
+
import { ObservableV2 } from 'lib0/observable';
|
|
3
|
+
export declare const messageUpdate = "update";
|
|
4
|
+
export declare const messageQuerySnapshot = "query-snapshot";
|
|
5
|
+
export declare const messageEphemeral = "ephemeral";
|
|
6
|
+
export declare const messageQueryEphemeral = "query-ephemeral";
|
|
7
|
+
export interface LoroUpdateMessage {
|
|
8
|
+
type: 'update';
|
|
9
|
+
update: number[];
|
|
10
|
+
docId: string;
|
|
11
|
+
}
|
|
12
|
+
export interface EphemeralMessage {
|
|
13
|
+
type: 'ephemeral';
|
|
14
|
+
ephemeral: number[];
|
|
15
|
+
docId: string;
|
|
16
|
+
}
|
|
17
|
+
export interface QueryEphemeralMessage {
|
|
18
|
+
type: 'query-ephemeral';
|
|
19
|
+
docId: string;
|
|
20
|
+
clientId?: string;
|
|
21
|
+
}
|
|
22
|
+
export interface QuerySnapshotMessage {
|
|
23
|
+
type: 'query-snapshot';
|
|
24
|
+
docId: string;
|
|
25
|
+
clientId?: string;
|
|
26
|
+
}
|
|
27
|
+
export type LoroWebSocketMessage = LoroUpdateMessage | EphemeralMessage | QueryEphemeralMessage | QuerySnapshotMessage;
|
|
28
|
+
/**
|
|
29
|
+
* Websocket Provider for Loro. Creates a websocket connection to sync the shared document.
|
|
30
|
+
* The document name is attached to the provided url. I.e. the following example
|
|
31
|
+
* creates a websocket connection to http://localhost:1235/my-document-name
|
|
32
|
+
*/
|
|
33
|
+
export declare class WebsocketProvider extends ObservableV2<any> {
|
|
34
|
+
static globalEphemeralStore: EphemeralStore | null;
|
|
35
|
+
wsServerUrl: string;
|
|
36
|
+
docId: string;
|
|
37
|
+
doc: LoroDoc | null;
|
|
38
|
+
_WS: any;
|
|
39
|
+
protocols: any[];
|
|
40
|
+
params: {};
|
|
41
|
+
ephemeralStore: any;
|
|
42
|
+
awareness: any;
|
|
43
|
+
ws: any;
|
|
44
|
+
wsconnected: boolean;
|
|
45
|
+
wsconnecting: boolean;
|
|
46
|
+
bcconnected: boolean;
|
|
47
|
+
disableBc: boolean;
|
|
48
|
+
bcChannel: string;
|
|
49
|
+
maxBackoffTime: number;
|
|
50
|
+
wsUnsuccessfulReconnects: number;
|
|
51
|
+
messageHandlers: any[];
|
|
52
|
+
_synced: boolean;
|
|
53
|
+
wsLastMessageReceived: number;
|
|
54
|
+
shouldConnect: boolean;
|
|
55
|
+
snapshotLoaded: boolean;
|
|
56
|
+
_checkInterval: any;
|
|
57
|
+
_resyncInterval: any;
|
|
58
|
+
_updateHandler: any;
|
|
59
|
+
_ephemeralUpdateIntegrator: any;
|
|
60
|
+
_exitIntegrator: any;
|
|
61
|
+
_bcSubscriber: any;
|
|
62
|
+
_lastExportedVersion: VersionVector;
|
|
63
|
+
_pendingUpdates: Uint8Array[];
|
|
64
|
+
/**
|
|
65
|
+
* @param {string} wsServerUrl
|
|
66
|
+
* @param {string} docId
|
|
67
|
+
* @param {LoroDoc} doc
|
|
68
|
+
* @param {object} opts
|
|
69
|
+
* @param {boolean} [opts.connect]
|
|
70
|
+
* @param {EphemeralStore} [opts.ephemeralStore]
|
|
71
|
+
* @param {Object<string,string>} [opts.params] specify url parameters
|
|
72
|
+
* @param {Array<string>} [opts.protocols] specify websocket protocols
|
|
73
|
+
* @param {typeof WebSocket} [opts.WebSocketPolyfill] Optionally provide a WebSocket polyfill
|
|
74
|
+
* @param {number} [opts.resyncInterval] Request server state every `resyncInterval` milliseconds
|
|
75
|
+
* @param {number} [opts.maxBackoffTime] Maximum amount of time to wait before trying to reconnect (we try to reconnect using exponential backoff)
|
|
76
|
+
* @param {boolean} [opts.disableBc] Disable cross-tab BroadcastChannel communication
|
|
77
|
+
*/
|
|
78
|
+
constructor(wsServerUrl: string, docId: string, doc: LoroDoc, { connect, ephemeralStore, params, protocols, WebSocketPolyfill, resyncInterval, maxBackoffTime, disableBc }?: {
|
|
79
|
+
connect?: boolean;
|
|
80
|
+
ephemeralStore?: any;
|
|
81
|
+
params?: {};
|
|
82
|
+
protocols?: any[];
|
|
83
|
+
WebSocketPolyfill?: {
|
|
84
|
+
new (url: string | URL, protocols?: string | string[]): WebSocket;
|
|
85
|
+
prototype: WebSocket;
|
|
86
|
+
readonly CONNECTING: 0;
|
|
87
|
+
readonly OPEN: 1;
|
|
88
|
+
readonly CLOSING: 2;
|
|
89
|
+
readonly CLOSED: 3;
|
|
90
|
+
};
|
|
91
|
+
resyncInterval?: number;
|
|
92
|
+
maxBackoffTime?: number;
|
|
93
|
+
disableBc?: boolean;
|
|
94
|
+
});
|
|
95
|
+
get url(): string;
|
|
96
|
+
/**
|
|
97
|
+
* @type {boolean}
|
|
98
|
+
*/
|
|
99
|
+
get synced(): boolean;
|
|
100
|
+
set synced(state: boolean);
|
|
101
|
+
destroy(): void;
|
|
102
|
+
connectBc(): void;
|
|
103
|
+
disconnectBc(): void;
|
|
104
|
+
disconnect(): void;
|
|
105
|
+
connect(): void;
|
|
106
|
+
/**
|
|
107
|
+
* Manually send a Loro document update to connected peers
|
|
108
|
+
* Call this method after making changes to the LoroDoc
|
|
109
|
+
* @param {Uint8Array} update The update bytes from LoroDoc
|
|
110
|
+
*/
|
|
111
|
+
sendUpdate(update: Uint8Array): void;
|
|
112
|
+
/**
|
|
113
|
+
* Force cleanup of stale ephemeral states (for debugging)
|
|
114
|
+
* Removes user states that haven't been active for more than 5 minutes
|
|
115
|
+
*/
|
|
116
|
+
cleanupStaleStates(): void;
|
|
117
|
+
}
|