{"spec_id":"line-styled","library":"chartjs","language":"javascript","code":"// anyplot.ai\n// line-styled: Styled Line Plot\n// Library: chartjs 4.4.7 | JavaScript 22.23.2\n// Quality: 94/100 | Created: 2026-09-05\n\n//# anyplot-orientation: landscape\nconst t = window.ANYPLOT_TOKENS;\n\n// --- Data (in-memory, deterministic) ---------------------------------------\nfunction lcg(seed) {\n  let state = seed;\n  return () => {\n    state = (state * 1664525 + 1013904223) % 4294967296;\n    return state / 4294967296;\n  };\n}\nconst rand = lcg(42);\n\nconst minutes = Array.from({ length: 60 }, (_, i) => i);\n\nfunction coreTemperature(baseline, rampRate, plateau, noiseScale) {\n  return minutes.map((minute) => {\n    const ramp = Math.min(minute * rampRate, plateau - baseline);\n    const noise = (rand() - 0.5) * noiseScale;\n    return Math.round((baseline + ramp + noise) * 10) / 10;\n  });\n}\n\n// Dash patterns tuned so each style stays distinguishable even when the\n// chart is scaled down to a mobile thumbnail: the dotted style uses a round\n// cap with a wide gap (reads as separated dots, not a near-solid line) and\n// the dash-dot gaps are enlarged so the dot doesn't merge into the dashes.\nconst series = [\n  { label: \"Core 1\", data: coreTemperature(46, 0.75, 82, 1.2), borderDash: [], cap: \"butt\", pointStyle: \"circle\" },\n  { label: \"Core 2\", data: coreTemperature(45, 0.58, 74, 1.0), borderDash: [16, 8], cap: \"butt\", pointStyle: \"rect\" },\n  { label: \"Core 3\", data: coreTemperature(47, 0.44, 66, 1.4), borderDash: [1, 9], cap: \"round\", pointStyle: \"triangle\" },\n  { label: \"Core 4\", data: coreTemperature(44, 0.3, 58, 0.9), borderDash: [16, 8, 3, 8], cap: \"butt\", pointStyle: \"rectRot\" },\n];\n\n// --- Mount -----------------------------------------------------------------\nconst canvas = document.createElement(\"canvas\");\ndocument.getElementById(\"container\").appendChild(canvas);\n\n// Custom Chart.js plugin (afterDatasetsDraw) that annotates the hottest\n// core's final reading — a deliberate focal point/data-storytelling touch\n// rather than treating all four series with equal visual weight.\nconst peakAnnotationPlugin = {\n  id: \"peakAnnotation\",\n  afterDatasetsDraw(chart) {\n    const hottest = series.reduce((best, s, i) => (s.data[s.data.length - 1] > series[best].data[series[best].data.length - 1] ? i : best), 0);\n    const meta = chart.getDatasetMeta(hottest);\n    const point = meta.data[meta.data.length - 1];\n    if (!point) return;\n    const value = series[hottest].data[series[hottest].data.length - 1];\n    const { ctx } = chart;\n    ctx.save();\n    ctx.font = \"600 15px sans-serif\";\n    ctx.fillStyle = t.palette[hottest];\n    ctx.textAlign = \"left\";\n    ctx.textBaseline = \"middle\";\n    ctx.fillText(`${series[hottest].label} hottest · ${value.toFixed(1)}°C`, point.x + 12, point.y);\n    ctx.restore();\n  },\n};\n\n// --- Chart -----------------------------------------------------------------\nnew Chart(canvas, {\n  type: \"line\",\n  data: {\n    labels: minutes,\n    datasets: series.map((s, i) => ({\n      label: s.label,\n      data: s.data,\n      borderColor: t.palette[i],\n      backgroundColor: t.palette[i],\n      borderDash: s.borderDash,\n      borderCapStyle: s.cap,\n      borderWidth: 3.5,\n      // Sparse markers every 15 minutes reinforce the dash pattern with a\n      // distinct point shape per series, so style stays legible even when\n      // the dash pattern itself compresses at small thumbnail scales.\n      pointStyle: s.pointStyle,\n      pointRadius: (ctx) => (ctx.dataIndex % 15 === 0 ? 6 : 0),\n      pointBackgroundColor: t.palette[i],\n      pointBorderColor: t.palette[i],\n      pointHoverRadius: 6,\n      tension: 0.25,\n    })),\n  },\n  plugins: [peakAnnotationPlugin],\n  options: {\n    responsive: true,\n    maintainAspectRatio: false,\n    animation: false,\n    layout: {\n      padding: { right: 260 },\n    },\n    plugins: {\n      title: {\n        display: true,\n        text: \"line-styled · javascript · chartjs · anyplot.ai\",\n        color: t.ink,\n        font: { size: 22, weight: \"500\" },\n        padding: { bottom: 24 },\n      },\n      legend: {\n        position: \"top\",\n        labels: { color: t.ink, font: { size: 16 }, boxWidth: 40 },\n      },\n    },\n    scales: {\n      x: {\n        title: { display: true, text: \"Stress Test Duration (minutes)\", color: t.ink, font: { size: 16 } },\n        ticks: { color: t.inkSoft, font: { size: 14 }, maxTicksLimit: 10 },\n        grid: { display: false },\n      },\n      y: {\n        title: { display: true, text: \"Core Temperature (°C)\", color: t.ink, font: { size: 16 } },\n        ticks: { color: t.inkSoft, font: { size: 14 } },\n        grid: { color: t.grid },\n      },\n    },\n  },\n});\n"}