mirror of
https://github.com/open-webui/open-webui
synced 2025-01-19 09:16:44 +00:00
Merge pull request #8623 from Tryanks/autocompletion
feat: non-english (chinese) autocompletion handling
This commit is contained in:
commit
3994b1c6f7
@ -65,6 +65,69 @@ export const AIAutocompletion = Extension.create({
|
|||||||
let touchStartX = 0;
|
let touchStartX = 0;
|
||||||
let touchStartY = 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 [
|
return [
|
||||||
new Plugin({
|
new Plugin({
|
||||||
key: new PluginKey('aiAutocompletion'),
|
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
|
handleAICompletion(view);
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
},
|
},
|
||||||
handleDOMEvents: {
|
handleDOMEvents: {
|
||||||
|
compositionstart: () => {
|
||||||
|
isComposing = true;
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
compositionend: (view) => {
|
||||||
|
isComposing = false;
|
||||||
|
handleAICompletion(view);
|
||||||
|
return false;
|
||||||
|
},
|
||||||
touchstart: (view, event) => {
|
touchstart: (view, event) => {
|
||||||
touchStartX = event.touches[0].clientX;
|
touchStartX = event.touches[0].clientX;
|
||||||
touchStartY = event.touches[0].clientY;
|
touchStartY = event.touches[0].clientY;
|
||||||
|
Loading…
Reference in New Issue
Block a user