@@ -148,6 +148,22 @@ 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
+ terminal . attachCustomKeyEventHandler ( ( ev ) => {
156
+ if ( ev . shiftKey && ev . key === "Enter" ) {
157
+ if ( ev . type === "keydown" ) {
158
+ websocketRef . current ?. send (
159
+ new TextEncoder ( ) . encode ( JSON . stringify ( { data : "\x1b\r" } ) ) ,
160
+ ) ;
161
+ }
162
+ return false ;
163
+ }
164
+ return true ;
165
+ } ) ;
166
+
151
167
terminal . open ( terminalWrapperRef . current ) ;
152
168
153
169
// We have to fit twice here. It's unknown why, but the first fit will
@@ -190,6 +206,7 @@ const TerminalPage: FC = () => {
190
206
} , [ navigate , reconnectionToken , searchParams ] ) ;
191
207
192
208
// Hook up the terminal through a web socket.
209
+ const websocketRef = useRef < Websocket > ( ) ;
193
210
useEffect ( ( ) => {
194
211
if ( ! terminal ) {
195
212
return ;
@@ -270,6 +287,7 @@ const TerminalPage: FC = () => {
270
287
. withBackoff ( new ExponentialBackoff ( 1000 , 6 ) )
271
288
. build ( ) ;
272
289
websocket . binaryType = "arraybuffer" ;
290
+ websocketRef . current = websocket ;
273
291
websocket . addEventListener ( WebsocketEvent . open , ( ) => {
274
292
// Now that we are connected, allow user input.
275
293
terminal . options = {
@@ -333,6 +351,7 @@ const TerminalPage: FC = () => {
333
351
d . dispose ( ) ;
334
352
}
335
353
websocket ?. close ( 1000 ) ;
354
+ websocketRef . current = undefined ;
336
355
} ;
337
356
} , [
338
357
command ,
0 commit comments