@@ -148,6 +148,25 @@ const TerminalPage: FC = () => {
148
148
} ) ,
149
149
) ;
150
150
151
+ // Make shift+enter send ^[^M (escaped carriage return). Applications
152
+ // typically take this to mean to insert a literal newline. There is no way
153
+ // to remove this handler, so we must attach it once and rely on a ref to
154
+ // send it to the current socket.
155
+ const escapedCarriageReturn = "\x1b\r" ;
156
+ terminal . attachCustomKeyEventHandler ( ( ev ) => {
157
+ if ( ev . shiftKey && ev . key === "Enter" ) {
158
+ if ( ev . type === "keydown" ) {
159
+ websocketRef . current ?. send (
160
+ new TextEncoder ( ) . encode (
161
+ JSON . stringify ( { data : escapedCarriageReturn } ) ,
162
+ ) ,
163
+ ) ;
164
+ }
165
+ return false ;
166
+ }
167
+ return true ;
168
+ } ) ;
169
+
151
170
terminal . open ( terminalWrapperRef . current ) ;
152
171
153
172
// We have to fit twice here. It's unknown why, but the first fit will
@@ -190,6 +209,7 @@ const TerminalPage: FC = () => {
190
209
} , [ navigate , reconnectionToken , searchParams ] ) ;
191
210
192
211
// Hook up the terminal through a web socket.
212
+ const websocketRef = useRef < Websocket > ( ) ;
193
213
useEffect ( ( ) => {
194
214
if ( ! terminal ) {
195
215
return ;
@@ -270,6 +290,7 @@ const TerminalPage: FC = () => {
270
290
. withBackoff ( new ExponentialBackoff ( 1000 , 6 ) )
271
291
. build ( ) ;
272
292
websocket . binaryType = "arraybuffer" ;
293
+ websocketRef . current = websocket ;
273
294
websocket . addEventListener ( WebsocketEvent . open , ( ) => {
274
295
// Now that we are connected, allow user input.
275
296
terminal . options = {
@@ -333,6 +354,7 @@ const TerminalPage: FC = () => {
333
354
d . dispose ( ) ;
334
355
}
335
356
websocket ?. close ( 1000 ) ;
357
+ websocketRef . current = undefined ;
336
358
} ;
337
359
} , [
338
360
command ,
0 commit comments