@@ -16,6 +16,12 @@ import { findMatchingResourceOrTemplate } from "@src/utils/mcp"
1616import { vscode } from "@src/utils/vscode"
1717import { removeLeadingNonAlphanumeric } from "@src/utils/removeLeadingNonAlphanumeric"
1818import { getLanguageFromPath } from "@src/utils/getLanguageFromPath"
19+ import {
20+ DropdownMenu ,
21+ DropdownMenuContent ,
22+ DropdownMenuItem ,
23+ DropdownMenuTrigger ,
24+ } from "@src/components/ui/dropdown-menu"
1925
2026import { ToolUseBlock , ToolUseBlockHeader } from "../common/ToolUseBlock"
2127import UpdateTodoListToolBlock from "./UpdateTodoListToolBlock"
@@ -1002,29 +1008,63 @@ export const ChatRowContent = ({
10021008
10031009 return (
10041010 < >
1005- < div
1006- className = { `group text-sm transition-opacity ${
1007- isApiRequestInProgress ? "opacity-100" : "opacity-40 hover:opacity-100"
1008- } `}
1009- style = { {
1010- ...headerStyle ,
1011- marginBottom :
1012- ( ( cost === null || cost === undefined ) && apiRequestFailedMessage ) ||
1013- apiReqStreamingFailedMessage
1014- ? 10
1015- : 0 ,
1016- justifyContent : "space-between" ,
1017- } } >
1018- < div style = { { display : "flex" , alignItems : "center" , gap : "10px" , flexGrow : 1 } } >
1019- { icon }
1020- { title }
1021- </ div >
1022- < div
1023- className = "text-xs text-vscode-dropdown-foreground border-vscode-dropdown-border/50 border px-1.5 py-0.5 rounded-lg"
1024- style = { { opacity : cost !== null && cost !== undefined && cost > 0 ? 1 : 0 } } >
1025- ${ Number ( cost || 0 ) ?. toFixed ( 4 ) }
1026- </ div >
1027- </ div >
1011+ < DropdownMenu >
1012+ < DropdownMenuTrigger asChild >
1013+ < div
1014+ className = { `group text-sm transition-opacity ${
1015+ isApiRequestInProgress ? "opacity-100" : "opacity-40 hover:opacity-100"
1016+ } `}
1017+ style = { {
1018+ ...headerStyle ,
1019+ marginBottom :
1020+ ( ( cost === null || cost === undefined ) && apiRequestFailedMessage ) ||
1021+ apiReqStreamingFailedMessage
1022+ ? 10
1023+ : 0 ,
1024+ justifyContent : "space-between" ,
1025+ } }
1026+ onContextMenu = { ( e ) => e . preventDefault ( ) } >
1027+ < div
1028+ style = { { display : "flex" , alignItems : "center" , gap : "10px" , flexGrow : 1 } } >
1029+ { icon }
1030+ { title }
1031+ </ div >
1032+ < div style = { { display : "flex" , alignItems : "center" , gap : "8px" } } >
1033+ < div
1034+ className = "text-xs text-vscode-dropdown-foreground border-vscode-dropdown-border/50 border px-1.5 py-0.5 rounded-lg"
1035+ style = { {
1036+ opacity : cost !== null && cost !== undefined && cost > 0 ? 1 : 0 ,
1037+ } } >
1038+ ${ Number ( cost || 0 ) ?. toFixed ( 4 ) }
1039+ </ div >
1040+ < div
1041+ className = "cursor-pointer shrink-0 opacity-0 group-hover:opacity-30 hover:!opacity-100 transition-opacity"
1042+ style = { { visibility : isApiRequestInProgress ? "hidden" : "visible" } }
1043+ onClick = { ( e ) => {
1044+ e . stopPropagation ( )
1045+ vscode . postMessage ( { type : "deleteMessage" , value : message . ts } )
1046+ } }
1047+ title = { t ( "chat:deleteMessageAndSubsequent" ) } >
1048+ < Trash2 className = "w-4 shrink-0" aria-label = "Delete message icon" />
1049+ </ div >
1050+ </ div >
1051+ </ div >
1052+ </ DropdownMenuTrigger >
1053+ < DropdownMenuContent >
1054+ < DropdownMenuItem
1055+ onClick = { ( ) => vscode . postMessage ( { type : "deleteMessage" , value : message . ts } ) }
1056+ className = "flex items-center gap-2"
1057+ disabled = { isApiRequestInProgress } >
1058+ < Trash2 className = "w-4 h-4" />
1059+ < span >
1060+ { t (
1061+ "chat:deleteMessageAndSubsequent" ,
1062+ "Delete message and subsequent nodes" ,
1063+ ) }
1064+ </ span >
1065+ </ DropdownMenuItem >
1066+ </ DropdownMenuContent >
1067+ </ DropdownMenu >
10281068 { ( ( ( cost === null || cost === undefined ) && apiRequestFailedMessage ) ||
10291069 apiReqStreamingFailedMessage ) && (
10301070 < ErrorRow
@@ -1053,22 +1093,49 @@ export const ChatRowContent = ({
10531093 return null // we should never see this message type
10541094 case "text" :
10551095 return (
1056- < div >
1057- < div style = { headerStyle } >
1058- < MessageCircle className = "w-4 shrink-0" aria-label = "Speech bubble icon" />
1059- < span style = { { fontWeight : "bold" } } > { t ( "chat:text.rooSaid" ) } </ span >
1060- </ div >
1061- < div className = "pl-6" >
1062- < Markdown markdown = { message . text } partial = { message . partial } />
1063- { message . images && message . images . length > 0 && (
1064- < div style = { { marginTop : "10px" } } >
1065- { message . images . map ( ( image , index ) => (
1066- < ImageBlock key = { index } imageData = { image } />
1067- ) ) }
1096+ < DropdownMenu >
1097+ < DropdownMenuTrigger asChild >
1098+ < div className = "group" onContextMenu = { ( e ) => e . preventDefault ( ) } >
1099+ < div style = { { ...headerStyle , justifyContent : "space-between" } } >
1100+ < div style = { { display : "flex" , alignItems : "center" , gap : "10px" } } >
1101+ < MessageCircle className = "w-4 shrink-0" aria-label = "Speech bubble icon" />
1102+ < span style = { { fontWeight : "bold" } } > { t ( "chat:text.rooSaid" ) } </ span >
1103+ </ div >
1104+ < div
1105+ className = "cursor-pointer shrink-0 opacity-0 group-hover:opacity-30 hover:!opacity-100 transition-opacity"
1106+ style = { { visibility : isStreaming ? "hidden" : "visible" } }
1107+ onClick = { ( e ) => {
1108+ e . stopPropagation ( )
1109+ vscode . postMessage ( { type : "deleteMessage" , value : message . ts } )
1110+ } }
1111+ title = { t ( "chat:deleteMessageAndSubsequent" ) } >
1112+ < Trash2 className = "w-4 shrink-0" aria-label = "Delete message icon" />
1113+ </ div >
10681114 </ div >
1069- ) }
1070- </ div >
1071- </ div >
1115+ < div className = "pl-6" >
1116+ < Markdown markdown = { message . text } partial = { message . partial } />
1117+ { message . images && message . images . length > 0 && (
1118+ < div style = { { marginTop : "10px" } } >
1119+ { message . images . map ( ( image , index ) => (
1120+ < ImageBlock key = { index } imageData = { image } />
1121+ ) ) }
1122+ </ div >
1123+ ) }
1124+ </ div >
1125+ </ div >
1126+ </ DropdownMenuTrigger >
1127+ < DropdownMenuContent >
1128+ < DropdownMenuItem
1129+ onClick = { ( ) => vscode . postMessage ( { type : "deleteMessage" , value : message . ts } ) }
1130+ className = "flex items-center gap-2"
1131+ disabled = { isStreaming } >
1132+ < Trash2 className = "w-4 h-4" />
1133+ < span >
1134+ { t ( "chat:deleteMessageAndSubsequent" , "Delete message and subsequent nodes" ) }
1135+ </ span >
1136+ </ DropdownMenuItem >
1137+ </ DropdownMenuContent >
1138+ </ DropdownMenu >
10721139 )
10731140 case "user_feedback" :
10741141 return (
@@ -1162,15 +1229,42 @@ export const ChatRowContent = ({
11621229 return < ErrorRow type = "error" message = { message . text || "" } />
11631230 case "completion_result" :
11641231 return (
1165- < >
1166- < div style = { headerStyle } >
1167- { icon }
1168- { title }
1169- </ div >
1170- < div className = "border-l border-green-600/30 ml-2 pl-4 pb-1" >
1171- < Markdown markdown = { message . text } />
1172- </ div >
1173- </ >
1232+ < DropdownMenu >
1233+ < DropdownMenuTrigger asChild >
1234+ < div className = "group" onContextMenu = { ( e ) => e . preventDefault ( ) } >
1235+ < div style = { { ...headerStyle , justifyContent : "space-between" } } >
1236+ < div style = { { display : "flex" , alignItems : "center" , gap : "10px" } } >
1237+ { icon }
1238+ { title }
1239+ </ div >
1240+ < div
1241+ className = "cursor-pointer shrink-0 opacity-0 group-hover:opacity-30 hover:!opacity-100 transition-opacity"
1242+ style = { { visibility : isStreaming ? "hidden" : "visible" } }
1243+ onClick = { ( e ) => {
1244+ e . stopPropagation ( )
1245+ vscode . postMessage ( { type : "deleteMessage" , value : message . ts } )
1246+ } }
1247+ title = { t ( "chat:deleteMessageAndSubsequent" ) } >
1248+ < Trash2 className = "w-4 shrink-0" aria-label = "Delete message icon" />
1249+ </ div >
1250+ </ div >
1251+ < div className = "border-l border-green-600/30 ml-2 pl-4 pb-1" >
1252+ < Markdown markdown = { message . text } />
1253+ </ div >
1254+ </ div >
1255+ </ DropdownMenuTrigger >
1256+ < DropdownMenuContent >
1257+ < DropdownMenuItem
1258+ onClick = { ( ) => vscode . postMessage ( { type : "deleteMessage" , value : message . ts } ) }
1259+ className = "flex items-center gap-2"
1260+ disabled = { isStreaming } >
1261+ < Trash2 className = "w-4 h-4" />
1262+ < span >
1263+ { t ( "chat:deleteMessageAndSubsequent" , "Delete message and subsequent nodes" ) }
1264+ </ span >
1265+ </ DropdownMenuItem >
1266+ </ DropdownMenuContent >
1267+ </ DropdownMenu >
11741268 )
11751269 case "shell_integration_warning" :
11761270 return < CommandExecutionError />
0 commit comments