Files
DEVICE_CONTACT/src/renderer/debug-hud.ts
2025-12-17 23:19:04 +02:00

104 lines
2.5 KiB
TypeScript

import type { CanvasRenderingContext2D } from "@napi-rs/canvas";
import type { Layout } from "./layout";
export type DebugStats = Record<string, string | number | boolean | undefined>;
export type DebugHudOptions = {
showGlobal: boolean;
showRenderer: boolean;
showCustom?: boolean;
};
export type DebugHudData = {
global: DebugStats | string[];
renderer: {
id: string;
label: string;
stats: DebugStats;
fps: number;
};
custom?: DebugStats;
};
export type DebugHud = {
draw: (ctx: CanvasRenderingContext2D, layout: Layout, data: DebugHudData) => void;
};
export function createDebugHud(options: DebugHudOptions): DebugHud {
const padding = 8;
const lineHeight = 16;
const bg = "rgba(0, 0, 0, 0.65)";
const fg = "yellow"; // global
const rendererFg = "#ff66cc"; // renderer
const customFg = "#00c6ff"; // custom
const drawBlock = (
ctx: CanvasRenderingContext2D,
x: number,
y: number,
title: string,
stats: DebugStats | string[],
color: string
): { width: number; height: number } => {
let lines: string[] = [];
if (Array.isArray(stats)) {
lines = stats
} else {
const keys = Object.keys(stats);
lines = [title, ...keys.map((k) => `${k}: ${String(stats[k])}`)];
}
ctx.font = "14px \"JetBrains Mono\", monospace";
const textWidth = Math.max(...lines.map((l) => ctx.measureText(l).width));
const height = lines.length * lineHeight + padding * 2;
const width = textWidth + padding * 2;
ctx.save();
ctx.fillStyle = bg;
ctx.fillRect(x, y, width, height);
ctx.fillStyle = color;
ctx.textBaseline = "top";
lines.forEach((line, i) => {
ctx.fillText(line, x + padding, y + padding + i * lineHeight);
});
ctx.restore();
return { width, height };
};
const draw = (ctx: CanvasRenderingContext2D, layout: Layout, data: DebugHudData) => {
if (!options.showGlobal && !options.showRenderer && !options.showCustom) return;
ctx.save();
ctx.imageSmoothingEnabled = false;
ctx.globalAlpha = 0.9;
let cursorY = padding;
const originX = padding;
if (options.showGlobal) {
const { height } = drawBlock(ctx, originX, cursorY, "Global", data.global, fg);
cursorY += height + padding;
}
if (options.showRenderer) {
const { height } = drawBlock(
ctx,
originX,
cursorY,
`Renderer: ${data.renderer.label}`,
{ fps: data.renderer.fps.toFixed(2), ...data.renderer.stats },
rendererFg
);
cursorY += height + padding;
}
if (options.showCustom && data.custom) {
drawBlock(ctx, originX, cursorY, "Custom", data.custom, customFg);
}
ctx.restore();
};
return { draw };
}