Skip to content

Commit 4ecfb5f

Browse files
committed
autocomplete gets called on actual changes
1 parent 73ac1eb commit 4ecfb5f

2 files changed

Lines changed: 33 additions & 19 deletions

File tree

packages/react-textarea/src/components/copilot-textarea/copilot-textarea.tsx

Lines changed: 21 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -62,12 +62,6 @@ export function CopilotTextarea(props: CopilotTextareaProps): JSX.Element {
6262
const [editor] = useState(() => {
6363
const editor = withReact(createEditor());
6464

65-
const { onChange } = editor;
66-
editor.onChange = () => {
67-
// props.onChange?.(editorToText(editor));
68-
onChange();
69-
};
70-
7165
const { isVoid } = editor;
7266
editor.isVoid = (element) => {
7367
switch (element.type) {
@@ -102,18 +96,35 @@ export function CopilotTextarea(props: CopilotTextareaProps): JSX.Element {
10296
});
10397

10498
const renderElementMemoized = useCallback(renderElement, []);
105-
const handleAutocompleteKeyDown = useCallback(
106-
useAutocomplete(editor, props.autocompleteConfig),
99+
const onChangeForAutocomplete = useCallback(
100+
useAutocomplete(props.autocompleteConfig),
107101
[editor, props.autocompleteConfig]
108102
);
109103

104+
const [textValue, setTextValue] = useState(props.value || "");
105+
110106
return (
111107
// Add the editable component inside the context.
112-
<Slate editor={editor} initialValue={initialValue}>
108+
<Slate
109+
editor={editor}
110+
initialValue={initialValue}
111+
onChange={(value) => {
112+
const isAstChange = editor.operations.some(
113+
(op) => "set_selection" !== op.type
114+
);
115+
116+
if (isAstChange) {
117+
const newTextValue = editorToText(editor);
118+
if (newTextValue !== textValue) {
119+
onChangeForAutocomplete(editor, newTextValue);
120+
}
121+
setTextValue(newTextValue);
122+
}
123+
}}
124+
>
113125
<Editable
114126
className={props.className}
115127
renderElement={renderElementMemoized}
116-
onKeyDown={handleAutocompleteKeyDown}
117128
/>
118129
</Slate>
119130
);

packages/react-textarea/src/hooks/useAutocomplete.ts

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,19 @@ import {
33
AutocompleteConfig,
44
CustomEditor,
55
} from "../components/copilot-textarea/copilot-textarea";
6-
import { Transforms } from "slate";
7-
import { editorToText } from "../lib/editorToText";
6+
import { Descendant, Transforms } from "slate";
87

98
export function useAutocomplete(
10-
editor: CustomEditor,
119
autocompleteConfig: AutocompleteConfig
12-
) {
10+
): (editor: CustomEditor, newValue: string) => void {
1311
const [timer, setTimer] = useState<number | null>(null);
1412
const controllerRef = useRef<AbortController | null>(null);
1513

16-
const appendSuggestion = async (text: string, abortSignal: AbortSignal) => {
14+
const appendSuggestion = async (
15+
editor: CustomEditor,
16+
text: string,
17+
abortSignal: AbortSignal
18+
) => {
1719
const suggestion = await autocompleteConfig.autocomplete(text, abortSignal);
1820

1921
// We'll assume for now that the autocomplete function might or might not respect the abort signal.
@@ -31,7 +33,7 @@ export function useAutocomplete(
3133
inline: true,
3234
children: [
3335
{
34-
text: "world",
36+
text: suggestion,
3537
},
3638
],
3739
},
@@ -47,7 +49,7 @@ export function useAutocomplete(
4749
}
4850
};
4951

50-
const handleKeyDown = (event: React.KeyboardEvent) => {
52+
const onChange = (editor: CustomEditor, newValue: string) => {
5153
if (timer) clearTimeout(timer);
5254

5355
// If there's an ongoing autocomplete request, abort it
@@ -62,7 +64,8 @@ export function useAutocomplete(
6264

6365
try {
6466
await appendSuggestion(
65-
editorToText(editor),
67+
editor,
68+
newValue,
6669
controllerRef.current.signal
6770
);
6871
} catch (error: any) {
@@ -76,7 +79,7 @@ export function useAutocomplete(
7679
);
7780
};
7881

79-
return handleKeyDown;
82+
return onChange;
8083
}
8184

8285
const defaultDebounceTime = 2;

0 commit comments

Comments
 (0)