Assessor_2.0/src/app/navigation-diagnostics.ts

49 lines
1.8 KiB
TypeScript

let enabled = false;
let steps: Record<string, number> = {};
/** UI-only instrumentation; production calls are just the original calculation. */
export function measureNavigationStep<T>(name: string, compute: () => T): T {
if (!import.meta.env.DEV || !enabled) return compute();
const started = performance.now();
try { return compute(); }
finally { steps[name] = (steps[name] ?? 0) + performance.now() - started; }
}
/** Opt-in, local development measurement. Never records report content. */
export function observeNavigationPerformance(): void {
enabled = true;
let startedAt = 0;
let target = '';
let sequence = 0;
let frames = 0;
let previousFrame = 0;
let longestFrame = 0;
const output = document.createElement('output');
output.id = 'assessor-navigation-performance';
output.hidden = true;
document.body.append(output);
document.addEventListener('click', event => {
const button = (event.target as Element).closest('.sidebar-primary-actions button');
if (!button || button.hasAttribute('disabled')) return;
target = button.textContent?.trim() ?? '';
startedAt = performance.now();
steps = {};
sequence += 1;
const current = sequence;
previousFrame = startedAt;
longestFrame = 0;
frames = 0;
const measure = (now: number) => {
if (sequence !== current) return;
longestFrame = Math.max(longestFrame, now - previousFrame);
previousFrame = now;
frames += 1;
if (frames < 3) { requestAnimationFrame(measure); return; }
output.textContent = JSON.stringify({ area: target, sequence: current,
paintMs: Math.round(now - startedAt), longestFrameMs: Math.round(longestFrame),
stepsMs: Object.fromEntries(Object.entries(steps).map(([name, duration]) => [name, Math.round(duration)])) });
};
requestAnimationFrame(measure);
}, true);
}