Skip to content

Commit 4af8446

Browse files
authored
fix: initialize terminal with correct size (#10369)
* Fit once during creation This does not fix any bugs (that I know of) but we only need to fit once when the terminal is created, not every time we reconnect. Granted, currently we do not support reconnecting without refreshing anyway so it does not really matter, but this just seems more correct. Plus now we will not have to pass the fit addon around. * Pass size when connecting web socket URL I think this will solve an issue where screen does does not correctly handle an immediate resize. It seems to ignore the resize, but even if you send it again nothing changes, seemingly thinking it is already at that size? * Use new struct for decoding reconnecting pty requests Decoding a JSON message does not touch omitted (or null) fields so once a message with a resize comes in, every single message from that point will cause a resize. I am not sure if this is an actual problem in practice but at the very least it seems unintentional.
1 parent 1286904 commit 4af8446

File tree

3 files changed

+21
-16
lines changed

3 files changed

+21
-16
lines changed

agent/reconnectingpty/reconnectingpty.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -196,8 +196,8 @@ func (s *ptyState) waitForStateOrContext(ctx context.Context, state State) (Stat
196196
// until EOF or an error writing to ptty or reading from conn.
197197
func readConnLoop(ctx context.Context, conn net.Conn, ptty pty.PTYCmd, metrics *prometheus.CounterVec, logger slog.Logger) {
198198
decoder := json.NewDecoder(conn)
199-
var req codersdk.ReconnectingPTYRequest
200199
for {
200+
var req codersdk.ReconnectingPTYRequest
201201
err := decoder.Decode(&req)
202202
if xerrors.Is(err, io.EOF) {
203203
return

site/src/pages/TerminalPage/TerminalPage.tsx

Lines changed: 16 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,6 @@ const TerminalPage: FC = () => {
5454
const [terminalState, setTerminalState] = useState<
5555
"connected" | "disconnected" | "initializing"
5656
>("initializing");
57-
const [fitAddon, setFitAddon] = useState<FitAddon | null>(null);
5857
const [searchParams] = useSearchParams();
5958
// The reconnection token is a unique token that identifies
6059
// a terminal session. It's generated by the client to reduce
@@ -125,7 +124,6 @@ const TerminalPage: FC = () => {
125124
terminal.loadAddon(new CanvasAddon());
126125
}
127126
const fitAddon = new FitAddon();
128-
setFitAddon(fitAddon);
129127
terminal.loadAddon(fitAddon);
130128
terminal.loadAddon(new Unicode11Addon());
131129
terminal.unicode.activeVersion = "11";
@@ -134,13 +132,21 @@ const TerminalPage: FC = () => {
134132
handleWebLinkRef.current(uri);
135133
}),
136134
);
137-
setTerminal(terminal);
135+
138136
terminal.open(xtermRef.current);
139-
const listener = () => {
140-
// This will trigger a resize event on the terminal.
141-
fitAddon.fit();
142-
};
137+
138+
// We have to fit twice here. It's unknown why, but the first fit will
139+
// overflow slightly in some scenarios. Applying a second fit resolves this.
140+
fitAddon.fit();
141+
fitAddon.fit();
142+
143+
// This will trigger a resize event on the terminal.
144+
const listener = () => fitAddon.fit();
143145
window.addEventListener("resize", listener);
146+
147+
// Terminal is correctly sized and is ready to be used.
148+
setTerminal(terminal);
149+
144150
return () => {
145151
window.removeEventListener("resize", listener);
146152
terminal.dispose();
@@ -165,16 +171,10 @@ const TerminalPage: FC = () => {
165171

166172
// Hook up the terminal through a web socket.
167173
useEffect(() => {
168-
if (!terminal || !fitAddon) {
174+
if (!terminal) {
169175
return;
170176
}
171177

172-
// We have to fit twice here. It's unknown why, but
173-
// the first fit will overflow slightly in some
174-
// scenarios. Applying a second fit resolves this.
175-
fitAddon.fit();
176-
fitAddon.fit();
177-
178178
// The terminal should be cleared on each reconnect
179179
// because all data is re-rendered from the backend.
180180
terminal.clear();
@@ -229,6 +229,8 @@ const TerminalPage: FC = () => {
229229
reconnectionToken,
230230
workspaceAgent.id,
231231
command,
232+
terminal.rows,
233+
terminal.cols,
232234
)
233235
.then((url) => {
234236
if (disposed) {
@@ -289,7 +291,6 @@ const TerminalPage: FC = () => {
289291
};
290292
}, [
291293
command,
292-
fitAddon,
293294
proxy.preferredPathAppURL,
294295
reconnectionToken,
295296
terminal,

site/src/utils/terminal.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,15 @@ export const terminalWebsocketUrl = async (
55
reconnect: string,
66
agentId: string,
77
command: string | undefined,
8+
height: number,
9+
width: number,
810
): Promise<string> => {
911
const query = new URLSearchParams({ reconnect });
1012
if (command) {
1113
query.set("command", command);
1214
}
15+
query.set("height", height.toString());
16+
query.set("width", width.toString());
1317

1418
const url = new URL(baseUrl || `${location.protocol}//${location.host}`);
1519
url.protocol = url.protocol === "https:" ? "wss:" : "ws:";

0 commit comments

Comments
 (0)