{"spec_id":"boxen-basic","library":"muix","language":"javascript","code":"// anyplot.ai\n// boxen-basic: Basic Boxen Plot (Letter-Value Plot)\n// Library: muix 7.29.1 | JavaScript 22.23.2\n// Quality: 92/100 | Created: 2026-09-01\nimport { ChartContainer } from \"@mui/x-charts/ChartContainer\";\nimport { ChartsGrid } from \"@mui/x-charts/ChartsGrid\";\nimport { ChartsXAxis } from \"@mui/x-charts/ChartsXAxis\";\nimport { ChartsYAxis } from \"@mui/x-charts/ChartsYAxis\";\nimport { useXScale, useYScale } from \"@mui/x-charts/hooks\";\n\nconst t = window.ANYPLOT_TOKENS;\nconst TITLE = \"API Response Times · boxen-basic · javascript · muix · anyplot.ai\";\nconst TITLE_HEIGHT = 56;\nconst LEGEND_HEIGHT = 44;\n\n// --- Data (in-memory, deterministic LCG — no seeded RNG in the browser) -----\nfunction lcg(seed) {\n  let state = seed;\n  return () => {\n    state = (state * 1664525 + 1013904223) % 4294967296;\n    return state / 4294967296;\n  };\n}\n\nfunction randomNormal(rand) {\n  const u1 = Math.max(rand(), 1e-9);\n  const u2 = rand();\n  return Math.sqrt(-2 * Math.log(u1)) * Math.cos(2 * Math.PI * u2);\n}\n\n// Server endpoints — right-skewed (log-normal) latency, the classic shape\n// where a boxen/letter-value plot earns its keep over a regular box plot:\n// the interesting behavior lives in the tail, not around the median.\nconst endpoints = [\n  { name: \"/search\", medianMs: 45, sigma: 0.3 },\n  { name: \"/profile\", medianMs: 65, sigma: 0.35 },\n  { name: \"/checkout\", medianMs: 120, sigma: 0.45 },\n  { name: \"/upload\", medianMs: 260, sigma: 0.55 },\n];\n\nconst N_PER_ENDPOINT = 3000;\nconst MAX_LEVELS = 6; // capped for legibility — deeper levels shrink toward imperceptible slivers at this canvas size\n\nconst rand = lcg(42);\n\nfunction orderStat(sortedAsc, depth) {\n  const n = sortedAsc.length;\n  const lo = Math.max(0, Math.floor(depth) - 1);\n  const hi = Math.min(n - 1, Math.ceil(depth) - 1);\n  return lo === hi ? sortedAsc[lo] : (sortedAsc[lo] + sortedAsc[hi]) / 2;\n}\n\n// Tukey letter-value recursion: each level's depth is half the previous\n// level's (floored) depth + 1, converging from the median toward the\n// extremes. Each level's box spans the order statistics at that depth from\n// either end — quartiles first, then eighths, sixteenths, and so on.\nfunction letterValues(sortedAsc, maxLevels) {\n  const n = sortedAsc.length;\n  let depth = (n + 1) / 2;\n  const median = orderStat(sortedAsc, depth);\n  const boxes = [];\n  for (let i = 0; i < maxLevels; i++) {\n    const nextDepth = (Math.floor(depth) + 1) / 2;\n    if (nextDepth < 1 || nextDepth === depth) break;\n    depth = nextDepth;\n    boxes.push({\n      lower: orderStat(sortedAsc, depth),\n      upper: orderStat(sortedAsc, n - depth + 1),\n      pctLower: (depth / (n + 1)) * 100,\n      pctUpper: 100 - (depth / (n + 1)) * 100,\n    });\n  }\n  return { median, boxes };\n}\n\nconst categoryStats = endpoints.map(({ name, medianMs, sigma }) => {\n  const mu = Math.log(medianMs);\n  const values = Array.from({ length: N_PER_ENDPOINT }, () =>\n    Math.exp(mu + sigma * randomNormal(rand)),\n  ).sort((a, b) => a - b);\n\n  const { median, boxes } = letterValues(values, MAX_LEVELS);\n  const outer = boxes[boxes.length - 1];\n  const outliers = values.filter((v) => v < outer.lower || v > outer.upper);\n\n  return { name, median, boxes, outer, outliers };\n});\n\n// series=[] means the ChartContainer has no dataset to infer a y-domain\n// from, so the log-scale axis needs an explicit min/max computed from the\n// actual plotted extremes (outer letter-value bounds + outliers).\nconst allExtremes = categoryStats.flatMap((s) => [\n  s.outer.lower,\n  s.outer.upper,\n  ...s.outliers,\n]);\nconst Y_MIN = Math.min(...allExtremes) * 0.85;\nconst Y_MAX = Math.max(...allExtremes) * 1.15;\n\n// Box width shrinks and fill lightens at deeper levels — the wider,\n// paler bands cover more of the tail but represent a thinner slice of the\n// distribution, giving the characteristic tapered \"boxen\" silhouette.\nconst WIDTH_FACTORS = [1, 0.82, 0.64, 0.48, 0.34, 0.22];\nconst FILL_OPACITY = [0.6, 0.5, 0.4, 0.32, 0.24, 0.17];\n\nfunction hexToRgba(hex, alpha) {\n  const r = parseInt(hex.slice(1, 3), 16);\n  const g = parseInt(hex.slice(3, 5), 16);\n  const b = parseInt(hex.slice(5, 7), 16);\n  return `rgba(${r}, ${g}, ${b}, ${alpha})`;\n}\n\n// --- Nested letter-value boxes -----------------------------------------------\n// The community package (7.29.1) ships no box/letter-value plot component at\n// all — this reproduces one as a custom SVG layer positioned via the chart's\n// own band/linear scale hooks, the same technique used for span overlays.\nfunction BoxenLayer() {\n  const xScale = useXScale();\n  const yScale = useYScale();\n  const bandwidth = xScale.bandwidth();\n  const baseWidth = bandwidth * 0.55;\n\n  return (\n    <g>\n      {categoryStats.map((cat, ci) => {\n        const center = xScale(cat.name) + bandwidth / 2;\n        const color = t.palette[ci % t.palette.length];\n        const innerWidth = baseWidth * WIDTH_FACTORS[0];\n\n        return (\n          <g key={cat.name}>\n            {[...cat.boxes].reverse().map((box, ri) => {\n              const idx = cat.boxes.length - 1 - ri;\n              const w = baseWidth * WIDTH_FACTORS[idx];\n              const yTop = yScale(box.upper);\n              const yBottom = yScale(box.lower);\n              return (\n                <rect\n                  key={idx}\n                  x={center - w / 2}\n                  y={yTop}\n                  width={w}\n                  height={Math.max(1, yBottom - yTop)}\n                  fill={color}\n                  fillOpacity={FILL_OPACITY[idx]}\n                  stroke={color}\n                  strokeOpacity={Math.min(1, FILL_OPACITY[idx] + 0.25)}\n                  strokeWidth={1}\n                />\n              );\n            })}\n            <line\n              x1={center - innerWidth / 2}\n              x2={center + innerWidth / 2}\n              y1={yScale(cat.median)}\n              y2={yScale(cat.median)}\n              stroke={t.pageBg}\n              strokeWidth={5}\n              strokeLinecap=\"round\"\n            />\n            <line\n              x1={center - innerWidth / 2}\n              x2={center + innerWidth / 2}\n              y1={yScale(cat.median)}\n              y2={yScale(cat.median)}\n              stroke={t.ink}\n              strokeWidth={2.5}\n              strokeLinecap=\"round\"\n            />\n            {cat.outliers.map((v, j) => (\n              <circle\n                key={j}\n                cx={center + ((j % 5) - 2) * 7}\n                cy={yScale(v)}\n                r={4}\n                fill={t.pageBg}\n                stroke={color}\n                strokeWidth={1.5}\n                fillOpacity={0.9}\n              />\n            ))}\n          </g>\n        );\n      })}\n    </g>\n  );\n}\n\nexport default function Chart() {\n  const chartHeight = window.ANYPLOT_SIZE.height - TITLE_HEIGHT - LEGEND_HEIGHT;\n  const brand = t.palette[0];\n  const inner = categoryStats[0].boxes[0];\n  const outer = categoryStats[0].outer;\n\n  const legendItems = [\n    { kind: \"line\", label: \"Median (50th pct.)\" },\n    {\n      kind: \"swatch\",\n      opacity: FILL_OPACITY[0],\n      label: `${inner.pctLower.toFixed(1)}–${inner.pctUpper.toFixed(1)}% (fourths)`,\n    },\n    {\n      kind: \"swatch\",\n      opacity: FILL_OPACITY[FILL_OPACITY.length - 1],\n      label: `${outer.pctLower.toFixed(1)}–${outer.pctUpper.toFixed(1)}% (deepest level)`,\n    },\n    { kind: \"outlier\", label: \"Outlier beyond deepest level\" },\n  ];\n\n  return (\n    <div\n      style={{\n        width: window.ANYPLOT_SIZE.width,\n        height: window.ANYPLOT_SIZE.height,\n      }}\n    >\n      <div\n        style={{\n          height: TITLE_HEIGHT,\n          lineHeight: `${TITLE_HEIGHT}px`,\n          paddingLeft: 24,\n          fontSize: 22,\n          fontWeight: 500,\n          color: t.ink,\n        }}\n      >\n        {TITLE}\n      </div>\n      <div\n        style={{\n          height: LEGEND_HEIGHT,\n          display: \"flex\",\n          alignItems: \"center\",\n          gap: 28,\n          paddingLeft: 24,\n          fontSize: 14,\n          color: t.inkSoft,\n        }}\n      >\n        {legendItems.map((item) => (\n          <div\n            key={item.label}\n            style={{ display: \"flex\", alignItems: \"center\", gap: 8 }}\n          >\n            {item.kind === \"line\" && (\n              <div style={{ width: 16, height: 2.5, background: t.ink }} />\n            )}\n            {item.kind === \"swatch\" && (\n              <div\n                style={{\n                  width: 14,\n                  height: 14,\n                  borderRadius: 3,\n                  background: hexToRgba(brand, item.opacity),\n                  border: `1px solid ${hexToRgba(brand, Math.min(1, item.opacity + 0.25))}`,\n                }}\n              />\n            )}\n            {item.kind === \"outlier\" && (\n              <div\n                style={{\n                  width: 10,\n                  height: 10,\n                  borderRadius: \"50%\",\n                  background: t.pageBg,\n                  border: `1.5px solid ${brand}`,\n                }}\n              />\n            )}\n            <span>{item.label}</span>\n          </div>\n        ))}\n      </div>\n      <ChartContainer\n        width={window.ANYPLOT_SIZE.width}\n        height={chartHeight}\n        series={[]}\n        skipAnimation\n        margin={{ top: 20, right: 40, bottom: 64, left: 96 }}\n        xAxis={[\n          {\n            id: \"endpoints\",\n            data: categoryStats.map((s) => s.name),\n            scaleType: \"band\",\n            label: \"Endpoint\",\n            labelStyle: { fontSize: 16 },\n            tickLabelStyle: { fontSize: 14 },\n          },\n        ]}\n        yAxis={[\n          {\n            id: \"latency\",\n            scaleType: \"log\",\n            min: Y_MIN,\n            max: Y_MAX,\n            label: \"Response Time (ms)\",\n            labelStyle: { fontSize: 16 },\n            tickLabelStyle: { fontSize: 14 },\n          },\n        ]}\n      >\n        <ChartsGrid\n          horizontal\n          sx={{\n            \"& .MuiChartsGrid-line\": {\n              opacity: 0.55,\n              strokeDasharray: \"2 5\",\n            },\n          }}\n        />\n        <BoxenLayer />\n        <ChartsXAxis axisId=\"endpoints\" />\n        <ChartsYAxis axisId=\"latency\" />\n      </ChartContainer>\n    </div>\n  );\n}\n"}