diff --git a/client/src/features/editor/Editor.tsx b/client/src/features/editor/Editor.tsx index 42956749..0af75b70 100644 --- a/client/src/features/editor/Editor.tsx +++ b/client/src/features/editor/Editor.tsx @@ -120,6 +120,10 @@ export const Editor = ({ onTitleChange, pageId, serializedEditorData }: EditorPr const handleBlockInput = useCallback( (e: React.FormEvent, block: CRDTBlock) => { if (!block) return; + if ((e.nativeEvent as InputEvent).isComposing) { + return; + } + let operationNode; const element = e.currentTarget; const newContent = element.textContent || ""; @@ -159,6 +163,27 @@ export const Editor = ({ onTitleChange, pageId, serializedEditorData }: EditorPr [sendCharInsertOperation, sendCharDeleteOperation], ); + const handleCompositionEnd = (e: React.CompositionEvent, block: CRDTBlock) => { + const event = e.nativeEvent as CompositionEvent; + const characters = [...event.data]; + const selection = window.getSelection(); + const caretPosition = selection?.focusOffset || 0; + const startPosition = caretPosition - characters.length; + + characters.forEach((char, index) => { + const insertPosition = startPosition + index; + const charNode = block.crdt.localInsert(insertPosition, char, block.id, pageId); + + sendCharInsertOperation({ + node: charNode.node, + blockId: block.id, + pageId, + }); + }); + + block.crdt.currentCaret = caretPosition; + }; + const subscriptionRef = useRef(false); useEffect(() => { @@ -286,6 +311,7 @@ export const Editor = ({ onTitleChange, pageId, serializedEditorData }: EditorPr block={block} isActive={block.id === editorState.currentBlock} onInput={handleBlockInput} + onCompositionEnd={handleCompositionEnd} onKeyDown={handleKeyDown} onClick={handleBlockClick} onAnimationSelect={handleAnimationSelect} diff --git a/client/src/features/editor/components/block/Block.tsx b/client/src/features/editor/components/block/Block.tsx index 3baa4827..897c8b57 100644 --- a/client/src/features/editor/components/block/Block.tsx +++ b/client/src/features/editor/components/block/Block.tsx @@ -16,6 +16,7 @@ interface BlockProps { block: CRDTBlock; isActive: boolean; onInput: (e: React.FormEvent, block: CRDTBlock) => void; + onCompositionEnd: (e: React.CompositionEvent, block: CRDTBlock) => void; onKeyDown: (e: React.KeyboardEvent) => void; onClick: (blockId: BlockId, e: React.MouseEvent) => void; onAnimationSelect: (blockId: BlockId, animation: AnimationType) => void; @@ -30,6 +31,7 @@ export const Block: React.FC = memo( block, isActive, onInput, + onCompositionEnd, onKeyDown, onClick, onAnimationSelect, @@ -127,6 +129,7 @@ export const Block: React.FC = memo( ref={blockRef} onKeyDown={onKeyDown} onInput={handleInput} + onCompositionEnd={(e) => onCompositionEnd(e, block)} onClick={(e) => onClick(block.id, e)} contentEditable suppressContentEditableWarning diff --git a/client/src/features/editor/hooks/useMarkdownGrammer.ts b/client/src/features/editor/hooks/useMarkdownGrammer.ts index 13604942..5d66022c 100644 --- a/client/src/features/editor/hooks/useMarkdownGrammer.ts +++ b/client/src/features/editor/hooks/useMarkdownGrammer.ts @@ -71,6 +71,7 @@ export const useMarkdownGrammer = ({ switch (e.key) { case "Enter": { + if (e.nativeEvent.isComposing) return; e.preventDefault(); const caretPosition = currentBlock.crdt.currentCaret; const currentContent = currentBlock.crdt.read();