支持撤回

This commit is contained in:
2025-11-12 09:17:32 +08:00
parent 5a957cbae9
commit 1682b62832
+45 -21
View File
@@ -136,6 +136,35 @@ function getTextSegmentBounds(txt: string, pos: number) {
return { start, end }; return { start, end };
} }
// 统一的文本替换方法:优先使用原生插入以保留撤回栈,失败时回退
function applyTextReplacement(
el: HTMLTextAreaElement | HTMLInputElement,
start: number,
end: number,
text: string,
) {
try {
if (typeof el.setSelectionRange === 'function') {
el.setSelectionRange(start, end);
}
const ok = (document as any).execCommand && (document as any).execCommand('insertText', false, text);
if (ok) return;
} catch {}
try {
el.setRangeText(text, start, end, 'end');
try {
const ie = new (window as any).InputEvent('input', { bubbles: true, data: text, inputType: 'insertReplacementText' });
el.dispatchEvent(ie);
} catch {
el.dispatchEvent(new Event('input', { bubbles: true }));
}
} catch {
const value = (el as any).value as string;
(el as any).value = value.slice(0, start) + text + value.slice(end);
el.dispatchEvent(new Event('input', { bubbles: true }));
}
}
async function onKeyDown(e: KeyboardEvent) { async function onKeyDown(e: KeyboardEvent) {
if (e.key === 'Tab') { if (e.key === 'Tab') {
// 在光标位置进行补全,不影响撤回 // 在光标位置进行补全,不影响撤回
@@ -145,21 +174,19 @@ async function onKeyDown(e: KeyboardEvent) {
const before = store.promptText.slice(0, pos); const before = store.promptText.slice(0, pos);
const match = before.match(/[^,]*$/); const match = before.match(/[^,]*$/);
const prefix = (match ? match[0] : '').trim(); const prefix = (match ? match[0] : '').trim();
const { start, end } = getTextSegmentBounds(store.promptText, pos); const { start, end } = getTextSegmentBounds(store.promptText, pos);
const list = store.getSuggestions(prefix, 8); const list = store.getSuggestions(prefix, 8);
if (list.length > 0) { if (list.length > 0) {
e.preventDefault(); e.preventDefault();
const s = list[0]; const s = list[0];
if (!s) return; if (!s) return;
// 通过 setRangeText 模拟用户输入,保留撤回/前进栈 // 使用原生插入或回退方案,确保撤回可用
el.setRangeText(s, start, end, 'end'); applyTextReplacement(el, start, end, s);
// 确保触发 v-model 同步 await nextTick();
el.dispatchEvent(new Event('input', { bubbles: true })); updateSuggestions();
await nextTick();
updateSuggestions();
}
} }
} }
}
async function copyLeft() { async function copyLeft() {
try { try {
@@ -403,9 +430,8 @@ async function applySuggestion(s: string) {
el.focus(); el.focus();
const pos = el.selectionStart ?? store.promptText.length; const pos = el.selectionStart ?? store.promptText.length;
const { start, end } = getTextSegmentBounds(store.promptText, pos); const { start, end } = getTextSegmentBounds(store.promptText, pos);
// 使用 setRangeText 替换整个片段以支持撤回 // 使用原生插入或回退方式替换片段,确保撤回可用
el.setRangeText(s, start, end, 'end'); applyTextReplacement(el, start, end, s);
el.dispatchEvent(new Event('input', { bubbles: true }));
await nextTick(); await nextTick();
updateSuggestions(); updateSuggestions();
} }
@@ -423,8 +449,7 @@ function onEditKeyDown(e: KeyboardEvent) {
if (list.length > 0) { if (list.length > 0) {
e.preventDefault(); e.preventDefault();
const s = list[0]; const s = list[0];
if (s) el.setRangeText(s, 0, val.length, 'end'); if (s) applyTextReplacement(el, 0, val.length, s);
el.dispatchEvent(new Event('input', { bubbles: true }));
updateEditSuggestions(); updateEditSuggestions();
} }
} }
@@ -435,9 +460,8 @@ function applyEditSuggestion(s: string) {
// 保持焦点在编辑输入上 // 保持焦点在编辑输入上
el.focus(); el.focus();
const val = editingValue.value || ''; const val = editingValue.value || '';
// 直接替换整个输入为建议 // 直接替换整个输入为建议,保证撤回可用
el.setRangeText(s, 0, val.length, 'end'); applyTextReplacement(el, 0, val.length, s);
el.dispatchEvent(new Event('input', { bubbles: true }));
updateEditSuggestions(); updateEditSuggestions();
} }