Skip to content

Commit b5c9b2d

Browse files
committed
feat: Add shift-click to append suggestions to text area
When a user shift-clicks a suggestion, the text is now appended to the current text area content instead of being sent immediately. This allows users to: - Build complex prompts by combining multiple suggestions - Edit suggestions before sending them - Reuse parts of previous suggestions The implementation passes the mouse event through the component chain and checks for the shift key before deciding whether to append or send the message.
1 parent 2303f67 commit b5c9b2d

File tree

4 files changed

+35
-9
lines changed

4 files changed

+35
-9
lines changed

webview-ui/src/__mocks__/lucide-react.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,5 @@ export const Check = () => React.createElement("div")
44
export const ChevronsUpDown = () => React.createElement("div")
55
export const Loader = () => React.createElement("div")
66
export const X = () => React.createElement("div")
7+
export const Copy = () => React.createElement("div")
78
export const Database = (props: any) => React.createElement("span", { "data-testid": "database-icon", ...props })

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ interface ChatRowProps {
3333
isStreaming: boolean
3434
onToggleExpand: () => void
3535
onHeightChange: (isTaller: boolean) => void
36-
onSuggestionClick?: (answer: string) => void
36+
onSuggestionClick?: (answer: string, event?: React.MouseEvent) => void
3737
}
3838

3939
interface ChatRowContentProps extends Omit<ChatRowProps, "onHeightChange"> {}

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

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1055,8 +1055,15 @@ const ChatView = ({ isHidden, showAnnouncement, hideAnnouncement, showHistoryVie
10551055
isLast={index === groupedMessages.length - 1}
10561056
onHeightChange={handleRowHeightChange}
10571057
isStreaming={isStreaming}
1058-
onSuggestionClick={(answer: string) => {
1059-
handleSendMessage(answer, [])
1058+
onSuggestionClick={(answer: string, event?: React.MouseEvent) => {
1059+
if (event?.shiftKey) {
1060+
// Always append to existing text, don't overwrite
1061+
setInputValue((currentValue) => {
1062+
return currentValue !== "" ? `${currentValue} \n${answer}` : answer
1063+
})
1064+
} else {
1065+
handleSendMessage(answer, [])
1066+
}
10601067
}}
10611068
/>
10621069
)

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

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,18 @@
11
import { useCallback } from "react"
22
import { cn } from "../../lib/utils"
33
import { Button } from "../ui/button"
4+
import { Copy } from "lucide-react"
45

56
interface FollowUpSuggestProps {
67
suggestions?: string[]
7-
onSuggestionClick?: (answer: string) => void
8+
onSuggestionClick?: (answer: string, event?: React.MouseEvent) => void
89
ts: number
910
}
1011

1112
const FollowUpSuggest = ({ suggestions = [], onSuggestionClick, ts = 1 }: FollowUpSuggestProps) => {
1213
const handleSuggestionClick = useCallback(
13-
(suggestion: string) => {
14-
onSuggestionClick?.(suggestion)
14+
(suggestion: string, event: React.MouseEvent) => {
15+
onSuggestionClick?.(suggestion, event)
1516
},
1617
[onSuggestionClick],
1718
)
@@ -26,14 +27,31 @@ const FollowUpSuggest = ({ suggestions = [], onSuggestionClick, ts = 1 }: Follow
2627
<div className="h-full scrollbar-thin scrollbar-thumb-vscode-scrollbarSlider-background scrollbar-track-transparent">
2728
<div className={cn("flex gap-2.5 pb-2 flex-col h-full")}>
2829
{suggestions.map((suggestion) => (
29-
<div key={`${suggestion}-${ts}`} className="w-full">
30+
<div key={`${suggestion}-${ts}`} className="w-full relative group">
3031
<Button
3132
variant="secondary"
32-
className="w-full text-left whitespace-normal break-words h-auto min-h-[28px] py-2 justify-start"
33-
onClick={() => handleSuggestionClick(suggestion)}
33+
className="w-full text-left whitespace-normal break-words h-auto min-h-[28px] py-2 justify-start pr-8"
34+
onClick={(event) => handleSuggestionClick(suggestion, event)}
3435
aria-label={suggestion}>
3536
<span className="text-left">{suggestion}</span>
3637
</Button>
38+
<div
39+
className="absolute top-1 right-1 opacity-0 group-hover:opacity-100 transition-opacity"
40+
onClick={(e) => {
41+
e.stopPropagation();
42+
// Simulate shift-click by directly calling the handler with shiftKey=true
43+
onSuggestionClick?.(suggestion, { ...e, shiftKey: true });
44+
}}
45+
title="Copy to input (same as Shift+Click)"
46+
>
47+
<Button
48+
variant="ghost"
49+
size="icon"
50+
className="h-6 w-6 p-1 hover:bg-vscode-button-hoverBackground"
51+
>
52+
<Copy className="h-4 w-4" />
53+
</Button>
54+
</div>
3755
</div>
3856
))}
3957
</div>

0 commit comments

Comments
 (0)