49 lines
1.8 KiB
TypeScript
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);
|
|
}
|