{"spec_id":"strip-basic","library":"muix","language":"javascript","code":"// anyplot.ai\n// strip-basic: Basic Strip Plot\n// Library: muix 7.29.1 | JavaScript 22.23.1\n// Quality: 86/100 | Created: 2026-08-05\n\nimport { ChartContainer } from \"@mui/x-charts/ChartContainer\";\nimport { ScatterPlot } from \"@mui/x-charts/ScatterChart\";\nimport { ChartsXAxis } from \"@mui/x-charts/ChartsXAxis\";\nimport { ChartsYAxis } from \"@mui/x-charts/ChartsYAxis\";\nimport { ChartsGrid } from \"@mui/x-charts/ChartsGrid\";\nimport { useXScale, useYScale } from \"@mui/x-charts/hooks\";\n\nconst t = window.ANYPLOT_TOKENS;\nconst { width, height } = window.ANYPLOT_SIZE;\n\n// Reproducible LCG (seed 42) — the browser harness has no seeded Math.random()\nlet seed = 42;\nfunction rng() {\n  seed = (1664525 * seed + 1013904223) >>> 0;\n  return seed / 4294967296;\n}\n\n// Box-Muller transform for approximately-normal samples from the LCG stream\nfunction gaussian() {\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\nfunction hexToRgba(hex, alpha) {\n  const n = parseInt(hex.slice(1), 16);\n  return `rgba(${(n >> 16) & 255}, ${(n >> 8) & 255}, ${n & 255}, ${alpha})`;\n}\n\n// Time to symptom relief (minutes) after treatment, four groups\nconst GROUPS = [\n  { name: \"Control\", mean: 46, std: 12, n: 42 },\n  { name: \"Drug A\", mean: 30, std: 10, n: 42 },\n  { name: \"Drug B\", mean: 19, std: 7, n: 42 },\n  { name: \"Drug C\", mean: 11, std: 5, n: 42 },\n];\n\nconst JITTER_WIDTH = 0.27; // wide spread within each category to reduce point-on-point overlap\nconst POINT_ALPHA = 0.62; // frequent overlap at n=42 per group\nconst MEAN_LINE_HALF_WIDTH = 0.34; // spans slightly beyond the jittered point cloud\n\n// Each group's own observations drive its mean line, so the marker reflects\n// the actual sampled data rather than the underlying distribution parameter.\nconst GROUP_DATA = GROUPS.map((group, i) => {\n  const points = Array.from({ length: group.n }, (_, j) => ({\n    x: i + (rng() * 2 - 1) * JITTER_WIDTH,\n    y: Math.max(2, group.mean + group.std * gaussian()),\n    id: `${group.name}-${j}`,\n  }));\n  const observedMean = points.reduce((sum, p) => sum + p.y, 0) / points.length;\n  return { ...group, points, observedMean };\n});\n\nconst series = GROUP_DATA.map((group, i) => ({\n  type: \"scatter\",\n  id: group.name,\n  label: group.name,\n  color: hexToRgba(t.palette[i], POINT_ALPHA),\n  markerSize: 9,\n  data: group.points,\n}));\n\n// Short, bold underline per category at its observed mean — reinforces the\n// Control-to-Drug-C decreasing-effect story the spec asks to highlight.\nfunction MeanLines() {\n  const xScale = useXScale(\"group\");\n  const yScale = useYScale(\"time\");\n  return (\n    <g>\n      {GROUP_DATA.map((group, i) => {\n        const y = yScale(group.observedMean);\n        return (\n          <line\n            key={group.name}\n            x1={xScale(i - MEAN_LINE_HALF_WIDTH)}\n            x2={xScale(i + MEAN_LINE_HALF_WIDTH)}\n            y1={y}\n            y2={y}\n            stroke={t.palette[i]}\n            strokeWidth={3}\n            strokeLinecap=\"round\"\n          />\n        );\n      })}\n    </g>\n  );\n}\n\nconst TITLE = \"Time to Symptom Relief · strip-basic · javascript · muix · anyplot.ai\";\nconst TITLE_FONT_SIZE = Math.round(22 * Math.min(1, 67 / TITLE.length)); // scale down past the 67-char baseline\n\nexport default function Chart() {\n  return (\n    <ChartContainer\n      width={width}\n      height={height}\n      margin={{ top: 62, right: 56, bottom: 88, left: 100 }}\n      sx={{ \"& .MuiChartsAxis-top, & .MuiChartsAxis-right\": { display: \"none\" } }}\n      series={series}\n      xAxis={[\n        {\n          id: \"group\",\n          min: -0.6,\n          max: GROUPS.length - 1 + 0.6,\n          tickInterval: GROUPS.map((_, i) => i),\n          valueFormatter: (v) => GROUPS[Math.round(v)]?.name ?? \"\",\n          label: \"Treatment Group\",\n          tickLabelStyle: { fontSize: 14, fill: t.inkSoft },\n          labelStyle: { fontSize: 16, fill: t.ink },\n        },\n      ]}\n      yAxis={[\n        {\n          id: \"time\",\n          min: 0,\n          label: \"Time to Relief (minutes)\",\n          tickLabelStyle: { fontSize: 14, fill: t.inkSoft },\n          labelStyle: { fontSize: 16, fill: t.ink },\n        },\n      ]}\n    >\n      <ChartsGrid horizontal />\n      <ScatterPlot skipAnimation />\n      <MeanLines />\n      <ChartsXAxis\n        axisId=\"group\"\n        tickLabelStyle={{ fontSize: 14, fill: t.inkSoft }}\n        labelStyle={{ fontSize: 16, fill: t.ink }}\n      />\n      <ChartsYAxis\n        axisId=\"time\"\n        tickLabelStyle={{ fontSize: 14, fill: t.inkSoft }}\n        labelStyle={{ fontSize: 16, fill: t.ink }}\n      />\n      <text x={width / 2} y={30} textAnchor=\"middle\" fontSize={TITLE_FONT_SIZE} fontWeight={600} fill={t.ink}>\n        {TITLE}\n      </text>\n    </ChartContainer>\n  );\n}\n"}