mirror of
https://github.com/RooVetGit/Roo-Code.git
synced 2026-08-28 05:27:24 +00:00
fix: prevent autocomplete mouse hover from interfering with keyboard navigation
- Added separate hover and selection states in ContextMenu - Mouse position no longer automatically highlights items - Mouse must move at least 5 pixels to activate hover highlighting - Keyboard selection (highlighted with activeSelectionBackground) takes precedence - Mouse hover shows subtle hover background only after movement - Fixes #8615
This commit is contained in:
parent
3a47c55a2e
commit
2a03b2b38f
1 changed files with 58 additions and 5 deletions
|
|
@ -1,4 +1,4 @@
|
|||
import React, { useEffect, useMemo, useRef, useState } from "react"
|
||||
import React, { useCallback, useEffect, useMemo, useRef, useState } from "react"
|
||||
import { getIconForFilePath, getIconUrlByName, getIconForDirectoryPath } from "vscode-material-icons"
|
||||
import { Settings } from "lucide-react"
|
||||
|
||||
|
|
@ -46,6 +46,9 @@ const ContextMenu: React.FC<ContextMenuProps> = ({
|
|||
}) => {
|
||||
const [materialIconsBaseUri, setMaterialIconsBaseUri] = useState("")
|
||||
const menuRef = useRef<HTMLDivElement>(null)
|
||||
const [hoveredIndex, setHoveredIndex] = useState<number | null>(null)
|
||||
const [hasMouseMoved, setHasMouseMoved] = useState(false)
|
||||
const lastMousePosRef = useRef<{ x: number; y: number } | null>(null)
|
||||
|
||||
const filteredOptions = useMemo(() => {
|
||||
return getContextMenuOptions(searchQuery, selectedType, queryItems, dynamicSearchResults, modes, commands)
|
||||
|
|
@ -73,6 +76,33 @@ const ContextMenu: React.FC<ContextMenuProps> = ({
|
|||
setMaterialIconsBaseUri(w.MATERIAL_ICONS_BASE_URI)
|
||||
}, [])
|
||||
|
||||
// Track mouse movement to distinguish between actual hover and incidental cursor position
|
||||
const handleMouseMove = useCallback((e: React.MouseEvent) => {
|
||||
const currentPos = { x: e.clientX, y: e.clientY }
|
||||
|
||||
// If this is the first mouse position, just store it
|
||||
if (!lastMousePosRef.current) {
|
||||
lastMousePosRef.current = currentPos
|
||||
return
|
||||
}
|
||||
|
||||
// Check if the mouse has moved more than a threshold (e.g., 5 pixels)
|
||||
const deltaX = Math.abs(currentPos.x - lastMousePosRef.current.x)
|
||||
const deltaY = Math.abs(currentPos.y - lastMousePosRef.current.y)
|
||||
|
||||
if (deltaX > 5 || deltaY > 5) {
|
||||
setHasMouseMoved(true)
|
||||
lastMousePosRef.current = currentPos
|
||||
}
|
||||
}, [])
|
||||
|
||||
// Reset mouse tracking when menu opens/closes
|
||||
useEffect(() => {
|
||||
setHasMouseMoved(false)
|
||||
setHoveredIndex(null)
|
||||
lastMousePosRef.current = null
|
||||
}, [searchQuery])
|
||||
|
||||
const renderOptionContent = (option: ContextMenuQueryItem) => {
|
||||
switch (option.type) {
|
||||
case ContextMenuOptionType.SectionHeader:
|
||||
|
|
@ -340,14 +370,18 @@ const ContextMenu: React.FC<ContextMenuProps> = ({
|
|||
filteredOptions.map((option, index) => (
|
||||
<div
|
||||
key={`${option.type}-${option.value || index}`}
|
||||
onClick={() => isOptionSelectable(option) && onSelect(option.type, option.value)}
|
||||
onClick={() => {
|
||||
if (isOptionSelectable(option)) {
|
||||
setSelectedIndex(index)
|
||||
onSelect(option.type, option.value)
|
||||
}
|
||||
}}
|
||||
style={{
|
||||
padding:
|
||||
option.type === ContextMenuOptionType.SectionHeader
|
||||
? "16px 8px 4px 8px"
|
||||
: "4px 8px",
|
||||
cursor: isOptionSelectable(option) ? "pointer" : "default",
|
||||
color: "var(--vscode-dropdown-foreground)",
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "space-between",
|
||||
|
|
@ -358,14 +392,33 @@ const ContextMenu: React.FC<ContextMenuProps> = ({
|
|||
marginBottom: "2px",
|
||||
}
|
||||
: {}),
|
||||
// Show different styles for selection vs hover
|
||||
...(index === selectedIndex && isOptionSelectable(option)
|
||||
? {
|
||||
backgroundColor: "var(--vscode-list-activeSelectionBackground)",
|
||||
color: "var(--vscode-list-activeSelectionForeground)",
|
||||
}
|
||||
: {}),
|
||||
: index === hoveredIndex && isOptionSelectable(option) && hasMouseMoved
|
||||
? {
|
||||
backgroundColor: "var(--vscode-list-hoverBackground)",
|
||||
color: "var(--vscode-dropdown-foreground)",
|
||||
}
|
||||
: {
|
||||
color: "var(--vscode-dropdown-foreground)",
|
||||
}),
|
||||
}}
|
||||
onMouseEnter={() => isOptionSelectable(option) && setSelectedIndex(index)}>
|
||||
onMouseEnter={() => {
|
||||
// Only update hover state if mouse has actually moved
|
||||
if (isOptionSelectable(option) && hasMouseMoved) {
|
||||
setHoveredIndex(index)
|
||||
}
|
||||
}}
|
||||
onMouseMove={handleMouseMove}
|
||||
onMouseLeave={() => {
|
||||
if (index === hoveredIndex) {
|
||||
setHoveredIndex(null)
|
||||
}
|
||||
}}>
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue