fix: ghost text post-process strip input prefix + mid-word isolation
CI / lint (push) Failing after 2s
CI / test (push) Has been skipped
CI / build (push) Has been skipped
CI / e2e (push) Has been skipped
CI / security (push) Failing after -8s

- Mid-word: extrait le mot partiel, demande ONLY remaining letters
- Post-process: strip le texte d'entree du debut de la reponse
- Support caracteres accentues dans la regex mid-word
- Prompt plus direct: Complete this word / Continue this text
This commit is contained in:
2026-06-16 12:01:38 -04:00
parent 9792c6a08b
commit 21ee6265d3
+29 -11
View File
@@ -1814,14 +1814,23 @@ body { font-family: var(--sans); background: var(--bg); color: var(--text); heig
}
function requestGhostCompletion() {
var text = ta.value.slice(0, ta.selectionStart);
if (text.trim().length < 1) return;
// Smart prompt: detect if mid-word, mid-phrase, or new block
var lastChar = text.slice(-1);
var midWord = /[a-zA-Z0-9]$/.test(lastChar);
var prompt = midWord
? 'Complete ONLY the current word being typed. Return ONLY the completion (no extra text):\n\n' + text
: 'Complete this text naturally in 1-2 short phrases. Return ONLY the new text (no repetition):\n\n' + text;
var fullText = ta.value.slice(0, ta.selectionStart);
if (fullText.trim().length < 1) return;
// Detect context: mid-word vs end of phrase
var lastChar = fullText.slice(-1);
var midWord = /[a-zA-Z0-9\u00C0-\u024F]$/.test(lastChar);
// For mid-word: extract the current word and ask AI to complete just it
var prompt, inputText;
if (midWord) {
var wordMatch = fullText.match(/([\w\u00C0-\u024F]+)$/);
var partialWord = wordMatch ? wordMatch[1] : fullText;
inputText = partialWord;
prompt = 'Complete this word. Return ONLY the remaining letters (no spaces, no extra text): ' + partialWord;
} else {
inputText = fullText;
prompt = 'Continue this text. Return ONLY the new text (do NOT repeat what is already written):\n' + fullText;
}
fetch('/api/ai/inline-complete', {
method: 'POST',
@@ -1830,10 +1839,19 @@ body { font-family: var(--sans); background: var(--bg); color: var(--text); heig
})
.then(function(r) { if (!r.ok) throw new Error('HTTP ' + r.status); return r.json(); })
.then(function(data) {
var prediction = (data.result || '').trim();
// Only show if prediction adds something new
var raw = (data.result || '').trim();
if (!raw || raw.length < 1) return;
// Strip any echoed input from the response
var prediction = raw;
if (midWord && prediction.toLowerCase().startsWith(inputText.toLowerCase())) {
prediction = prediction.slice(inputText.length);
} else if (!midWord && prediction.startsWith(inputText)) {
prediction = prediction.slice(inputText.length);
}
prediction = prediction.trim();
// Only show if prediction adds something meaningful
var alreadyThere = ta.value.slice(ta.selectionStart).trimStart();
if (prediction && prediction.length > 1 && !alreadyThere.startsWith(prediction.slice(0, Math.min(8, prediction.length)))) {
if (prediction && prediction.length > 0 && !alreadyThere.startsWith(prediction.slice(0, Math.min(6, prediction.length)))) {
showGhost(prediction);
}
})