6
6
import 'vs/css!./inlineChatQuickVoice' ;
7
7
import { renderIcon } from 'vs/base/browser/ui/iconLabel/iconLabels' ;
8
8
import { Codicon } from 'vs/base/common/codicons' ;
9
- import { KeyCode } from 'vs/base/common/keyCodes' ;
9
+ import { KeyChord , KeyCode , KeyMod } from 'vs/base/common/keyCodes' ;
10
10
import { ContentWidgetPositionPreference , ICodeEditor , IContentWidget , IContentWidgetPosition } from 'vs/editor/browser/editorBrowser' ;
11
11
import { EditorAction2 } from 'vs/editor/browser/editorExtensions' ;
12
12
import { IEditorContribution } from 'vs/editor/common/editorCommon' ;
@@ -17,10 +17,11 @@ 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 { h , reset } from 'vs/base/browser/dom' ;
20
+ import { getWindow , h , reset , runAtThisOrScheduleAtNextAnimationFrame } 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
+ import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding' ;
24
25
25
26
const CTX_QUICK_CHAT_IN_PROGRESS = new RawContextKey < boolean > ( 'inlineChat.quickChatInProgress' , false ) ;
26
27
@@ -33,14 +34,29 @@ export class StartAction extends EditorAction2 {
33
34
category : AbstractInlineChatAction . category ,
34
35
precondition : ContextKeyExpr . and ( HasSpeechProvider , CTX_QUICK_CHAT_IN_PROGRESS . toNegated ( ) ) ,
35
36
f1 : true ,
36
- // keybinding: {
37
- // primary: KeyChord(KeyCode.F12 , KeyCode.F12 ),
38
- // weight: KeybindingWeight.WorkbenchContrib
39
- // }
37
+ keybinding : {
38
+ primary : KeyChord ( KeyMod . CtrlCmd | KeyCode . KeyK , KeyCode . KeyI ) ,
39
+ weight : KeybindingWeight . WorkbenchContrib + 100
40
+ }
40
41
} ) ;
41
42
}
42
43
43
- override runEditorCommand ( accessor : ServicesAccessor , editor : ICodeEditor , ...args : any [ ] ) {
44
+ override runEditorCommand ( accessor : ServicesAccessor , editor : ICodeEditor ) {
45
+ const keybindingService = accessor . get ( IKeybindingService ) ;
46
+
47
+ const holdMode = keybindingService . enableKeybindingHoldMode ( this . desc . id ) ;
48
+ if ( holdMode ) {
49
+ let shouldCallStop = false ;
50
+ const handle = setTimeout ( ( ) => {
51
+ shouldCallStop = true ;
52
+ } , 500 ) ;
53
+ holdMode . finally ( ( ) => {
54
+ clearTimeout ( handle ) ;
55
+ if ( shouldCallStop ) {
56
+ InlineChatQuickVoice . get ( editor ) ?. stop ( ) ;
57
+ }
58
+ } ) ;
59
+ }
44
60
InlineChatQuickVoice . get ( editor ) ?. start ( ) ;
45
61
}
46
62
}
@@ -55,13 +71,13 @@ export class StopAction extends EditorAction2 {
55
71
precondition : ContextKeyExpr . and ( HasSpeechProvider , CTX_QUICK_CHAT_IN_PROGRESS ) ,
56
72
f1 : true ,
57
73
keybinding : {
58
- primary : KeyCode . Escape ,
59
- weight : KeybindingWeight . WorkbenchContrib
74
+ primary : KeyChord ( KeyMod . CtrlCmd | KeyCode . KeyK , KeyCode . KeyI ) ,
75
+ weight : KeybindingWeight . WorkbenchContrib + 100
60
76
}
61
77
} ) ;
62
78
}
63
79
64
- override runEditorCommand ( accessor : ServicesAccessor , editor : ICodeEditor , ... args : any [ ] ) {
80
+ override runEditorCommand ( _accessor : ServicesAccessor , editor : ICodeEditor ) {
65
81
InlineChatQuickVoice . get ( editor ) ?. stop ( ) ;
66
82
}
67
83
}
@@ -93,12 +109,14 @@ class QuickVoiceWidget implements IContentWidget {
93
109
private readonly _domNode = document . createElement ( 'div' ) ;
94
110
private readonly _elements = h ( '.inline-chat-quick-voice@main' , [
95
111
h ( 'span@mic' ) ,
96
- h ( 'span@message' ) ,
112
+ h ( 'span.message @message' ) ,
97
113
] ) ;
98
114
99
115
constructor ( private readonly _editor : ICodeEditor ) {
100
116
this . _domNode . appendChild ( this . _elements . root ) ;
101
117
this . _domNode . style . zIndex = '1000' ;
118
+ this . _domNode . tabIndex = - 1 ;
119
+ this . _domNode . style . outline = 'none' ;
102
120
reset ( this . _elements . mic , renderIcon ( Codicon . micFilled ) ) ;
103
121
}
104
122
@@ -134,17 +152,22 @@ class QuickVoiceWidget implements IContentWidget {
134
152
135
153
// ---
136
154
137
- updateInput ( input : string | undefined ) : void {
155
+ updateInput ( input : string | undefined , isDefinite : boolean ) : void {
156
+ this . _elements . message . classList . toggle ( 'preview' , ! isDefinite ) ;
138
157
this . _elements . message . textContent = input ?? '' ;
139
158
}
140
159
160
+ focus ( ) : void {
161
+ this . _domNode . focus ( ) ;
162
+ }
163
+
141
164
active ( ) : void {
142
165
this . _elements . main . classList . add ( 'recording' ) ;
143
166
}
144
167
145
168
reset ( ) : void {
146
169
this . _elements . main . classList . remove ( 'recording' ) ;
147
- this . updateInput ( undefined ) ;
170
+ this . updateInput ( undefined , true ) ;
148
171
}
149
172
}
150
173
@@ -179,8 +202,11 @@ export class InlineChatQuickVoice implements IEditorContribution {
179
202
this . _editor . addContentWidget ( this . _widget ) ;
180
203
this . _ctxQuickChatInProgress . set ( true ) ;
181
204
182
- let message : string | undefined ;
205
+ runAtThisOrScheduleAtNextAnimationFrame ( getWindow ( this . _widget . getDomNode ( ) ) , ( ) => {
206
+ this . _widget . focus ( ) ; // requires RAF because...
207
+ } ) ;
183
208
209
+ let message : string | undefined ;
184
210
const session = this . _speechService . createSpeechToTextSession ( cts . token ) ;
185
211
const listener = session . onDidChange ( e => {
186
212
@@ -195,12 +221,11 @@ export class InlineChatQuickVoice implements IEditorContribution {
195
221
case SpeechToTextStatus . Stopped :
196
222
break ;
197
223
case SpeechToTextStatus . Recognizing :
198
- // TODO@jrieken special rendering for "in-flight" message?
199
- this . _widget . updateInput ( ! message ? e . text : `${ message } ${ e . text } ` ) ;
224
+ this . _widget . updateInput ( ! message ? e . text : `${ message } ${ e . text } ` , false ) ;
200
225
break ;
201
226
case SpeechToTextStatus . Recognized :
202
227
message = ! message ? e . text : `${ message } ${ e . text } ` ;
203
- this . _widget . updateInput ( message ) ;
228
+ this . _widget . updateInput ( message , true ) ;
204
229
break ;
205
230
}
206
231
} ) ;
0 commit comments