Browse Source

Merge pull request #8623 from Tryanks/autocompletion

feat: non-english (chinese) autocompletion handling
Timothy Jaeryang Baek 3 months ago
parent
commit
3994b1c6f7
1 changed files with 73 additions and 54 deletions
  1. 73 54
      src/lib/components/common/RichTextInput/AutoCompletion.js

+ 73 - 54
src/lib/components/common/RichTextInput/AutoCompletion.js

@@ -65,6 +65,69 @@ export const AIAutocompletion = Extension.create({
 		let touchStartX = 0;
 		let touchStartY = 0;
 
+		let isComposing = false;
+
+		const handleAICompletion = (view) => {
+			const { state, dispatch } = view;
+			const { selection } = state;
+			const { $head } = selection;
+
+			// Start debounce logic for AI generation only if the cursor is at the end of the paragraph
+			if (selection.empty && $head.pos === $head.end()) {
+				// Set up debounce for AI generation
+				if (this.options.debounceTime !== null) {
+					clearTimeout(debounceTimer);
+
+					// Capture current position
+					const currentPos = $head.before();
+
+					debounceTimer = setTimeout(() => {
+						if (isComposing) return false;
+
+						const newState = view.state;
+						const newSelection = newState.selection;
+						const newNode = newState.doc.nodeAt(currentPos);
+
+						// Check if the node still exists and is still a paragraph
+						if (
+							newNode &&
+							newNode.type.name === 'paragraph' &&
+							newSelection.$head.pos === newSelection.$head.end() &&
+							newSelection.$head.pos === currentPos + newNode.nodeSize - 1
+						) {
+							const prompt = newNode.textContent;
+
+							if (prompt.trim() !== '') {
+								if (loading) return true;
+								loading = true;
+								this.options
+									.generateCompletion(prompt)
+									.then((suggestion) => {
+										if (suggestion && suggestion.trim() !== '') {
+											if (view.state.selection.$head.pos === view.state.selection.$head.end()) {
+												if (view.state === newState) {
+													view.dispatch(
+														newState.tr.setNodeMarkup(currentPos, null, {
+															...newNode.attrs,
+															class: 'ai-autocompletion',
+															'data-prompt': prompt,
+															'data-suggestion': suggestion
+														})
+													);
+												}
+											}
+										}
+									})
+									.finally(() => {
+										loading = false;
+									});
+							}
+						}
+					}, this.options.debounceTime);
+				}
+			}
+		};
+
 		return [
 			new Plugin({
 				key: new PluginKey('aiAutocompletion'),
@@ -125,64 +188,20 @@ export const AIAutocompletion = Extension.create({
 								);
 							}
 
-							// Start debounce logic for AI generation only if the cursor is at the end of the paragraph
-							if (selection.empty && $head.pos === $head.end()) {
-								// Set up debounce for AI generation
-								if (this.options.debounceTime !== null) {
-									clearTimeout(debounceTimer);
-
-									// Capture current position
-									const currentPos = $head.before();
-
-									debounceTimer = setTimeout(() => {
-										const newState = view.state;
-										const newSelection = newState.selection;
-										const newNode = newState.doc.nodeAt(currentPos);
-
-										// Check if the node still exists and is still a paragraph
-										if (
-											newNode &&
-											newNode.type.name === 'paragraph' &&
-											newSelection.$head.pos === newSelection.$head.end() &&
-											newSelection.$head.pos === currentPos + newNode.nodeSize - 1
-										) {
-											const prompt = newNode.textContent;
-
-											if (prompt.trim() !== '') {
-												if (loading) return true;
-												loading = true;
-												this.options
-													.generateCompletion(prompt)
-													.then((suggestion) => {
-														if (suggestion && suggestion.trim() !== '') {
-															if (
-																view.state.selection.$head.pos === view.state.selection.$head.end()
-															) {
-																if (view.state === newState) {
-																	view.dispatch(
-																		newState.tr.setNodeMarkup(currentPos, null, {
-																			...newNode.attrs,
-																			class: 'ai-autocompletion',
-																			'data-prompt': prompt,
-																			'data-suggestion': suggestion
-																		})
-																	);
-																}
-															}
-														}
-													})
-													.finally(() => {
-														loading = false;
-													});
-											}
-										}
-									}, this.options.debounceTime);
-								}
-							}
+							handleAICompletion(view);
 						}
 						return false;
 					},
 					handleDOMEvents: {
+						compositionstart: () => {
+							isComposing = true;
+							return false;
+						},
+						compositionend: (view) => {
+							isComposing = false;
+							handleAICompletion(view);
+							return false;
+						},
 						touchstart: (view, event) => {
 							touchStartX = event.touches[0].clientX;
 							touchStartY = event.touches[0].clientY;