import { isBetter } from "./runtime-metrics.ts"; import type { MetricDirection } from "./runtime-model.ts"; export function computeAutoresearchDashboardImprovement(input: { baseline: number | null; best: number | null; direction: MetricDirection | null; }): { label: string; className: string } { if (input.baseline === null || input.best === null || input.baseline === 0) { return { label: "โ€”", className: "" }; } const rawPercent = ((input.best - input.baseline) / input.baseline) * 100; const improved = input.direction ? isBetter(input.best, input.baseline, input.direction) : false; const signed = rawPercent > 0 ? `+${rawPercent.toFixed(1)}%` : `${rawPercent.toFixed(1)}%`; return { label: signed, className: improved ? "good" : rawPercent === 0 ? "warn" : "bad" }; } export function formatAutoresearchDashboardNumber(value: number | null, unit: string): string { if (value === null || !Number.isFinite(value)) return "โ€”"; const formatted = Math.abs(value) >= 100 ? value.toFixed(0) : Number.isInteger(value) ? String(value) : value.toFixed(2); return `${formatted}${unit}`; } export function cssClassToken(value: string): string { return value.replace(/[^a-z0-9_-]/giu, "_").toLowerCase(); } export function escapeScriptJson(value: string): string { return value.replace(//g, "\\u003e").replace(/&/g, "\\u0026"); } export function renderAutoresearchDashboardShareSvg(input: { metricName: string; posture: string; improvement: string; baseline: string; best: string; recommendedNext: string; }): string { return `pi-autoresearch${escapeHtml(input.metricName)} ยท ${escapeHtml(input.posture)}${escapeHtml(input.improvement)}${escapeHtml(input.baseline)} โ†’ ${escapeHtml(input.best)}${escapeHtml(input.recommendedNext).slice(0, 120)}`; } export function escapeHtml(value: string): string { return value .replace(/&/g, "&") .replace(//g, ">") .replace(/"/g, """) .replace(/'/g, "'"); }