export type GameplayFrameRateLimit = 30 | 60 | "display"; export type PreviewFrameRateLimit = 15 | 30 | 60 | "display"; export const GAMEPLAY_FRAME_RATE_LIMITS: readonly GameplayFrameRateLimit[] = [30, 60, "display"]; export const PREVIEW_FRAME_RATE_LIMITS: readonly PreviewFrameRateLimit[] = [15, 30, 60, "display"]; export const DEFAULT_GAMEPLAY_FRAME_RATE_LIMIT: GameplayFrameRateLimit = 60; export const DEFAULT_PREVIEW_FRAME_RATE_LIMIT: PreviewFrameRateLimit = 30; export function normalizeGameplayFrameRateLimit( value: unknown, fallback: GameplayFrameRateLimit = DEFAULT_GAMEPLAY_FRAME_RATE_LIMIT, ): GameplayFrameRateLimit { return value === 30 || value === 60 || value === "display" ? value : fallback; } export function normalizePreviewFrameRateLimit( value: unknown, fallback: PreviewFrameRateLimit = DEFAULT_PREVIEW_FRAME_RATE_LIMIT, ): PreviewFrameRateLimit { return value === 15 || value === 30 || value === 60 || value === "display" ? value : fallback; } export function nextFrameRateLimit( current: T, order: readonly T[], ): T { const index = order.indexOf(current); return order[(index + 1) % order.length] ?? order[0] ?? current; } export function frameRateLimitLabel(limit: number | "display"): string { return limit === "display" ? "Display refresh" : `${limit} FPS`; } export function numericFrameRateLimit(limit: number | "display"): number | null { return limit === "display" ? null : limit; }