debug: add logging and simplify fit logic

- Simplified fit logic: just fit after open, after fonts load, and on resize
- Added console logging to debug what FitAddon calculates
- Single fitTerminal() function used everywhere
- Removed complex retry logic that wasn't working
This commit is contained in:
Fusion
2026-05-24 15:30:34 +02:00
parent fa17b13413
commit fed49dba6d
+25 -68
View File
@@ -201,74 +201,35 @@ export const TerminalComponent: React.FC<TerminalProps> = ({
term.loadAddon(fitAddon); term.loadAddon(fitAddon);
term.loadAddon(new WebLinksAddon()); term.loadAddon(new WebLinksAddon());
// Wait for container to have non-zero dimensions before opening xterm
// xterm docs: "parent must be visible (have dimensions) when open is called"
const container = terminalRef.current; const container = terminalRef.current;
let ws: WebSocket; let ws: WebSocket;
const initTerminal = () => { // Open xterm immediately
term.open(container); term.open(container);
// Connect WebSocket
ws = connectWebSocket(); ws = connectWebSocket();
// Robust fitting: wait for fonts and stable dimensions // Fit terminal and notify backend
const performFit = () => { const fitTerminal = () => {
fitAddon.fit(); if (!fitAddonRef.current || !termRef.current) return;
const { cols, rows } = term; fitAddonRef.current.fit();
const { cols, rows } = termRef.current;
console.log(`[Terminal] Fitted: ${cols}x${rows}, container: ${container.clientWidth}x${container.clientHeight}`);
if (ws.readyState === WebSocket.OPEN) { if (ws.readyState === WebSocket.OPEN) {
ws.send( ws.send(JSON.stringify({ type: "resize", cols, rows }));
JSON.stringify({
type: "resize",
cols,
rows,
})
);
}
return { cols, rows };
};
const ensureProperFit = (attempt = 0) => {
const { cols, rows } = performFit();
// If dimensions are unreasonably small, retry (layout still settling)
if ((rows <= 1 || cols <= 10) && attempt < 30) {
setTimeout(() => ensureProperFit(attempt + 1), 100);
} }
}; };
// Wait for fonts to load, then fit with retry logic // Initial fit after layout settles
requestAnimationFrame(() => {
requestAnimationFrame(() => {
fitTerminal();
});
});
// Refit after font load (metrics may change)
document.fonts.ready.then(() => { document.fonts.ready.then(() => {
requestAnimationFrame(() => { requestAnimationFrame(() => fitTerminal());
requestAnimationFrame(() => {
ensureProperFit();
}); });
});
});
// Fit after mobile header auto-hides (3s delay + 0.3s transition)
setTimeout(() => {
performFit();
}, 4000);
};
// Check if container already has dimensions
const rect = container.getBoundingClientRect();
if (rect.width > 0 && rect.height > 0) {
initTerminal();
} else {
// Wait for container to be laid out
const resizeObserver = new ResizeObserver((entries) => {
for (const entry of entries) {
const { width, height } = entry.contentRect;
if (width > 0 && height > 0) {
resizeObserver.disconnect();
initTerminal();
break;
}
}
});
resizeObserver.observe(container);
}
// Handle terminal input // Handle terminal input
term.onData((data) => { term.onData((data) => {
@@ -288,27 +249,22 @@ export const TerminalComponent: React.FC<TerminalProps> = ({
ws.send(data); ws.send(data);
}); });
// Handle resize with debounce // Handle window resize with debounce
let resizeTimeout: ReturnType<typeof setTimeout>; let resizeTimeout: ReturnType<typeof setTimeout>;
const handleResize = () => { const handleResize = () => {
clearTimeout(resizeTimeout); clearTimeout(resizeTimeout);
resizeTimeout = setTimeout(() => { resizeTimeout = setTimeout(() => {
fitAddon.fit(); fitTerminal();
const { cols, rows } = term;
if (ws.readyState === WebSocket.OPEN) {
ws.send(
JSON.stringify({
type: "resize",
cols,
rows,
})
);
}
}, 250); }, 250);
}; };
window.addEventListener("resize", handleResize); window.addEventListener("resize", handleResize);
// Refit after mobile header auto-hides (3s delay + 0.3s transition)
const headerHideTimeout = setTimeout(() => {
fitTerminal();
}, 4000);
// Notify parent about terminal readiness // Notify parent about terminal readiness
if (onTerminalReadyRef.current) { if (onTerminalReadyRef.current) {
const sendData = (data: string) => { const sendData = (data: string) => {
@@ -336,6 +292,7 @@ export const TerminalComponent: React.FC<TerminalProps> = ({
return () => { return () => {
clearTimeout(resizeTimeout); clearTimeout(resizeTimeout);
clearTimeout(headerHideTimeout);
window.removeEventListener("resize", handleResize); window.removeEventListener("resize", handleResize);
document.removeEventListener("visibilitychange", handleVisibilityChange); document.removeEventListener("visibilitychange", handleVisibilityChange);
if (ws) { if (ws) {