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 [searchText, setSearchText] = useState('')
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) => {
itemRefs.current[index] = el
@ -186,6 +188,7 @@ const MentionModelsButton: FC<Props> = ({ mentionModels, onMentionModel: onSelec
setIsOpen(true)
setSelectedIndex(0)
setSearchText('')
setMenuDismissed(false) // Reset dismissed flag when manually showing selector
}
const handleKeyDown = (e: KeyboardEvent) => {
@ -218,6 +221,7 @@ const MentionModelsButton: FC<Props> = ({ mentionModels, onMentionModel: onSelec
} else if (e.key === 'Escape') {
setIsOpen(false)
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(' ')) {
setIsOpen(false)
setSearchText('')
} else if (lastAtIndex !== -1) {
// Get the text after @ for search
const searchStr = textBeforeCursor.slice(lastAtIndex + 1)
setSearchText(searchStr)
setMenuDismissed(false) // Reset dismissed flag when @ is removed
} else {
// Only open menu if it wasn't explicitly dismissed
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)
}
}
}, [isOpen, selectedIndex, flatModelItems, mentionModels])
// Hide dropdown if no models available
if (flatModelItems.length === 0) {
return null
}
}, [isOpen, selectedIndex, flatModelItems, mentionModels, menuDismissed])
const menu = (
<div ref={menuRef} className="ant-dropdown-menu">
{modelMenuItems.map((group, groupIndex) => {
if (!group) return null
{flatModelItems.length > 0 ? (
modelMenuItems.map((group, groupIndex) => {
if (!group) return null
// Calculate the starting index for this group's items
const startIndex = modelMenuItems.slice(0, groupIndex).reduce((acc, g) => acc + (g?.children?.length || 0), 0)
// Calculate starting index for items in this group
const startIndex = modelMenuItems.slice(0, groupIndex).reduce((acc, g) => acc + (g?.children?.length || 0), 0)
return (
<div key={group.key} className="ant-dropdown-menu-item-group">
<div className="ant-dropdown-menu-item-group-title">{group.label}</div>
<div>
{group.children.map((item, idx) => (
<div
key={item.key}
ref={(el) => setItemRef(startIndex + idx, el)}
className={`ant-dropdown-menu-item ${selectedIndex === startIndex + idx ? 'ant-dropdown-menu-item-selected' : ''}`}
onClick={item.onClick}>
<span className="ant-dropdown-menu-item-icon">{item.icon}</span>
{item.label}
</div>
))}
return (
<div key={group.key} className="ant-dropdown-menu-item-group">
<div className="ant-dropdown-menu-item-group-title">{group.label}</div>
<div>
{group.children.map((item, idx) => (
<div
key={item.key}
ref={(el) => setItemRef(startIndex + idx, el)}
className={`ant-dropdown-menu-item ${
selectedIndex === startIndex + idx ? 'ant-dropdown-menu-item-selected' : ''
}`}
onClick={item.onClick}>
<span className="ant-dropdown-menu-item-icon">{item.icon}</span>
{item.label}
</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>
)
@ -334,6 +345,17 @@ const DropdownMenuStyle = createGlobalStyle`
&::-webkit-scrollbar-track {
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 {