Fix prompt placeholder overlap during IME input

This commit is contained in:
wangbo 2026-05-13 23:47:05 +08:00
parent f1535a94c2
commit f254551522

View File

@ -49,15 +49,16 @@ export function PlaygroundPromptMentionInput(props: {
const editableRef = useRef<HTMLDivElement>(null);
const dropdownRef = useRef<HTMLDivElement>(null);
const blurTimerRef = useRef<number | undefined>(undefined);
const isComposingRef = useRef(false);
const [text, setText] = useState(props.value);
const [focused, setFocused] = useState(false);
const [isComposing, setIsComposing] = useState(false);
const [hasEditableContent, setHasEditableContent] = useState(() => promptTextHasContent(props.value));
const [mentionOpen, setMentionOpen] = useState(false);
const [mentionAtIndex, setMentionAtIndex] = useState(-1);
const [mentionSearch, setMentionSearch] = useState('');
const [highlightIndex, setHighlightIndex] = useState(0);
const [dropdownPosition, setDropdownPosition] = useState({ top: 0, left: 0, placement: 'bottom' as 'bottom' | 'top' });
const showPlaceholder = text.trim().length === 0;
const showPlaceholder = !hasEditableContent;
const uploadSignature = useMemo(
() => props.uploads.map((item) => `${item.id}:${item.kind}:${item.name}:${item.url}`).join('|'),
@ -78,8 +79,12 @@ export function PlaygroundPromptMentionInput(props: {
}, [mentionItems, mentionSearch]);
useEffect(() => {
if (props.value === text) return;
if (props.value === text) {
setHasEditableContent(promptTextHasContent(props.value));
return;
}
setText(props.value);
setHasEditableContent(promptTextHasContent(props.value));
if (!focused) {
requestAnimationFrame(() => renderToEditable(props.value));
}
@ -89,6 +94,7 @@ export function PlaygroundPromptMentionInput(props: {
const cleaned = removeInvalidPlaygroundResourceTokens(text, props.uploads);
if (cleaned !== text) {
setText(cleaned);
setHasEditableContent(promptTextHasContent(cleaned));
props.onChange(cleaned);
requestAnimationFrame(() => renderToEditable(cleaned));
return;
@ -149,6 +155,7 @@ export function PlaygroundPromptMentionInput(props: {
const editable = editableRef.current;
if (!editable) return;
editable.innerHTML = textToHtml(nextText, props.uploads);
setHasEditableContent(promptTextHasContent(nextText));
if (typeof caret === 'number') {
editable.focus();
setCaretOffset(editable, caret);
@ -157,8 +164,10 @@ export function PlaygroundPromptMentionInput(props: {
function commitFromEditable(shouldInspectMention: boolean, event?: InputEvent) {
const editable = editableRef.current;
if (!editable || props.disabled || isComposing) return;
if (!editable || props.disabled) return;
const nextText = serializeEditableToPlainText(editable);
setHasEditableContent(promptTextHasContent(nextText));
if (isComposingRef.current || event?.isComposing) return;
setText(nextText);
props.onChange(nextText);
if (!shouldInspectMention) return;
@ -206,6 +215,7 @@ export function PlaygroundPromptMentionInput(props: {
const nextText = `${before}${token} ${after}`;
const nextCaret = before.length + token.length + 1;
setText(nextText);
setHasEditableContent(promptTextHasContent(nextText));
props.onChange(nextText);
closeMention();
requestAnimationFrame(() => renderToEditable(nextText, nextCaret));
@ -257,10 +267,13 @@ export function PlaygroundPromptMentionInput(props: {
}, 120);
}}
onCompositionEnd={() => {
setIsComposing(false);
isComposingRef.current = false;
setHasEditableContent(editablePromptTextHasContent(editableRef.current));
requestAnimationFrame(() => commitFromEditable(true));
}}
onCompositionStart={() => setIsComposing(true)}
onCompositionStart={() => {
isComposingRef.current = true;
}}
onFocus={() => {
setFocused(true);
if (blurTimerRef.current) window.clearTimeout(blurTimerRef.current);
@ -313,6 +326,14 @@ export function PlaygroundPromptMentionInput(props: {
);
}
function promptTextHasContent(raw: string) {
return raw.trim().length > 0;
}
function editablePromptTextHasContent(editable: HTMLElement | null) {
return editable ? promptTextHasContent(serializeEditableToPlainText(editable)) : false;
}
function MentionThumb(props: { item: PlaygroundMentionUpload }) {
if (props.item.kind === 'image') {
return <img className="promptMentionThumb" src={props.item.url} alt="" draggable={false} />;