"use strict"; /** * Visualization module for replication analysis dashboard. * Responsible for rendering charts and updating tables dynamically. */ /** * @typedef {Object} RunComparison * @property {string} run_id * @property {Object} metrics * @property {number[]} metrics.bandwidth * @property {number[]} metrics.retry_tailp99 * @property {string[]} segments */ /** * Render bandwidth chart based on provided run data. * @param {RunComparison} data */ export function renderBandwidthChart(data) { const container = document.getElementById('bandwidth-chart'); if (!container) return; container.innerHTML = ''; const canvas = document.createElement('canvas'); canvas.setAttribute('aria-label', 'Bandwidth Chart'); canvas.setAttribute('role', 'img'); container.appendChild(canvas); const ctx = canvas.getContext('2d'); const bandwidth = data.metrics?.bandwidth ?? []; if (!bandwidth.length) { ctx.font = '16px sans-serif'; ctx.fillText('No bandwidth data available', 10, 30); return; } const spacing = canvas.width / bandwidth.length || 5; const maxValue = Math.max(...bandwidth); const scale = (canvas.height - 20) / maxValue; ctx.strokeStyle = '#3366cc'; ctx.beginPath(); bandwidth.forEach((val, i) => { const x = i * spacing + 10; const y = canvas.height - val * scale - 10; if (i === 0) ctx.moveTo(x, y); else ctx.lineTo(x, y); }); ctx.stroke(); } /** * Render retry_tailp99 chart as a trend visualization. * @param {RunComparison} data */ export function renderRetryTailChart(data) { const container = document.getElementById('retry-tailp99-chart'); if (!container) return; container.innerHTML = ''; const canvas = document.createElement('canvas'); canvas.setAttribute('aria-label', 'Retry Tailp99 Trend'); canvas.setAttribute('role', 'img'); container.appendChild(canvas); const ctx = canvas.getContext('2d'); const retries = data.metrics?.retry_tailp99 ?? []; if (!retries.length) { ctx.font = '16px sans-serif'; ctx.fillText('No retry_tailp99 data available', 10, 30); return; } const spacing = canvas.width / retries.length || 5; const maxValue = Math.max(...retries); const scale = (canvas.height - 20) / maxValue; ctx.strokeStyle = '#cc3333'; ctx.beginPath(); retries.forEach((val, i) => { const x = i * spacing + 10; const y = canvas.height - val * scale - 10; if (i === 0) ctx.moveTo(x, y); else ctx.lineTo(x, y); }); ctx.stroke(); } /** * Update results table with latest run metrics. * @param {RunComparison} data */ export function updateResultsTable(data) { const table = document.getElementById('results-table'); if (!table) return; table.innerHTML = ''; const headerRow = document.createElement('tr'); ['Segment', 'Bandwidth', 'Retry Tail p99'].forEach(h => { const th = document.createElement('th'); th.textContent = h; headerRow.appendChild(th); }); table.appendChild(headerRow); const {segments = [], metrics: {bandwidth = [], retry_tailp99 = []} = {}} = data; const len = Math.max(segments.length, bandwidth.length, retry_tailp99.length); for (let i = 0; i < len; i++) { const row = document.createElement('tr'); const seg = document.createElement('td'); seg.textContent = segments[i] ?? '-'; const bw = document.createElement('td'); bw.textContent = bandwidth[i]?.toFixed(2) ?? '-'; const rt = document.createElement('td'); rt.textContent = retry_tailp99[i]?.toFixed(2) ?? '-'; row.appendChild(seg); row.appendChild(bw); row.appendChild(rt); table.appendChild(row); } } /** * Ensure responsiveness on resize. */ window.addEventListener('resize', () => { const dataAttr = document.body.dataset.runComparison; if (!dataAttr) return; try { const data = JSON.parse(dataAttr); renderBandwidthChart(data); renderRetryTailChart(data); } catch (err) { console.error('Failed to re-render charts on resize:', err); } });