Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: improve model mention autocomplete behavior under IME #1779

Merged
merged 1 commit into from
Feb 17, 2025
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
88 changes: 55 additions & 33 deletions src/renderer/src/pages/home/Inputbar/MentionModelsButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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) => {
Expand Down Expand Up @@ -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
}
}

Expand All @@ -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)
}
}
}

Expand All @@ -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

// Calculate the starting index for this group's items
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>
))}
{flatModelItems.length > 0 ? (
modelMenuItems.map((group, groupIndex) => {
if (!group) return null

// 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>
))}
</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>
)

Expand Down Expand Up @@ -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 {
Expand Down