Fix custom fit with fallback to font measurement

The renderer.getMetrics() may not be accessible since it's private.
Added fallback to measure cell size using a test span element with
the configured font family and size.
This commit is contained in:
GitHub Copilot
2026-01-28 07:39:35 +00:00
parent b90be26952
commit e05c1cd1b4
2 changed files with 57 additions and 15 deletions
File diff suppressed because one or more lines are too long
+53 -11
View File
@@ -486,16 +486,37 @@ class WebTerminal {
* uses canvas rendering without a visible scrollbar.
*/
private fit(): void {
const renderer = (this.terminal as unknown as { renderer?: { getMetrics?: () => { width: number; height: number } } }).renderer;
if (!renderer?.getMetrics) {
// Fall back to FitAddon if we can't access renderer
this.fitAddon.fit();
return;
}
// Try to get metrics from renderer (private but accessible at runtime)
const termAny = this.terminal as unknown as Record<string, unknown>;
const renderer = termAny.renderer as { getMetrics?: () => { width: number; height: number } } | undefined;
const metrics = renderer.getMetrics();
if (!metrics || metrics.width === 0 || metrics.height === 0) {
return;
let cellWidth: number;
let cellHeight: number;
if (renderer?.getMetrics) {
const metrics = renderer.getMetrics();
if (metrics && metrics.width > 0 && metrics.height > 0) {
cellWidth = metrics.width;
cellHeight = metrics.height;
} else {
// Fall back to measuring
const dims = this.measureCellSize();
if (!dims) {
this.fitAddon.fit();
return;
}
cellWidth = dims.width;
cellHeight = dims.height;
}
} else {
// Fall back to measuring
const dims = this.measureCellSize();
if (!dims) {
this.fitAddon.fit();
return;
}
cellWidth = dims.width;
cellHeight = dims.height;
}
const style = window.getComputedStyle(this.element);
@@ -511,14 +532,35 @@ class WebTerminal {
return;
}
const cols = Math.max(2, Math.floor(availableWidth / metrics.width));
const rows = Math.max(1, Math.floor(availableHeight / metrics.height));
const cols = Math.max(2, Math.floor(availableWidth / cellWidth));
const rows = Math.max(1, Math.floor(availableHeight / cellHeight));
if (cols !== this.terminal.cols || rows !== this.terminal.rows) {
this.terminal.resize(cols, rows);
}
}
/** Measure cell size by creating a test character */
private measureCellSize(): { width: number; height: number } | null {
const testElement = document.createElement('span');
testElement.style.visibility = 'hidden';
testElement.style.position = 'absolute';
testElement.style.fontFamily = this.options.fontFamily || 'monospace';
testElement.style.fontSize = `${this.options.fontSize || 14}px`;
testElement.style.lineHeight = 'normal';
testElement.textContent = 'W';
document.body.appendChild(testElement);
const width = testElement.offsetWidth;
const height = testElement.offsetHeight;
document.body.removeChild(testElement);
if (width > 0 && height > 0) {
return { width, height };
}
return null;
}
/** Setup resize observer for container */
private setupResizeObserver(): void {
const resizeObserver = new ResizeObserver(() => {