@@ -24,6 +24,7 @@ import BrowserSessionRow from "./BrowserSessionRow"
24
24
import ChatRow from "./ChatRow"
25
25
import ChatTextArea from "./ChatTextArea"
26
26
import TaskHeader from "./TaskHeader"
27
+ import { AudioType } from "../../../../src/shared/WebviewMessage"
27
28
28
29
interface ChatViewProps {
29
30
isHidden : boolean
@@ -61,10 +62,24 @@ const ChatView = ({ isHidden, showAnnouncement, hideAnnouncement, showHistoryVie
61
62
const [ showScrollToBottom , setShowScrollToBottom ] = useState ( false )
62
63
const [ isAtBottom , setIsAtBottom ] = useState ( false )
63
64
65
+ const [ wasStreaming , setWasStreaming ] = useState < boolean > ( false )
66
+ const [ hasStarted , setHasStarted ] = useState ( false )
67
+
64
68
// UI layout depends on the last 2 messages
65
69
// (since it relies on the content of these messages, we are deep comparing. i.e. the button state after hitting button sets enableButtons to false, and this effect otherwise would have to true again even if messages didn't change
66
70
const lastMessage = useMemo ( ( ) => messages . at ( - 1 ) , [ messages ] )
67
71
const secondLastMessage = useMemo ( ( ) => messages . at ( - 2 ) , [ messages ] )
72
+
73
+ function playSound ( audioType : AudioType ) {
74
+ vscode . postMessage ( { type : "playSound" , audioType } )
75
+ }
76
+
77
+ function playSoundOnMessage ( audioType : AudioType ) {
78
+ if ( hasStarted && ! isStreaming ) {
79
+ playSound ( audioType )
80
+ }
81
+ }
82
+
68
83
useDeepCompareEffect ( ( ) => {
69
84
// if last message is an ask, show user ask UI
70
85
// if user finished a task, then start a new task with a new conversation history since in this moment that the extension is waiting for user response, the user could close the extension and the conversation history would be lost.
@@ -75,27 +90,31 @@ const ChatView = ({ isHidden, showAnnouncement, hideAnnouncement, showHistoryVie
75
90
const isPartial = lastMessage . partial === true
76
91
switch ( lastMessage . ask ) {
77
92
case "api_req_failed" :
93
+ playSoundOnMessage ( "progress_loop" )
78
94
setTextAreaDisabled ( true )
79
95
setClineAsk ( "api_req_failed" )
80
96
setEnableButtons ( true )
81
97
setPrimaryButtonText ( "Retry" )
82
98
setSecondaryButtonText ( "Start New Task" )
83
99
break
84
100
case "mistake_limit_reached" :
101
+ playSoundOnMessage ( "progress_loop" )
85
102
setTextAreaDisabled ( false )
86
103
setClineAsk ( "mistake_limit_reached" )
87
104
setEnableButtons ( true )
88
105
setPrimaryButtonText ( "Proceed Anyways" )
89
106
setSecondaryButtonText ( "Start New Task" )
90
107
break
91
108
case "followup" :
109
+ playSoundOnMessage ( "notification" )
92
110
setTextAreaDisabled ( isPartial )
93
111
setClineAsk ( "followup" )
94
112
setEnableButtons ( isPartial )
95
113
// setPrimaryButtonText(undefined)
96
114
// setSecondaryButtonText(undefined)
97
115
break
98
116
case "tool" :
117
+ playSoundOnMessage ( "notification" )
99
118
setTextAreaDisabled ( isPartial )
100
119
setClineAsk ( "tool" )
101
120
setEnableButtons ( ! isPartial )
@@ -113,20 +132,23 @@ const ChatView = ({ isHidden, showAnnouncement, hideAnnouncement, showHistoryVie
113
132
}
114
133
break
115
134
case "browser_action_launch" :
135
+ playSoundOnMessage ( "notification" )
116
136
setTextAreaDisabled ( isPartial )
117
137
setClineAsk ( "browser_action_launch" )
118
138
setEnableButtons ( ! isPartial )
119
139
setPrimaryButtonText ( "Approve" )
120
140
setSecondaryButtonText ( "Reject" )
121
141
break
122
142
case "command" :
143
+ playSoundOnMessage ( "notification" )
123
144
setTextAreaDisabled ( isPartial )
124
145
setClineAsk ( "command" )
125
146
setEnableButtons ( ! isPartial )
126
147
setPrimaryButtonText ( "Run Command" )
127
148
setSecondaryButtonText ( "Reject" )
128
149
break
129
150
case "command_output" :
151
+ playSoundOnMessage ( "notification" )
130
152
setTextAreaDisabled ( false )
131
153
setClineAsk ( "command_output" )
132
154
setEnableButtons ( true )
@@ -135,13 +157,15 @@ const ChatView = ({ isHidden, showAnnouncement, hideAnnouncement, showHistoryVie
135
157
break
136
158
case "completion_result" :
137
159
// extension waiting for feedback. but we can just present a new task button
160
+ playSoundOnMessage ( "celebration" )
138
161
setTextAreaDisabled ( isPartial )
139
162
setClineAsk ( "completion_result" )
140
163
setEnableButtons ( ! isPartial )
141
164
setPrimaryButtonText ( "Start New Task" )
142
165
setSecondaryButtonText ( undefined )
143
166
break
144
167
case "resume_task" :
168
+ playSoundOnMessage ( "notification" )
145
169
setTextAreaDisabled ( false )
146
170
setClineAsk ( "resume_task" )
147
171
setEnableButtons ( true )
@@ -150,6 +174,7 @@ const ChatView = ({ isHidden, showAnnouncement, hideAnnouncement, showHistoryVie
150
174
setDidClickCancel ( false ) // special case where we reset the cancel button state
151
175
break
152
176
case "resume_completed_task" :
177
+ playSoundOnMessage ( "celebration" )
153
178
setTextAreaDisabled ( false )
154
179
setClineAsk ( "resume_completed_task" )
155
180
setEnableButtons ( true )
@@ -441,6 +466,36 @@ const ChatView = ({ isHidden, showAnnouncement, hideAnnouncement, showHistoryVie
441
466
return true
442
467
} )
443
468
} , [ modifiedMessages ] )
469
+ useEffect ( ( ) => {
470
+ if ( isStreaming ) {
471
+ // Set to true once any request has started
472
+ setHasStarted ( true )
473
+ }
474
+ // Only execute when isStreaming changes from true to false
475
+ if ( wasStreaming && ! isStreaming && lastMessage ) {
476
+ // Play appropriate sound based on lastMessage content
477
+ if ( lastMessage . type === "ask" ) {
478
+ switch ( lastMessage . ask ) {
479
+ case "api_req_failed" :
480
+ case "mistake_limit_reached" :
481
+ playSound ( "progress_loop" )
482
+ break
483
+ case "tool" :
484
+ case "followup" :
485
+ case "browser_action_launch" :
486
+ case "resume_task" :
487
+ playSound ( "notification" )
488
+ break
489
+ case "completion_result" :
490
+ case "resume_completed_task" :
491
+ playSound ( "celebration" )
492
+ break
493
+ }
494
+ }
495
+ }
496
+ // Update previous value
497
+ setWasStreaming ( isStreaming )
498
+ } , [ isStreaming , lastMessage ] )
444
499
445
500
const isBrowserSessionMessage = ( message : ClineMessage ) : boolean => {
446
501
// which of visible messages are browser session messages, see above
0 commit comments