Skip to content

Commit 69295d9

Browse files
committed
clean up
1 parent 1f23aa6 commit 69295d9

File tree

5 files changed

+74
-35
lines changed

5 files changed

+74
-35
lines changed

packages/module/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@
3535
"tag": "prerelease"
3636
},
3737
"dependencies": {
38-
"@novnc/novnc": "^1.5.0",
38+
"@novnc/novnc": "1.5.0",
3939
"@patternfly/react-core": "^6.0.0",
4040
"@patternfly/react-styles": "^6.0.0",
4141
"@spice-project/spice-html5": "^0.2.1",

packages/module/patternfly-docs/content/extensions/react-console/examples/SerialConsoleCustom.jsx

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { useState, useRef, createRef } from 'react';
1+
import React, { useState, useRef, createRef, useCallback } from 'react';
22
import { debounce } from '@patternfly/react-core';
33
import { SerialConsole } from '@patternfly/react-console';
44

@@ -7,16 +7,24 @@ export const SerialConsoleCustom = () => {
77
const setConnected = useRef(debounce(() => setStatus('connected'), 3000)).current;
88
const ref2 = createRef();
99

10+
const onConnect = useCallback(() => {
11+
setStatus('loading');
12+
setConnected();
13+
}, [setConnected]);
14+
15+
const onDisconnect = useCallback(() => {
16+
setStatus('disconnected');
17+
}, []);
18+
19+
const onData = useCallback((data) => {
20+
ref2.current?.onDataReceived?.(data);
21+
}, []);
22+
1023
return (
1124
<SerialConsole
12-
onConnect={() => {
13-
setStatus('loading');
14-
setConnected();
15-
}}
16-
onDisconnect={() => setStatus('disconnected')}
17-
onData={data => {
18-
ref2.current.onDataReceived(data);
19-
}}
25+
onConnect={onConnect}
26+
onDisconnect={onDisconnect}
27+
onData={onData}
2028
status={status}
2129
ref={ref2}
2230
/>

packages/module/src/components/SerialConsole/XTerm.tsx

Lines changed: 34 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,11 @@ export const XTerm: React.FunctionComponent<XTermProps> = ({
8080
}, [onBeforeUnload]);
8181

8282
useEffect(() => {
83+
const container = ref.current;
84+
if (!container) {
85+
return;
86+
}
87+
8388
const fitAddon = new FitAddon();
8489
terminalRef.current = new Terminal({
8590
cols,
@@ -92,8 +97,8 @@ export const XTerm: React.FunctionComponent<XTermProps> = ({
9297

9398
const onWindowResize = () => {
9499
const geometry = fitAddon.proposeDimensions();
95-
if (geometry) {
96-
terminalRef.current.resize(geometry.rows, geometry.cols);
100+
if (geometry && terminalRef.current) {
101+
terminalRef.current.resize(geometry.cols, geometry.rows);
97102
}
98103
};
99104

@@ -107,29 +112,45 @@ export const XTerm: React.FunctionComponent<XTermProps> = ({
107112

108113
terminalRef.current.loadAddon(fitAddon);
109114

110-
terminalRef.current.open(ref.current);
115+
let resizeListener: (() => void) | null = null;
111116

112-
const resizeListener = debounce(onWindowResize, 100);
113-
114-
if (!rows) {
115-
if (canUseDOM) {
117+
// Defer open so the container is laid out and xterm's renderer has valid dimensions
118+
// before Viewport._innerRefresh runs (avoids "Cannot read properties of undefined (reading 'dimensions')")
119+
const rafId = requestAnimationFrame(() => {
120+
if (!container.isConnected || !terminalRef.current) {
121+
return;
122+
}
123+
terminalRef.current.open(container);
124+
resizeListener = !rows ? debounce(onWindowResize, 100) : null;
125+
if (resizeListener && canUseDOM) {
116126
window.addEventListener('resize', resizeListener);
117127
}
118-
onWindowResize();
119-
}
120-
terminalRef.current.focus();
128+
if (!rows) {
129+
onWindowResize();
130+
}
131+
terminalRef.current?.focus();
132+
});
121133

122134
return () => {
123-
terminalRef.current.dispose();
124-
if (canUseDOM) {
135+
cancelAnimationFrame(rafId);
136+
if (resizeListener && canUseDOM) {
125137
window.removeEventListener('resize', resizeListener);
126138
}
139+
terminalRef.current?.dispose();
140+
terminalRef.current = null;
127141
onFocusOut();
128142
};
129143
}, [cols, fontFamily, fontSize, onData, onFocusOut, onTitleChanged, rows]);
130144

131145
// ensure react never reuses this div by keying it with the terminal widget
132146
// Workaround for xtermjs/xterm.js#3172
133-
return <div ref={ref} role="list" onFocus={onFocusIn} onBlur={onFocusOut} />;
147+
return (
148+
<div
149+
ref={ref}
150+
role="list"
151+
onFocus={onFocusIn}
152+
onBlur={onFocusOut}
153+
/>
154+
);
134155
};
135156
XTerm.displayName = 'XTerm';

packages/module/src/components/VncConsole/VncConsole.tsx

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@ export const VncConsole: React.FunctionComponent<VncConsoleProps> = ({
9494
textCtrlAltDel
9595
}) => {
9696
const rfb = useRef<any>(null);
97+
const rfbDisconnectedRef = useRef(false);
9798

9899
const novncElem = useRef<HTMLDivElement>(null);
99100
const [status, setStatus] = useState(CONNECTING);
@@ -104,6 +105,7 @@ export const VncConsole: React.FunctionComponent<VncConsoleProps> = ({
104105

105106
const _onDisconnected = useCallback(
106107
(e: any) => {
108+
rfbDisconnectedRef.current = true;
107109
setStatus(DISCONNECTED);
108110
onDisconnected(e);
109111
},
@@ -141,6 +143,10 @@ export const VncConsole: React.FunctionComponent<VncConsoleProps> = ({
141143
}, [rfb, _onDisconnected, _onSecurityFailure]);
142144

143145
const connect = useCallback(() => {
146+
if (!novncElem.current) {
147+
return;
148+
}
149+
rfbDisconnectedRef.current = false;
144150
const protocol = encrypt ? 'wss' : 'ws';
145151
const url = `${protocol}://${host}:${port}/${path}`;
146152

@@ -175,22 +181,26 @@ export const VncConsole: React.FunctionComponent<VncConsoleProps> = ({
175181

176182
useEffect(() => {
177183
initLogging(vncLogging);
178-
try {
179-
connect();
180-
} catch (e) {
181-
onInitFailed && onInitFailed(e);
182-
rfb.current = undefined;
183-
}
184+
// Defer RFB creation so the container is laid out and has dimensions (avoids noVNC "dimensions" undefined)
185+
const rafId = requestAnimationFrame(() => {
186+
try {
187+
connect();
188+
} catch (e) {
189+
onInitFailed && onInitFailed(e);
190+
rfb.current = undefined;
191+
}
192+
});
184193

185194
return () => {
195+
cancelAnimationFrame(rafId);
186196
disconnect();
187197
removeEventListeners();
188198
rfb.current = undefined;
189199
};
190200
}, [connect, onInitFailed, removeEventListeners, vncLogging]);
191201

192202
const disconnect = () => {
193-
if (!rfb.current) {
203+
if (!rfb.current || rfbDisconnectedRef.current) {
194204
return;
195205
}
196206
rfb.current.disconnect();

yarn.lock

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3432,10 +3432,10 @@ __metadata:
34323432
languageName: node
34333433
linkType: hard
34343434

3435-
"@novnc/novnc@npm:^1.5.0":
3436-
version: 1.6.0
3437-
resolution: "@novnc/novnc@npm:1.6.0"
3438-
checksum: 10c0/b014445548b769e66e33f0f60ec6a1676aeff9cad509c371b4fa6b00b496a8b8a9df15707411230df72ed205d819eca08c6555b67717a805fb8043045c831d08
3435+
"@novnc/novnc@npm:1.5.0":
3436+
version: 1.5.0
3437+
resolution: "@novnc/novnc@npm:1.5.0"
3438+
checksum: 10c0/811a1b3b6a0bfbef3092babb8b1b1febc0273c79abd1902231558194d64125d5fa7f36219e45d94b78fbae6f5444bc39637909f4aa7d96691479dca27b9052c9
34393439
languageName: node
34403440
linkType: hard
34413441

@@ -3636,7 +3636,7 @@ __metadata:
36363636
version: 0.0.0-use.local
36373637
resolution: "@patternfly/react-console@workspace:packages/module"
36383638
dependencies:
3639-
"@novnc/novnc": "npm:^1.5.0"
3639+
"@novnc/novnc": "npm:1.5.0"
36403640
"@patternfly/documentation-framework": "npm:6.0.0-alpha.108"
36413641
"@patternfly/patternfly": "npm:^6.0.0"
36423642
"@patternfly/patternfly-a11y": "npm:^4.3.1"

0 commit comments

Comments
 (0)