Skip to content

Commit 5fd3af9

Browse files
committed
feat: support shift+enter in terminal
It acts the same alt+enter, but is more familiar to users.
1 parent 28789d7 commit 5fd3af9

File tree

2 files changed

+37
-0
lines changed

2 files changed

+37
-0
lines changed

site/src/pages/TerminalPage/TerminalPage.test.tsx

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import "jest-canvas-mock";
22
import { waitFor } from "@testing-library/react";
3+
import userEvent from "@testing-library/user-event";
34
import { API } from "api/api";
45
import WS from "jest-websocket-mock";
56
import { http, HttpResponse } from "msw";
@@ -148,4 +149,21 @@ describe("TerminalPage", () => {
148149
ws.send(text);
149150
await expectTerminalText(container, text);
150151
});
152+
153+
it("supports shift+enter", async () => {
154+
const ws = new WS(
155+
`ws://localhost/api/v2/workspaceagents/${MockWorkspaceAgent.id}/pty`,
156+
);
157+
158+
const { container } = await renderTerminal();
159+
// Ideally we could use ws.connected but that seems to pause React updates.
160+
// For now, wait for the initial resize message instead.
161+
await ws.nextMessage;
162+
163+
// Type out the shift+enter.
164+
await userEvent.type(container, "{Shift}{Enter}");
165+
const msg = await ws.nextMessage;
166+
const req = JSON.parse(new TextDecoder().decode(msg as Uint8Array));
167+
expect(req.data).toBe("\x1b\r");
168+
});
151169
});

site/src/pages/TerminalPage/TerminalPage.tsx

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,22 @@ const TerminalPage: FC = () => {
148148
}),
149149
);
150150

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+
151167
terminal.open(terminalWrapperRef.current);
152168

153169
// We have to fit twice here. It's unknown why, but the first fit will
@@ -190,6 +206,7 @@ const TerminalPage: FC = () => {
190206
}, [navigate, reconnectionToken, searchParams]);
191207

192208
// Hook up the terminal through a web socket.
209+
const websocketRef = useRef<Websocket>();
193210
useEffect(() => {
194211
if (!terminal) {
195212
return;
@@ -270,6 +287,7 @@ const TerminalPage: FC = () => {
270287
.withBackoff(new ExponentialBackoff(1000, 6))
271288
.build();
272289
websocket.binaryType = "arraybuffer";
290+
websocketRef.current = websocket;
273291
websocket.addEventListener(WebsocketEvent.open, () => {
274292
// Now that we are connected, allow user input.
275293
terminal.options = {
@@ -333,6 +351,7 @@ const TerminalPage: FC = () => {
333351
d.dispose();
334352
}
335353
websocket?.close(1000);
354+
websocketRef.current = undefined;
336355
};
337356
}, [
338357
command,

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy