{"spec_id":"histogram-stepwise","library":"muix","language":"javascript","code":"// anyplot.ai\n// histogram-stepwise: Step Histogram\n// Library: muix 7.29.1 | JavaScript 22.23.2\n// Quality: 91/100 | Created: 2026-09-05\nimport { LineChart } from \"@mui/x-charts/LineChart\";\nimport { ChartsReferenceLine } from \"@mui/x-charts/ChartsReferenceLine\";\nimport Box from \"@mui/material/Box\";\nimport Typography from \"@mui/material/Typography\";\n\nconst t = window.ANYPLOT_TOKENS;\n\n// --- Data (in-memory, deterministic) ---------------------------------------\n// Tiny fixed-seed LCG — the browser has no seeded RNG.\nfunction makeLcg(seed: number) {\n  let state = seed >>> 0;\n  return function next() {\n    state = (Math.imul(state, 1664525) + 1013904223) >>> 0;\n    return state / 4294967296;\n  };\n}\n\nfunction randNormal(rng: () => number) {\n  const u1 = Math.max(rng(), 1e-9);\n  const u2 = rng();\n  return Math.sqrt(-2 * Math.log(u1)) * Math.cos(2 * Math.PI * u2);\n}\n\nconst rng = makeLcg(42);\nconst SAMPLE_SIZE = 800;\n\n// API response latency (ms) before / after an optimization pass.\nconst latencyV1 = Array.from({ length: SAMPLE_SIZE }, () =>\n  Math.max(8, 118 + randNormal(rng) * 22),\n);\nconst latencyV2 = Array.from({ length: SAMPLE_SIZE }, () =>\n  Math.max(8, 92 + randNormal(rng) * 16),\n);\n\nconst BIN_COUNT = 26;\nconst allValues = latencyV1.concat(latencyV2);\nconst minValue = Math.min(...allValues);\nconst maxValue = Math.max(...allValues);\nconst binWidth = (maxValue - minValue) / BIN_COUNT;\nconst binEdges = Array.from({ length: BIN_COUNT + 1 }, (_, i) => minValue + i * binWidth);\n\nfunction binCounts(values: number[]) {\n  const counts = new Array(BIN_COUNT).fill(0);\n  for (const value of values) {\n    const index = Math.min(BIN_COUNT - 1, Math.floor((value - minValue) / binWidth));\n    counts[index] += 1;\n  }\n  return counts;\n}\n\n// One x-point per bin edge (not per corner) — MUI X's native `curve=\"stepAfter\"`\n// draws the horizontal/vertical step segments itself. The leading edge is\n// duplicated once (0 -> count[0]) so the outline rises from zero, and a\n// trailing 0 closes it back to the axis at the final edge.\nconst stepX = [binEdges[0], binEdges[0], ...binEdges.slice(1)];\nconst countsV1 = binCounts(latencyV1);\nconst countsV2 = binCounts(latencyV2);\nconst stepV1 = [0, ...countsV1, 0];\nconst stepV2 = [0, ...countsV2, 0];\n\n// Bin-center of each distribution's tallest bar — used to call out the\n// before/after latency shift with reference lines.\nfunction peakCenter(counts: number[]) {\n  const peakIndex = counts.indexOf(Math.max(...counts));\n  return (binEdges[peakIndex] + binEdges[peakIndex + 1]) / 2;\n}\n\nconst peakV1 = peakCenter(countsV1);\nconst peakV2 = peakCenter(countsV2);\n\n// --- Chart (default-exported component — the harness mounts it) -------------\nexport default function Chart() {\n  const W = window.ANYPLOT_SIZE.width;\n  const H = window.ANYPLOT_SIZE.height;\n  const CHART_TOP = 64;\n\n  const title = \"histogram-stepwise · javascript · muix · anyplot.ai\";\n  const titleSize = title.length > 67 ? Math.round((22 * 67) / title.length) : 22;\n\n  return (\n    <Box sx={{ position: \"relative\", width: W, height: H, bgcolor: t.pageBg }}>\n      <Box sx={{ position: \"absolute\", top: 20, left: 56, right: 56 }}>\n        <Typography sx={{ color: t.ink, fontSize: titleSize, fontWeight: 500 }}>{title}</Typography>\n      </Box>\n      <Box sx={{ position: \"absolute\", top: CHART_TOP, left: 0, right: 0, bottom: 0 }}>\n        <LineChart\n          width={W}\n          height={H - CHART_TOP}\n          skipAnimation\n          series={[\n            {\n              data: stepV1,\n              label: \"API v1 latency\",\n              color: t.palette[0],\n              curve: \"stepAfter\",\n              area: false,\n              showMark: false,\n            },\n            {\n              data: stepV2,\n              label: \"API v2 latency\",\n              color: t.palette[1],\n              curve: \"stepAfter\",\n              area: false,\n              showMark: false,\n            },\n          ]}\n          xAxis={[\n            {\n              data: stepX,\n              scaleType: \"linear\",\n              label: \"Response latency (ms)\",\n              labelStyle: { fontSize: 16 },\n              tickLabelStyle: { fontSize: 14 },\n              valueFormatter: (value: number) => value.toFixed(0),\n            },\n          ]}\n          yAxis={[\n            {\n              label: \"Count\",\n              labelStyle: { fontSize: 16 },\n              tickLabelStyle: { fontSize: 14 },\n            },\n          ]}\n          grid={{ horizontal: true }}\n          slotProps={{ legend: { labelStyle: { fontSize: 14 } } }}\n          sx={{ \"& .MuiLineElement-root\": { strokeWidth: 3 } }}\n        >\n          <ChartsReferenceLine\n            x={peakV1}\n            label={`v1 peak ~${peakV1.toFixed(0)}ms`}\n            labelStyle={{ fontSize: 13, fill: t.palette[0] }}\n            lineStyle={{ stroke: t.palette[0], strokeDasharray: \"6 4\" }}\n          />\n          <ChartsReferenceLine\n            x={peakV2}\n            label={`v2 peak ~${peakV2.toFixed(0)}ms`}\n            labelStyle={{ fontSize: 13, fill: t.palette[1] }}\n            lineStyle={{ stroke: t.palette[1], strokeDasharray: \"6 4\" }}\n          />\n        </LineChart>\n      </Box>\n    </Box>\n  );\n}\n"}