Skip to content

Commit bcb1c5c

Browse files
committed
feat: add ctrl/cmd+click navigation for file mentions in chat textarea
- Added click handler to highlight layer to detect clicks on mentions - Track ctrl/cmd key state to enable clickable behavior - Extract file path from clicked mention and send openMention message - Added CSS styles for visual feedback when mentions are clickable - Mentions show pointer cursor and underline on hover when ctrl/cmd is pressed Implements #8144
1 parent 87b45de commit bcb1c5c

File tree

2 files changed

+85
-0
lines changed

2 files changed

+85
-0
lines changed

webview-ui/src/components/chat/ChatTextArea.tsx

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ import { MAX_IMAGES_PER_MESSAGE } from "./ChatView"
3131
import ContextMenu from "./ContextMenu"
3232
import { IndexingStatusBadge } from "./IndexingStatusBadge"
3333
import { usePromptHistory } from "./hooks/usePromptHistory"
34+
import "../../styles/chat-textarea.css"
3435

3536
interface ChatTextAreaProps {
3637
inputValue: string
@@ -205,6 +206,7 @@ export const ChatTextArea = forwardRef<HTMLTextAreaElement, ChatTextAreaProps>(
205206
const [isMouseDownOnMenu, setIsMouseDownOnMenu] = useState(false)
206207
const highlightLayerRef = useRef<HTMLDivElement>(null)
207208
const [selectedMenuIndex, setSelectedMenuIndex] = useState(-1)
209+
const [isCtrlOrCmdPressed, setIsCtrlOrCmdPressed] = useState(false)
208210
const [selectedType, setSelectedType] = useState<ContextMenuOptionType | null>(null)
209211
const [justDeletedSpaceAfterMention, setJustDeletedSpaceAfterMention] = useState(false)
210212
const [intendedCursorPosition, setIntendedCursorPosition] = useState<number | null>(null)
@@ -712,6 +714,59 @@ export const ChatTextArea = forwardRef<HTMLTextAreaElement, ChatTextAreaProps>(
712714
setIsMouseDownOnMenu(true)
713715
}, [])
714716

717+
// Track ctrl/cmd key state
718+
useEffect(() => {
719+
const handleKeyDown = (e: KeyboardEvent) => {
720+
if (e.ctrlKey || e.metaKey) {
721+
setIsCtrlOrCmdPressed(true)
722+
}
723+
}
724+
725+
const handleKeyUp = (e: KeyboardEvent) => {
726+
if (!e.ctrlKey && !e.metaKey) {
727+
setIsCtrlOrCmdPressed(false)
728+
}
729+
}
730+
731+
window.addEventListener("keydown", handleKeyDown)
732+
window.addEventListener("keyup", handleKeyUp)
733+
734+
return () => {
735+
window.removeEventListener("keydown", handleKeyDown)
736+
window.removeEventListener("keyup", handleKeyUp)
737+
}
738+
}, [])
739+
740+
// Handle clicks on mentions in the highlight layer
741+
const handleHighlightClick = useCallback((e: React.MouseEvent<HTMLDivElement>) => {
742+
// Only handle clicks with ctrl/cmd key pressed
743+
if (!e.ctrlKey && !e.metaKey) {
744+
return
745+
}
746+
747+
// Get the clicked element
748+
const target = e.target as HTMLElement
749+
750+
// Check if we clicked on a mention highlight
751+
if (target.classList.contains("mention-context-textarea-highlight")) {
752+
const mentionText = target.textContent
753+
if (mentionText) {
754+
// Extract the mention text (remove @ prefix if present)
755+
const cleanMention = mentionText.startsWith("@") ? mentionText.slice(1) : mentionText
756+
757+
// Send message to open the mention
758+
vscode.postMessage({
759+
type: "openMention",
760+
text: cleanMention,
761+
})
762+
763+
// Prevent default behavior
764+
e.preventDefault()
765+
e.stopPropagation()
766+
}
767+
}
768+
}, [])
769+
715770
const updateHighlights = useCallback(() => {
716771
if (!textAreaRef.current || !highlightLayerRef.current) return
717772

@@ -982,6 +1037,7 @@ export const ChatTextArea = forwardRef<HTMLTextAreaElement, ChatTextAreaProps>(
9821037
<div
9831038
ref={highlightLayerRef}
9841039
data-testid="highlight-layer"
1040+
onClick={handleHighlightClick}
9851041
className={cn(
9861042
"absolute",
9871043
"inset-0",
@@ -1004,6 +1060,7 @@ export const ChatTextArea = forwardRef<HTMLTextAreaElement, ChatTextAreaProps>(
10041060
"z-10",
10051061
"forced-color-adjust-none",
10061062
"rounded",
1063+
isCtrlOrCmdPressed && "mention-highlight-clickable",
10071064
)}
10081065
style={{
10091066
color: "transparent",
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
/* Styles for clickable mentions in the chat textarea */
2+
.mention-highlight-clickable .mention-context-textarea-highlight {
3+
cursor: pointer;
4+
position: relative;
5+
}
6+
7+
.mention-highlight-clickable .mention-context-textarea-highlight:hover {
8+
text-decoration: underline;
9+
text-decoration-color: var(--vscode-textLink-foreground);
10+
text-underline-offset: 2px;
11+
}
12+
13+
/* Visual feedback for clickable state */
14+
.mention-highlight-clickable .mention-context-textarea-highlight::after {
15+
content: "";
16+
position: absolute;
17+
inset: -2px;
18+
border-radius: 3px;
19+
pointer-events: none;
20+
opacity: 0;
21+
transition: opacity 0.15s ease;
22+
background: var(--vscode-textLink-foreground);
23+
mix-blend-mode: multiply;
24+
}
25+
26+
.mention-highlight-clickable .mention-context-textarea-highlight:hover::after {
27+
opacity: 0.1;
28+
}

0 commit comments

Comments
 (0)