fix(frontend): 多行输入时保持光标可见
adjustTextareaSize 重算高度会打乱 scrollTop,导致多行输入时光标被滚出视野。 新增 keepEditorSelectionVisible:高度重设后,聚焦态用编辑器 coordsAtPos 算出 光标位置并滚回可视区(上下留 lineHeight*0.3 padding),非聚焦态恢复原 scrollTop。 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
9be775d346
commit
e34e2c965c
@ -1528,6 +1528,9 @@ const adjustTextareaSize = () => {
|
||||
return;
|
||||
}
|
||||
const target = root;
|
||||
const previousScrollTop = target.scrollTop;
|
||||
const selectionShouldStayVisible =
|
||||
document.activeElement === target || target.contains(document.activeElement);
|
||||
target.style.height = 'auto';
|
||||
const computedStyle = window.getComputedStyle(target);
|
||||
const lineHeight = parseFloat(computedStyle.lineHeight || '20') || 20;
|
||||
@ -1537,11 +1540,58 @@ const adjustTextareaSize = () => {
|
||||
const multiline = targetHeight > lineHeight * 1.4;
|
||||
applyLineMetrics(lines, multiline);
|
||||
target.style.height = `${targetHeight}px`;
|
||||
keepEditorSelectionVisible(target, {
|
||||
previousScrollTop,
|
||||
selectionShouldStayVisible,
|
||||
padding: Math.max(4, lineHeight * 0.3)
|
||||
});
|
||||
nextTick(() => {
|
||||
emitComposerHeight();
|
||||
});
|
||||
};
|
||||
|
||||
const keepEditorSelectionVisible = (
|
||||
target: HTMLElement,
|
||||
options: { previousScrollTop: number; selectionShouldStayVisible: boolean; padding: number }
|
||||
) => {
|
||||
const maxScrollTop = Math.max(0, target.scrollHeight - target.clientHeight);
|
||||
if (maxScrollTop <= 0) {
|
||||
target.scrollTop = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!options.selectionShouldStayVisible) {
|
||||
target.scrollTop = Math.min(maxScrollTop, Math.max(0, options.previousScrollTop));
|
||||
return;
|
||||
}
|
||||
|
||||
requestAnimationFrame(() => {
|
||||
const instance = editor.value;
|
||||
if (!instance) {
|
||||
target.scrollTop = Math.min(maxScrollTop, Math.max(0, options.previousScrollTop));
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const caretRect = instance.view.coordsAtPos(instance.state.selection.head);
|
||||
const targetRect = target.getBoundingClientRect();
|
||||
const padding = options.padding;
|
||||
const currentMaxScrollTop = Math.max(0, target.scrollHeight - target.clientHeight);
|
||||
let nextScrollTop = target.scrollTop;
|
||||
|
||||
if (caretRect.bottom > targetRect.bottom - padding) {
|
||||
nextScrollTop += caretRect.bottom - targetRect.bottom + padding;
|
||||
} else if (caretRect.top < targetRect.top + padding) {
|
||||
nextScrollTop -= targetRect.top + padding - caretRect.top;
|
||||
}
|
||||
|
||||
target.scrollTop = Math.min(currentMaxScrollTop, Math.max(0, nextScrollTop));
|
||||
} catch {
|
||||
target.scrollTop = Math.min(maxScrollTop, Math.max(0, options.previousScrollTop));
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const onInput = (event: Event) => {
|
||||
const target = event.target as HTMLTextAreaElement;
|
||||
if (!props.isConnected || props.inputLocked) {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user