feat: improve model mention autocomplete behavior under IME

This commit is contained in:
FischLu 2025-02-16 15:31:28 +01:00 committed by 亢奋猫
parent 09a347cae4
commit 5a7bcd5997

View File

@ -28,6 +28,8 @@ const MentionModelsButton: FC<Props> = ({ mentionModels, onMentionModel: onSelec
const menuRef = useRef<HTMLDivElement>(null) const menuRef = useRef<HTMLDivElement>(null)
const [searchText, setSearchText] = useState('') const [searchText, setSearchText] = useState('')
const itemRefs = useRef<Array<HTMLDivElement | null>>([]) const itemRefs = useRef<Array<HTMLDivElement | null>>([])
// Add a new state to track if menu was dismissed
const [menuDismissed, setMenuDismissed] = useState(false)
const setItemRef = (index: number, el: HTMLDivElement | null) => { const setItemRef = (index: number, el: HTMLDivElement | null) => {
itemRefs.current[index] = el itemRefs.current[index] = el
@ -186,6 +188,7 @@ const MentionModelsButton: FC<Props> = ({ mentionModels, onMentionModel: onSelec
setIsOpen(true) setIsOpen(true)
setSelectedIndex(0) setSelectedIndex(0)
setSearchText('') setSearchText('')
setMenuDismissed(false) // Reset dismissed flag when manually showing selector
} }
const handleKeyDown = (e: KeyboardEvent) => { const handleKeyDown = (e: KeyboardEvent) => {
@ -218,6 +221,7 @@ const MentionModelsButton: FC<Props> = ({ mentionModels, onMentionModel: onSelec
} else if (e.key === 'Escape') { } else if (e.key === 'Escape') {
setIsOpen(false) setIsOpen(false)
setSearchText('') setSearchText('')
setMenuDismissed(true) // Set dismissed flag when Escape is pressed
} }
} }
@ -230,10 +234,14 @@ const MentionModelsButton: FC<Props> = ({ mentionModels, onMentionModel: onSelec
if (lastAtIndex === -1 || textBeforeCursor.slice(lastAtIndex + 1).includes(' ')) { if (lastAtIndex === -1 || textBeforeCursor.slice(lastAtIndex + 1).includes(' ')) {
setIsOpen(false) setIsOpen(false)
setSearchText('') setSearchText('')
} else if (lastAtIndex !== -1) { setMenuDismissed(false) // Reset dismissed flag when @ is removed
// Get the text after @ for search } else {
const searchStr = textBeforeCursor.slice(lastAtIndex + 1) // Only open menu if it wasn't explicitly dismissed
setSearchText(searchStr) if (!menuDismissed) {
setIsOpen(true)
const searchStr = textBeforeCursor.slice(lastAtIndex + 1)
setSearchText(searchStr)
}
} }
} }
@ -252,39 +260,42 @@ const MentionModelsButton: FC<Props> = ({ mentionModels, onMentionModel: onSelec
textArea.removeEventListener('input', handleTextChange) textArea.removeEventListener('input', handleTextChange)
} }
} }
}, [isOpen, selectedIndex, flatModelItems, mentionModels]) }, [isOpen, selectedIndex, flatModelItems, mentionModels, menuDismissed])
// Hide dropdown if no models available
if (flatModelItems.length === 0) {
return null
}
const menu = ( const menu = (
<div ref={menuRef} className="ant-dropdown-menu"> <div ref={menuRef} className="ant-dropdown-menu">
{modelMenuItems.map((group, groupIndex) => { {flatModelItems.length > 0 ? (
if (!group) return null modelMenuItems.map((group, groupIndex) => {
if (!group) return null
// Calculate the starting index for this group's items // Calculate starting index for items in this group
const startIndex = modelMenuItems.slice(0, groupIndex).reduce((acc, g) => acc + (g?.children?.length || 0), 0) const startIndex = modelMenuItems.slice(0, groupIndex).reduce((acc, g) => acc + (g?.children?.length || 0), 0)
return ( return (
<div key={group.key} className="ant-dropdown-menu-item-group"> <div key={group.key} className="ant-dropdown-menu-item-group">
<div className="ant-dropdown-menu-item-group-title">{group.label}</div> <div className="ant-dropdown-menu-item-group-title">{group.label}</div>
<div> <div>
{group.children.map((item, idx) => ( {group.children.map((item, idx) => (
<div <div
key={item.key} key={item.key}
ref={(el) => setItemRef(startIndex + idx, el)} ref={(el) => setItemRef(startIndex + idx, el)}
className={`ant-dropdown-menu-item ${selectedIndex === startIndex + idx ? 'ant-dropdown-menu-item-selected' : ''}`} className={`ant-dropdown-menu-item ${
onClick={item.onClick}> selectedIndex === startIndex + idx ? 'ant-dropdown-menu-item-selected' : ''
<span className="ant-dropdown-menu-item-icon">{item.icon}</span> }`}
{item.label} onClick={item.onClick}>
</div> <span className="ant-dropdown-menu-item-icon">{item.icon}</span>
))} {item.label}
</div>
))}
</div>
</div> </div>
</div> )
) })
})} ) : (
<div className="ant-dropdown-menu-item-group">
<div className="ant-dropdown-menu-item no-results">{t('models.no_matches')}</div>
</div>
)}
</div> </div>
) )
@ -334,6 +345,17 @@ const DropdownMenuStyle = createGlobalStyle`
&::-webkit-scrollbar-track { &::-webkit-scrollbar-track {
background: transparent; background: transparent;
} }
.no-results {
padding: 8px 12px;
color: var(--color-text-3);
cursor: default;
font-size: 14px;
&:hover {
background: none;
}
}
} }
.ant-dropdown-menu-item-group { .ant-dropdown-menu-item-group {