@@ -17,11 +17,13 @@ import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegis
17
17
import { HasSpeechProvider , ISpeechService , SpeechToTextStatus } from 'vs/workbench/contrib/speech/common/speechService' ;
18
18
import { CancellationTokenSource } from 'vs/base/common/cancellation' ;
19
19
import { InlineChatController } from 'vs/workbench/contrib/inlineChat/browser/inlineChatController' ;
20
- import { getWindow , h , reset , runAtThisOrScheduleAtNextAnimationFrame } from 'vs/base/browser/dom' ;
20
+ import * as dom from 'vs/base/browser/dom' ;
21
21
import { IDimension } from 'vs/editor/common/core/dimension' ;
22
22
import { EditorOption } from 'vs/editor/common/config/editorOptions' ;
23
23
import { AbstractInlineChatAction } from 'vs/workbench/contrib/inlineChat/browser/inlineChatActions' ;
24
24
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding' ;
25
+ import { Emitter , Event } from 'vs/base/common/event' ;
26
+ import { DisposableStore } from 'vs/base/common/lifecycle' ;
25
27
26
28
const CTX_QUICK_CHAT_IN_PROGRESS = new RawContextKey < boolean > ( 'inlineChat.quickChatInProgress' , false ) ;
27
29
@@ -107,17 +109,27 @@ class QuickVoiceWidget implements IContentWidget {
107
109
readonly suppressMouseDown = true ;
108
110
109
111
private readonly _domNode = document . createElement ( 'div' ) ;
110
- private readonly _elements = h ( '.inline-chat-quick-voice@main' , [
111
- h ( 'span@mic' ) ,
112
- h ( 'span.message@message' ) ,
112
+ private readonly _elements = dom . h ( '.inline-chat-quick-voice@main' , [
113
+ dom . h ( 'span@mic' ) ,
114
+ dom . h ( 'span.message@message' ) ,
113
115
] ) ;
114
116
117
+ private _focusTracker : dom . IFocusTracker | undefined ;
118
+
119
+ private readonly _onDidBlur = new Emitter < void > ( ) ;
120
+ readonly onDidBlur : Event < void > = this . _onDidBlur . event ;
121
+
115
122
constructor ( private readonly _editor : ICodeEditor ) {
116
123
this . _domNode . appendChild ( this . _elements . root ) ;
117
124
this . _domNode . style . zIndex = '1000' ;
118
125
this . _domNode . tabIndex = - 1 ;
119
126
this . _domNode . style . outline = 'none' ;
120
- reset ( this . _elements . mic , renderIcon ( Codicon . micFilled ) ) ;
127
+ dom . reset ( this . _elements . mic , renderIcon ( Codicon . micFilled ) ) ;
128
+ }
129
+
130
+ dispose ( ) : void {
131
+ this . _focusTracker ?. dispose ( ) ;
132
+ this . _onDidBlur . dispose ( ) ;
121
133
}
122
134
123
135
getId ( ) : string {
@@ -150,24 +162,33 @@ class QuickVoiceWidget implements IContentWidget {
150
162
return null ;
151
163
}
152
164
165
+ afterRender ( ) : void {
166
+ this . _domNode . focus ( ) ;
167
+ this . _focusTracker ?. dispose ( ) ;
168
+ this . _focusTracker = dom . trackFocus ( this . _domNode ) ;
169
+ this . _focusTracker . onDidBlur ( ( ) => this . _onDidBlur . fire ( ) ) ;
170
+ }
171
+
153
172
// ---
154
173
155
174
updateInput ( input : string | undefined , isDefinite : boolean ) : void {
156
175
this . _elements . message . classList . toggle ( 'preview' , ! isDefinite ) ;
157
176
this . _elements . message . textContent = input ?? '' ;
158
177
}
159
178
160
- focus ( ) : void {
161
- this . _domNode . focus ( ) ;
179
+ show ( ) {
180
+ this . _editor . addContentWidget ( this ) ;
162
181
}
163
182
164
183
active ( ) : void {
165
184
this . _elements . main . classList . add ( 'recording' ) ;
166
185
}
167
186
168
- reset ( ) : void {
187
+ hide ( ) {
169
188
this . _elements . main . classList . remove ( 'recording' ) ;
170
189
this . updateInput ( undefined , true ) ;
190
+ this . _editor . removeContentWidget ( this ) ;
191
+ this . _focusTracker ?. dispose ( ) ;
171
192
}
172
193
}
173
194
@@ -179,6 +200,7 @@ export class InlineChatQuickVoice implements IEditorContribution {
179
200
return editor . getContribution < InlineChatQuickVoice > ( InlineChatQuickVoice . ID ) ;
180
201
}
181
202
203
+ private readonly _store = new DisposableStore ( ) ;
182
204
private readonly _ctxQuickChatInProgress : IContextKey < boolean > ;
183
205
private readonly _widget : QuickVoiceWidget ;
184
206
private _finishCallback ?: ( abort : boolean ) => void ;
@@ -188,24 +210,25 @@ export class InlineChatQuickVoice implements IEditorContribution {
188
210
@ISpeechService private readonly _speechService : ISpeechService ,
189
211
@IContextKeyService contextKeyService : IContextKeyService ,
190
212
) {
191
- this . _widget = new QuickVoiceWidget ( this . _editor ) ;
213
+ this . _widget = this . _store . add ( new QuickVoiceWidget ( this . _editor ) ) ;
214
+ this . _widget . onDidBlur ( ( ) => this . _finishCallback ?.( true ) , undefined , this . _store ) ;
192
215
this . _ctxQuickChatInProgress = CTX_QUICK_CHAT_IN_PROGRESS . bindTo ( contextKeyService ) ;
193
216
}
194
217
195
218
dispose ( ) : void {
196
219
this . _finishCallback ?.( true ) ;
220
+ this . _ctxQuickChatInProgress . reset ( ) ;
221
+ this . _store . dispose ( ) ;
197
222
}
198
223
199
224
start ( ) {
200
225
226
+ this . _finishCallback ?.( true ) ;
227
+
201
228
const cts = new CancellationTokenSource ( ) ;
202
- this . _editor . addContentWidget ( this . _widget ) ;
229
+ this . _widget . show ( ) ;
203
230
this . _ctxQuickChatInProgress . set ( true ) ;
204
231
205
- runAtThisOrScheduleAtNextAnimationFrame ( getWindow ( this . _widget . getDomNode ( ) ) , ( ) => {
206
- this . _widget . focus ( ) ; // requires RAF because...
207
- } ) ;
208
-
209
232
let message : string | undefined ;
210
233
const session = this . _speechService . createSpeechToTextSession ( cts . token ) ;
211
234
const listener = session . onDidChange ( e => {
@@ -233,8 +256,7 @@ export class InlineChatQuickVoice implements IEditorContribution {
233
256
const done = ( abort : boolean ) => {
234
257
cts . dispose ( true ) ;
235
258
listener . dispose ( ) ;
236
- this . _widget . reset ( ) ;
237
- this . _editor . removeContentWidget ( this . _widget ) ;
259
+ this . _widget . hide ( ) ;
238
260
this . _ctxQuickChatInProgress . reset ( ) ;
239
261
240
262
if ( ! abort && message ) {
@@ -252,5 +274,4 @@ export class InlineChatQuickVoice implements IEditorContribution {
252
274
cancel ( ) : void {
253
275
this . _finishCallback ?.( true ) ;
254
276
}
255
-
256
277
}
0 commit comments