From 368177e5874b0ae53777415bc351d9a023f9d03f Mon Sep 17 00:00:00 2001 From: FischLu Date: Sun, 16 Feb 2025 15:31:28 +0100 Subject: [PATCH] feat: improve model mention autocomplete behavior under IME --- .../home/Inputbar/MentionModelsButton.tsx | 84 ++++++++++++------- 1 file changed, 53 insertions(+), 31 deletions(-) diff --git a/src/renderer/src/pages/home/Inputbar/MentionModelsButton.tsx b/src/renderer/src/pages/home/Inputbar/MentionModelsButton.tsx index e408a052a3..5ebf7fac8f 100644 --- a/src/renderer/src/pages/home/Inputbar/MentionModelsButton.tsx +++ b/src/renderer/src/pages/home/Inputbar/MentionModelsButton.tsx @@ -28,6 +28,8 @@ const MentionModelsButton: FC = ({ mentionModels, onMentionModel: onSelec const menuRef = useRef(null) const [searchText, setSearchText] = useState('') const itemRefs = useRef>([]) + // 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 = ({ 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 = ({ 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 = ({ 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 = ({ 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 = (
- {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 ( -
-
{group.label}
-
- {group.children.map((item, idx) => ( -
setItemRef(startIndex + idx, el)} - className={`ant-dropdown-menu-item ${selectedIndex === startIndex + idx ? 'ant-dropdown-menu-item-selected' : ''}`} - onClick={item.onClick}> - {item.icon} - {item.label} -
- ))} + return ( +
+
{group.label}
+
+ {group.children.map((item, idx) => ( +
setItemRef(startIndex + idx, el)} + className={`ant-dropdown-menu-item ${ + selectedIndex === startIndex + idx ? 'ant-dropdown-menu-item-selected' : '' + }`} + onClick={item.onClick}> + {item.icon} + {item.label} +
+ ))} +
-
- ) - })} + ) + }) + ) : ( +
+
{t('models.no_matches')}
+
+ )}
) @@ -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 {