mirror of
https://github.com/openfrontio/OpenFrontIO.git
synced 2026-06-21 10:43:27 +00:00
f7598369ed
## Description: This PR consolidates ad hoc platform/environment/viewport detection into a single shared utility. It is scoped to this refactor only, and serves as groundwork for the mobile-focused feature work planned for the v31 milestone. ### What changed - Introduced a shared `Platform` utility centralising: - OS detection (with `userAgentData` + UA fallback) - Electron environment detection - Viewport breakpoint helpers (`isMobileWidth`, `isTabletWidth`, `isDesktopWidth`) - Replaced duplicated inline checks across client files with the shared API. - Normalised Mac detection to derive from the consolidated OS logic rather than a separate regex. ### Why - Multiple client files each independently ran `navigator.userAgent` regexes or copy-pasted `isElectron` logic — this unifies all of that. - Puts a stable, tested abstraction in place before v31 mobile work lands, so mobile feature branches have a consistent surface to build against. ## Please complete the following: - [x] I have added screenshots for all UI updates (N/A: refactor only, no visible UI changes) - [x] I process any text displayed to the user through translateText() and I've added it to the en.json file (N/A: no new user-facing strings) - [x] I have added relevant tests to the test directory (N/A: refactor only) - [x] I confirm I have thoroughly tested these changes and take full responsibility for any bugs introduced ## Please put your Discord username so you can be contacted if a bug or regression is found: skigim
135 lines
3.0 KiB
TypeScript
135 lines
3.0 KiB
TypeScript
import { Platform } from "../Platform";
|
|
|
|
export type RendererType = "Canvas2D" | "WebGL1" | "WebGL2";
|
|
|
|
export interface BrowserInfo {
|
|
engine: string;
|
|
platform: string;
|
|
os: string;
|
|
dpr: number;
|
|
}
|
|
|
|
export interface GraphicsDiagnostics {
|
|
browser: BrowserInfo;
|
|
rendering: RenderingInfo;
|
|
power: PowerInfo;
|
|
}
|
|
|
|
export interface GPUInfo {
|
|
vendor?: string;
|
|
renderer?: string;
|
|
software?: boolean;
|
|
unavailable?: boolean;
|
|
}
|
|
|
|
export interface RenderingInfo {
|
|
type: RendererType;
|
|
antialias?: boolean;
|
|
maxTextureSize?: number;
|
|
shaderHighp?: boolean;
|
|
gpu?: GPUInfo;
|
|
}
|
|
|
|
export interface PerformanceInfo {
|
|
fps: number;
|
|
worstFrameMs: number;
|
|
jankPercent: number;
|
|
throttlingLikely: boolean;
|
|
}
|
|
|
|
export interface PowerInfo {
|
|
charging?: boolean;
|
|
level?: string;
|
|
unavailable?: boolean;
|
|
}
|
|
|
|
export async function collectGraphicsDiagnostics(
|
|
canvas: HTMLCanvasElement,
|
|
): Promise<GraphicsDiagnostics> {
|
|
/* ---------- Browser / OS ---------- */
|
|
|
|
const uaData = (navigator as any).userAgentData;
|
|
|
|
const os = Platform.os;
|
|
|
|
const browser: BrowserInfo = {
|
|
engine: uaData?.brands
|
|
? uaData.brands.map((b: any) => b.brand).join(", ")
|
|
: navigator.userAgent,
|
|
platform: navigator.platform,
|
|
os,
|
|
dpr: window.devicePixelRatio,
|
|
};
|
|
|
|
/* ---------- Rendering ---------- */
|
|
|
|
let gl: WebGLRenderingContext | WebGL2RenderingContext | null = null;
|
|
let type: RendererType = "Canvas2D";
|
|
|
|
gl =
|
|
canvas.getContext("webgl2", { antialias: true }) ??
|
|
canvas.getContext("webgl", { antialias: true });
|
|
|
|
if (gl) {
|
|
const isWebGL2 =
|
|
typeof WebGL2RenderingContext !== "undefined" &&
|
|
gl instanceof WebGL2RenderingContext;
|
|
type = isWebGL2 ? "WebGL2" : "WebGL1";
|
|
}
|
|
|
|
const rendering: RenderingInfo = { type };
|
|
|
|
if (gl) {
|
|
rendering.antialias = gl.getContextAttributes()?.antialias ?? false;
|
|
rendering.maxTextureSize = gl.getParameter(gl.MAX_TEXTURE_SIZE);
|
|
|
|
const precision = gl.getShaderPrecisionFormat(
|
|
gl.FRAGMENT_SHADER,
|
|
gl.HIGH_FLOAT,
|
|
);
|
|
rendering.shaderHighp = precision !== null && precision.precision > 0;
|
|
|
|
const debugInfo = gl.getExtension("WEBGL_debug_renderer_info");
|
|
|
|
if (debugInfo) {
|
|
const renderer = gl.getParameter(
|
|
(debugInfo as any).UNMASKED_RENDERER_WEBGL,
|
|
) as string;
|
|
|
|
const vendor = gl.getParameter(
|
|
(debugInfo as any).UNMASKED_VENDOR_WEBGL,
|
|
) as string;
|
|
rendering.gpu = {
|
|
vendor,
|
|
renderer,
|
|
software: /swiftshader|llvmpipe|software/i.test(renderer),
|
|
};
|
|
} else {
|
|
rendering.gpu = { unavailable: true };
|
|
}
|
|
}
|
|
|
|
/* ---------- Power ---------- */
|
|
|
|
let power: PowerInfo = {};
|
|
|
|
if ("getBattery" in navigator) {
|
|
try {
|
|
const battery = await (navigator as any).getBattery();
|
|
power = {
|
|
charging: battery.charging,
|
|
level: Math.round(battery.level * 100) + "%",
|
|
};
|
|
} catch {
|
|
power = { unavailable: true };
|
|
}
|
|
} else {
|
|
power = { unavailable: true };
|
|
}
|
|
return {
|
|
browser,
|
|
rendering,
|
|
power,
|
|
};
|
|
}
|