{"spec_id":"scatter-annotated","library":"muix","language":"javascript","code":"// anyplot.ai\n// scatter-annotated: Annotated Scatter Plot with Text Labels\n// Library: muix 7.29.1 | JavaScript 22.23.2\n// Quality: 91/100 | Created: 2026-09-05\nimport { ScatterChart } from \"@mui/x-charts/ScatterChart\";\nimport { useXScale, useYScale, getValueToPositionMapper } from \"@mui/x-charts/hooks\";\n\nconst t = window.ANYPLOT_TOKENS;\nconst isDark = window.ANYPLOT_THEME === \"dark\";\n// The JS harness token set has no \"muted\" anchor (see prompts/default-style-guide.md\n// \"Semantic anchors\" — other/rest role), so the theme-adaptive hex is applied directly.\nconst MUTED = isDark ? \"#A8A79F\" : \"#6B6A63\";\n\nfunction hexToRgba(hex: string, alpha: number) {\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// --- Data (in-memory, deterministic) ---------------------------------------\n// Revenue ($B) vs. market cap ($B) for a cohort of tech companies. Highlighted\n// companies deviate from the general revenue-to-cap trend (growth premium or\n// value discount) — that deviation is exactly what the labels call out.\ntype Point = { id: string; x: number; y: number; z?: number };\n\nconst highlighted: (Point & { label: string; dx: number; dy: number })[] = [\n  { id: \"nexacloud\", x: 8, y: 210, label: \"NexaCloud\", dx: 40, dy: -16 },\n  { id: \"solstice\", x: 45, y: 60, label: \"Solstice Robotics\", dx: -44, dy: -18 },\n  { id: \"vertex\", x: 62, y: 340, label: \"Vertex Analytics\", dx: 42, dy: -14 },\n  { id: \"ferrotech\", x: 120, y: 95, label: \"Ferrotech Dynamics\", dx: 44, dy: -16 },\n  { id: \"lumen\", x: 15, y: 180, label: \"Lumen Bioworks\", dx: 40, dy: 20 },\n  { id: \"aurora\", x: 88, y: 410, label: \"Aurora Semiconductors\", dx: 44, dy: 18 },\n  { id: \"cascade\", x: 150, y: 60, label: \"Cascade Freight\", dx: -46, dy: -16 },\n  { id: \"nimbus\", x: 30, y: 265, label: \"Nimbus Software\", dx: 42, dy: -12 },\n  { id: \"ridgeline\", x: 95, y: 70, label: \"Ridgeline Materials\", dx: 40, dy: 20 },\n  { id: \"halcyon\", x: 55, y: 40, label: \"Halcyon Energy\", dx: 40, dy: -20 },\n  { id: \"zephyr\", x: 22, y: 155, label: \"Zephyr Mobility\", dx: -42, dy: 20 },\n  { id: \"granite\", x: 110, y: 130, label: \"Granite Financial\", dx: -46, dy: -16 },\n];\n\n// Unlabeled peer cloud that establishes the general revenue-to-cap trend the\n// highlighted companies deviate from — annotating every point would clutter a\n// 30-point chart, so only the notable outliers above carry a text label.\nconst peers: Point[] = [\n  { id: \"p1\", x: 10, y: 45 },\n  { id: \"p2\", x: 18, y: 70 },\n  { id: \"p3\", x: 25, y: 80 },\n  { id: \"p4\", x: 33, y: 95 },\n  { id: \"p5\", x: 40, y: 110 },\n  { id: \"p6\", x: 48, y: 120 },\n  { id: \"p7\", x: 52, y: 135 },\n  { id: \"p8\", x: 60, y: 150 },\n  { id: \"p9\", x: 68, y: 155 },\n  { id: \"p10\", x: 75, y: 165 },\n  { id: \"p11\", x: 82, y: 175 },\n  { id: \"p12\", x: 90, y: 185 },\n  { id: \"p13\", x: 98, y: 195 },\n  { id: \"p14\", x: 105, y: 200 },\n  { id: \"p15\", x: 115, y: 205 },\n  { id: \"p16\", x: 125, y: 215 },\n  { id: \"p17\", x: 135, y: 225 },\n  { id: \"p18\", x: 145, y: 235 },\n];\n\n// --- Label + leader-line overlay (child of ScatterChart; reads the same ---\n// --- x/y scales the ScatterPlot itself uses, so labels track the markers) --\nfunction PointAnnotations() {\n  const xScale = useXScale();\n  const yScale = useYScale();\n  const getX = getValueToPositionMapper(xScale);\n  const getY = getValueToPositionMapper(yScale);\n\n  return (\n    <g>\n      {highlighted.map((p) => {\n        const px = getX(p.x);\n        const py = getY(p.y);\n        const lx = px + p.dx;\n        const ly = py + p.dy;\n        const tx = lx + (p.dx >= 0 ? 6 : -6);\n        return (\n          <g key={p.id}>\n            <line x1={px} y1={py} x2={lx} y2={ly} stroke={MUTED} strokeWidth={1} opacity={0.6} />\n            <text\n              x={tx}\n              y={ly}\n              fontSize={14}\n              fontWeight={500}\n              fill={t.ink}\n              textAnchor={p.dx >= 0 ? \"start\" : \"end\"}\n              dominantBaseline=\"middle\"\n            >\n              {p.label}\n            </text>\n          </g>\n        );\n      })}\n    </g>\n  );\n}\n\n// --- Chart (default-exported component — the harness mounts it) -------------\nexport default function Chart() {\n  return (\n    <div style={{ width: \"100%\", height: \"100%\", position: \"relative\" }}>\n      {/* Title rendered in the chart's top margin space — MUI X has no built-in title slot */}\n      <div\n        style={{\n          position: \"absolute\",\n          top: 14,\n          left: 0,\n          right: 0,\n          textAlign: \"center\",\n          zIndex: 1,\n          fontSize: 19,\n          fontWeight: 500,\n          color: t.ink,\n          pointerEvents: \"none\",\n        }}\n      >\n        Tech Company Valuations · scatter-annotated · javascript · muix · anyplot.ai\n      </div>\n\n      <ScatterChart\n        width={window.ANYPLOT_SIZE.width}\n        height={window.ANYPLOT_SIZE.height}\n        skipAnimation\n        disableVoronoi\n        grid={{ horizontal: true, vertical: true }}\n        xAxis={[{ label: \"Annual Revenue ($B)\", min: 0, max: 165, tickLabelStyle: { fontSize: 14 } }]}\n        yAxis={[\n          {\n            label: \"Market Cap ($B)\",\n            min: 0,\n            max: 430,\n            tickLabelStyle: { fontSize: 14 },\n            // labelRefPoint offsets by `tickFontSize` (the legacy prop, not\n            // tickLabelStyle) — bump it so the rotated axis label clears the\n            // wide 3-digit tick numbers instead of overlapping them.\n            tickFontSize: 54,\n          },\n        ]}\n        series={[\n          {\n            id: \"highlighted\",\n            label: \"Highlighted\",\n            data: highlighted,\n            markerSize: 16,\n            color: hexToRgba(t.palette[0], 0.78),\n          },\n          {\n            id: \"peers\",\n            label: \"Peer companies\",\n            data: peers,\n            markerSize: 9,\n            color: hexToRgba(MUTED, 0.6),\n          },\n        ]}\n        slotProps={{\n          legend: { direction: \"row\", position: { vertical: \"bottom\", horizontal: \"middle\" } },\n        }}\n        sx={{\n          \"& .MuiChartsAxis-bottom .MuiChartsAxis-label\": { fontSize: \"16px !important\" },\n          \"& .MuiChartsAxis-left .MuiChartsAxis-label\": { fontSize: \"13px !important\" },\n          \"& .MuiChartsLegend-label\": { fontSize: \"15px !important\" },\n        }}\n        margin={{ top: 70, right: 60, bottom: 90, left: 130 }}\n      >\n        <PointAnnotations />\n      </ScatterChart>\n    </div>\n  );\n}\n"}