@utsp/runtime-client
Version:
UTSP Runtime Client - Local and multi-user client runtime
268 lines (262 loc) • 9.19 kB
TypeScript
import { AudioManager } from '@utsp/audio';
export { AudioManager, AudioManagerOptions, ToneOptions } from '@utsp/audio';
import { IApplication } from '@utsp/types';
export { IApplication } from '@utsp/types';
import { AutoplayOverlayOptions } from '@utsp/render';
/**
* Runtime Client Types and Options
*/
/**
* Client runtime mode
*/
type ClientRuntimeMode = 'local' | 'connected';
/**
* Renderer type to use
*/
declare enum RendererType {
/** WebGL2 renderer - Optimized WebGL 1.0 with GPU palette, instanced rendering (default, recommended) */
TerminalGL = "webgl",
/** Canvas 2D renderer - CPU fallback with ImageData optimization for benchmarks */
Terminal2D = "terminal2d"
}
/**
* Render mode for ClientRuntime
*/
type RenderMode =
/** Continuous rendering at max FPS (default) - requestAnimationFrame loop */
'continuous'
/** On-demand rendering - only renders when explicitly requested via requestRender() */
| 'on-demand';
/**
* Base client runtime options
*/
interface BaseClientRuntimeOptions {
/** Application instance */
application: IApplication;
/** Container HTML element for rendering */
container: HTMLElement;
/** Enable debug logging */
debug?: boolean;
/** Initial display width in columns (default: 80). Can be overridden by IApplication via Display */
width?: number;
/** Initial display height in rows (default: 25). Can be overridden by IApplication via Display */
height?: number;
/** Renderer type (default: Auto) */
renderer?: RendererType;
/** Mobile/Touch input configuration */
mobileInputConfig?: {
/** Prevent default touch behavior (scroll, zoom). Default: true for canvas, false for page */
preventDefault?: boolean;
/** Use passive event listeners for better scroll performance. Default: false */
passive?: boolean;
/** Maximum simultaneous touches. Default: 10 */
maxTouches?: number;
};
/** Show debug grid to visualize cell boundaries (default: false) */
showGrid?: boolean;
/** Enable ImageData rendering for Canvas 2D (default: true). Provides pixel-perfect rendering with no gaps between cells. 10-20× faster than fillRect. Only for Terminal2D renderer with bitmap fonts. */
useImageDataRendering?: boolean;
/** Render mode: 'continuous' (default) renders at max FPS, 'on-demand' only renders when explicitly requested. When tickRate is 0, automatically switches to 'on-demand'. */
renderMode?: RenderMode;
/** Tick rate in ticks per second (default: 30). Set to 0 to disable update loop (only init/initUser, no update/updateUser). When set to 0, automatically enables 'on-demand' render mode. */
tickRate?: number;
/** Capture input events to prevent default browser behavior (Tab, arrows, etc.). Default: false. When true, preventDefault() and stopPropagation() are called on keyboard and mouse events to keep focus in the terminal. All F keys (F1-F12) and Ctrl/Cmd shortcuts are automatically excluded. */
captureInput?: boolean;
/** Enable input handling (keyboard, mouse, touch, gamepad). Default: true. When false, no input listeners are created - useful for display-only clients in documentation pages where you want to scroll freely without the client intercepting events. */
inputEnabled?: boolean;
/**
* Autoplay mode. Default: true.
*
* - `true`: Application starts immediately (current behavior)
* - `false`: Displays a "Click to Start" button overlay. Application only starts after user interaction.
*
* Useful for:
* - Audio APIs that require user interaction before playing
* - Preventing automatic resource consumption
* - Better UX on mobile where autoplay may not be desired
*
* @example
* ```typescript
* const runtime = new ClientRuntime({
* mode: 'local',
* application: myApp,
* container: document.getElementById('app'),
* autoplay: false, // Show "Click to Start" button
* autoplayOptions: {
* buttonText: 'Click to Play',
* buttonColor: '#00ff00'
* }
* });
* ```
*/
autoplay?: boolean;
/**
* Autoplay overlay customization options.
* Only used when `autoplay: false`.
*/
autoplayOptions?: Omit<AutoplayOverlayOptions, 'onStart'>;
}
/**
* Local mode options (standalone, no network)
*/
interface LocalModeOptions extends BaseClientRuntimeOptions {
/** Runtime mode */
mode: 'local';
/** User ID (default: 'local') */
userId?: string;
/** User name (default: 'User') */
username?: string;
}
/**
* Connected mode options (multiplayer)
*/
interface ConnectedModeOptions extends BaseClientRuntimeOptions {
/** Runtime mode */
mode: 'connected';
/** Server URL (e.g., 'ws://localhost:3000') */
serverUrl: string;
/** User name */
username?: string;
/** Auto-reconnect on disconnect */
autoReconnect?: boolean;
/** Authentication token */
token?: string;
}
/**
* Union type of client runtime options
*/
type ClientRuntimeOptions = LocalModeOptions | ConnectedModeOptions;
/**
* Performance timing for a single frame
*/
interface FrameTiming {
/** Time spent in Core tick (ms) */
coreTime: number;
/** Time spent in Renderer (ms) */
renderTime: number;
/** Total frame time (ms) */
totalTime: number;
}
/**
* Client runtime statistics
*/
interface ClientRuntimeStats {
/** Runtime mode */
mode: ClientRuntimeMode;
/** Is runtime currently running */
running: boolean;
/** Number of users (1 for local, multiple for connected) */
userCount: number;
/** Current FPS */
fps: number;
/** Uptime in milliseconds */
uptime: number;
/** Total frames processed */
totalFrames: number;
/** Network latency in ms (connected mode only) */
latency?: number;
/** Last frame timing breakdown */
lastFrameTiming?: FrameTiming;
/** Average frame timing over last 60 frames */
avgFrameTiming?: FrameTiming;
}
/**
* Client Runtime
*
* Unified client runtime supporting both local and connected modes.
*/
declare class ClientRuntime {
private core;
private rendererManager;
private rendererType;
private input;
private networkSync;
private options;
private running;
private startTime;
private lastTimestamp;
private userId;
private mode;
private visibilityChangeHandler?;
private tickRate;
private accumulatedTime;
private readonly FRAME_TIME_MIN;
private lastRenderTimestamp;
private rafId;
private performanceMonitor;
private renderMode;
private renderRequested;
private autoplayOverlay;
private autoplay;
private audioManager;
constructor(options: ClientRuntimeOptions);
getMode(): ClientRuntimeMode;
getRendererType(): RendererType;
isRunning(): boolean;
private createRenderer;
setTickRate(tickRate: number): void;
setRenderMode(mode: 'continuous' | 'on-demand'): void;
setMaxFPS(fps: number): void;
getTickRate(): number;
/**
* Request a render in on-demand mode.
* Has no effect in continuous mode (renders automatically).
*
* @example
* ```typescript
* // On-demand mode: render only when needed
* const runtime = new ClientRuntime({
* mode: 'local',
* renderMode: 'on-demand',
* tickRate: 0, // Disable update loop
* application: myApp,
* container: document.getElementById('app')
* });
*
* await runtime.start();
*
* // Manually request render after changing something
* core.getUser('local').setCell(0, 0, '@', 1, 0);
* runtime.requestRender(); // Trigger render
* ```
*/
requestRender(): void;
start(): Promise<void>;
stop(): Promise<void>;
getStats(): ClientRuntimeStats;
destroy(): Promise<void>;
private createLocalUser;
/**
* Load sounds from Core's SoundRegistry into AudioManager's SoundBank
* Used in standalone mode to transfer sounds registered via core.loadSound()
* @private
*/
private loadSoundsFromRegistry;
private mainLoop;
private collectAndApplyInput;
private collectAndSendInput;
private render;
/**
* Handle palette change from Core
* Converts Core palette Map to RGBColor array and updates renderer
*/
private onCorePaletteChanged;
/**
* Handle bitmap font change from Core
* Loads the font from registry and updates renderer
*/
private onCoreBitmapFontChanged;
/**
* Get the AudioManager instance
* Returns null before start() is called
*/
getAudioManager(): InstanceType<typeof AudioManager> | null;
/**
* Get the AudioContext instance (convenience method)
* Returns null if AudioManager not initialized
*/
getAudioContext(): AudioContext | null;
private log;
}
export { ClientRuntime, RendererType };
export type { BaseClientRuntimeOptions, ClientRuntimeMode, ClientRuntimeOptions, ClientRuntimeStats, ConnectedModeOptions, FrameTiming, LocalModeOptions, RenderMode };