Files
NetMesh/infrastructure/config/xtermPerformance.ts

201 lines
5.9 KiB
TypeScript
Raw Normal View History

/**
* XTerm.js Performance Optimization Configuration
*
* This file contains platform-specific optimizations for xterm performance.
* macOS has different performance characteristics than Windows due to:
* - Stricter GPU memory management
* - Different rendering pipeline (Metal vs DirectX)
* - Memory pressure handling
*/
export const XTERM_UNLIMITED_SCROLLBACK_CAP = 50000;
export function resolveXTermScrollback(scrollback: number): number {
// xterm.js treats 0 as "no scrollback". Keep the app's 0 sentinel useful
// without asking xterm to resize/reflow nearly one million buffer rows.
return scrollback === 0 ? XTERM_UNLIMITED_SCROLLBACK_CAP : scrollback;
}
export const XTERM_PERFORMANCE_CONFIG = {
// Memory and Scrollback Settings
scrollback: {
// Windows can handle larger buffers efficiently
default: 3000,
// macOS performance degrades with large scrollbacks
// due to more aggressive memory pressure
macOS: 1000,
// Mobile-like environments
lowMemory: 500,
},
// Rendering optimizations
rendering: {
// Disable cursor blinking - reduces render calls significantly
cursorBlink: false,
// Allow transparency is expensive on macOS with Metal
// Disabling it improves performance by 15-20%
allowTransparency: false,
// Custom glyphs: xterm.js draws box/block characters on canvas
// instead of using font glyphs, eliminating gaps between cells
customGlyphs: true,
// Font rendering settings
letterSpacing: 0,
lineHeight: 1,
// Keep viewport movement smooth without feeling sluggish.
smoothScrollDuration: 120,
},
// WebGL-specific optimizations
webgl: {
// Enable WebGL by default for GPU acceleration
enabled: true,
// User can choose DOM renderer on any platform (canvas removed in xterm 6.0)
preferDOM: false,
// Handle WebGL context loss gracefully
enableContextLoss: true,
},
// Event handling optimizations
events: {
// Use document override for better event routing on macOS
documentOverride: true,
// Standard tab width (8 spaces)
tabStopWidth: 8,
// Let the SSH daemon handle EOL conversion
convertEol: false,
// Allow bracketed paste mode for better paste handling
ignoreBracketedPasteMode: false,
},
// Logging (disable in production for performance)
logging: {
logLevel: 'off' as const, // 'off' | 'error' | 'warn' | 'info' | 'debug'
},
// Resize debouncing (macOS can get flooded with resize events)
resize: {
// Debounce delay in milliseconds
// Higher values reduce CPU usage but may feel less responsive
debounceMs: 50,
// Use requestAnimationFrame for resize fitting
useRAF: true,
},
// Performance monitoring thresholds
monitoring: {
// Log performance warning if render takes longer than this (ms)
slowRenderThreshold: 16, // 60fps = 16.67ms per frame
// Log warning if data buffer gets too large
largeBufferThreshold: 1024 * 1024, // 1MB
},
// Output-pressure quiet window shared by optional terminal side work.
highlighting: {
// Let large output settle before deferred work catches up.
largeOutputQuietMs: 480,
},
};
export type XTermPlatform = "darwin" | "win32" | "linux";
type RendererType = "dom";
type LogLevel = "off" | "error" | "warn" | "info" | "debug";
export type ResolvedXTermPerformance = {
options: {
scrollback: number;
cursorBlink: boolean;
allowTransparency: boolean;
customGlyphs: boolean;
letterSpacing: number;
lineHeight: number;
smoothScrollDuration: number;
documentOverride: boolean;
tabStopWidth: number;
convertEol: boolean;
ignoreBracketedPasteMode: boolean;
logLevel: LogLevel;
rendererType?: RendererType;
};
useWebGLAddon: boolean;
preferDOMRenderer: boolean;
};
const isLowMemoryDevice = (deviceMemoryGb?: number) =>
typeof deviceMemoryGb === "number" && deviceMemoryGb > 0 && deviceMemoryGb <= 4;
export type RendererPreference = "auto" | "webgl" | "dom";
/**
* Resolve a platform and hardware aware performance profile.
* When rendererType is 'auto', uses DOM on low-memory devices to avoid WebGL overhead.
*/
export function resolveXTermPerformanceConfig({
platform = "darwin",
deviceMemoryGb,
rendererType = "auto",
}: {
platform?: XTermPlatform;
deviceMemoryGb?: number;
rendererType?: RendererPreference;
} = {}): ResolvedXTermPerformance {
const baseConfig = XTERM_PERFORMANCE_CONFIG;
const lowMem = isLowMemoryDevice(deviceMemoryGb);
// Determine if we should use DOM renderer (canvas removed in xterm 6.0)
let resolvedPreferDOM: boolean;
if (rendererType === "dom") {
resolvedPreferDOM = true;
} else if (rendererType === "webgl") {
resolvedPreferDOM = false;
} else {
// Auto mode: use DOM on low-memory devices
resolvedPreferDOM = baseConfig.webgl.preferDOM || lowMem;
}
const scrollbackProfile = lowMem
? "lowMemory"
: platform === "darwin"
? "macOS"
: "default";
const resolvedRendererType = resolvedPreferDOM ? ("dom" as const) : undefined;
const baseOptions = {
scrollback: baseConfig.scrollback[scrollbackProfile],
cursorBlink: baseConfig.rendering.cursorBlink,
allowTransparency: baseConfig.rendering.allowTransparency,
customGlyphs: baseConfig.rendering.customGlyphs,
letterSpacing: baseConfig.rendering.letterSpacing,
lineHeight: baseConfig.rendering.lineHeight,
smoothScrollDuration: baseConfig.rendering.smoothScrollDuration,
documentOverride: baseConfig.events.documentOverride,
tabStopWidth: baseConfig.events.tabStopWidth,
convertEol: baseConfig.events.convertEol,
ignoreBracketedPasteMode: baseConfig.events.ignoreBracketedPasteMode,
logLevel: baseConfig.logging.logLevel,
};
const options = resolvedRendererType
? { ...baseOptions, rendererType: resolvedRendererType }
: baseOptions;
return {
options,
useWebGLAddon: baseConfig.webgl.enabled && !resolvedPreferDOM,
preferDOMRenderer: resolvedPreferDOM,
};
}