{"spec_id":"area-cumulative-flow","library":"chartjs","language":"javascript","code":"// anyplot.ai\n// area-cumulative-flow: Cumulative Flow Diagram for Workflow Analytics\n// Library: chartjs 4.4.7 | JavaScript 22.23.2\n// Quality: 92/100 | Created: 2026-08-18\n//# anyplot-orientation: landscape\n\nconst t = window.ANYPLOT_TOKENS;\n\n// --- Data (in-memory, deterministic) ----------------------------------------\n// Simulates a 90-day Kanban board: Backlog -> In Progress -> Review -> Done.\n// Each stage's cumulative count is the total number of items that have ever\n// reached that stage. Downstream stages lag behind Backlog by a per-stage\n// delay, so backlog >= inProgress >= review >= done at every date. The Review\n// delay widens after day 45 to simulate a bottleneck (growing \"In Progress\"\n// band).\nlet seed = 42;\nconst nextRand = () => {\n  seed = (seed * 1103515245 + 12345) & 0x7fffffff;\n  return seed / 0x7fffffff;\n};\n\nconst DAYS = 90;\nconst START_DATE = new Date(\"2024-03-01T00:00:00Z\");\n\nconst dateLabels = [];\nconst backlog = [];\nlet backlogTotal = 0;\nfor (let day = 0; day < DAYS; day++) {\n  const d = new Date(START_DATE);\n  d.setUTCDate(d.getUTCDate() + day);\n  dateLabels.push(d.toISOString().slice(0, 10));\n\n  backlogTotal += 2 + Math.floor(nextRand() * 4); // 2-5 new items/day\n  backlog.push(backlogTotal);\n}\n\nconst LAG_IN_PROGRESS = 4;\nconst inProgress = [];\nconst review = [];\nconst done = [];\nfor (let day = 0; day < DAYS; day++) {\n  const lagReview = day < 45 ? 10 : 10 + (day - 45) * 0.6;\n  const lagDone = lagReview + 6;\n\n  const idxInProgress = Math.min(DAYS - 1, Math.floor(Math.max(0, day - LAG_IN_PROGRESS)));\n  const idxReview = Math.min(DAYS - 1, Math.floor(Math.max(0, day - lagReview)));\n  const idxDone = Math.min(DAYS - 1, Math.floor(Math.max(0, day - lagDone)));\n\n  inProgress.push(backlog[idxInProgress]);\n  review.push(backlog[idxReview]);\n  done.push(backlog[idxDone]);\n}\n\n// Stacked top (Backlog, earliest stage) to bottom (Done, latest stage). Each\n// band fills to the next stage's line, so the visible area is the WIP\n// currently sitting in that stage — not a running sum of the raw values.\nconst stages = [\n  { label: \"Backlog\", data: backlog, fill: 1 },\n  { label: \"In Progress\", data: inProgress, fill: 2 },\n  { label: \"Review\", data: review, fill: 3 },\n  { label: \"Done\", data: done, fill: \"origin\" },\n];\n\n// Day the Review lag starts widening (see lagReview above) — the onset of the\n// growing \"In Progress\" WIP bottleneck the data was constructed to show.\nconst BOTTLENECK_DAY = 45;\n\n// A thin dashed reference line + label marking that onset, drawn with\n// Chart.js's native per-chart plugin hook (no external annotation plugin).\nconst bottleneckMarkerPlugin = {\n  id: \"bottleneckMarker\",\n  afterDraw(chart) {\n    const { ctx, chartArea, scales } = chart;\n    const x = scales.x.getPixelForValue(dateLabels[BOTTLENECK_DAY], BOTTLENECK_DAY);\n    ctx.save();\n    ctx.strokeStyle = t.inkSoft;\n    ctx.lineWidth = 1.5;\n    ctx.setLineDash([4, 4]);\n    ctx.beginPath();\n    ctx.moveTo(x, chartArea.top);\n    ctx.lineTo(x, chartArea.bottom);\n    ctx.stroke();\n\n    ctx.setLineDash([]);\n    ctx.fillStyle = t.inkSoft;\n    ctx.font = \"13px sans-serif\";\n    ctx.textAlign = \"left\";\n    ctx.textBaseline = \"top\";\n    ctx.fillText(\"Bottleneck onset\", x + 8, chartArea.top + 6);\n    ctx.restore();\n  },\n};\n\n// --- Mount -------------------------------------------------------------------\nconst canvas = document.createElement(\"canvas\");\ndocument.getElementById(\"container\").appendChild(canvas);\n\n// --- Chart ---------------------------------------------------------------\nnew Chart(canvas, {\n  type: \"line\",\n  data: {\n    labels: dateLabels,\n    datasets: stages.map((stage, i) => ({\n      label: stage.label,\n      data: stage.data,\n      borderColor: t.palette[i],\n      backgroundColor: `${t.palette[i]}E6`,\n      fill: stage.fill,\n      borderWidth: 2.5,\n      pointRadius: 0,\n      tension: 0,\n    })),\n  },\n  plugins: [bottleneckMarkerPlugin],\n  options: {\n    responsive: true,\n    maintainAspectRatio: false,\n    animation: false,\n    interaction: { intersect: false, mode: \"index\" },\n    layout: { padding: { top: 8, right: 24, bottom: 4, left: 4 } },\n    plugins: {\n      title: {\n        display: true,\n        text: \"area-cumulative-flow · javascript · chartjs · anyplot.ai\",\n        color: t.ink,\n        font: { size: 25, weight: \"bold\" },\n        padding: { top: 4, bottom: 18 },\n      },\n      legend: {\n        labels: { color: t.ink, font: { size: 16 }, usePointStyle: true, pointStyle: \"rectRounded\", padding: 18 },\n      },\n    },\n    scales: {\n      x: {\n        ticks: { color: t.inkSoft, font: { size: 14 }, maxTicksLimit: 10, maxRotation: 0 },\n        grid: { display: false },\n        title: { display: true, text: \"Date\", color: t.ink, font: { size: 16 } },\n      },\n      y: {\n        beginAtZero: true,\n        ticks: { color: t.inkSoft, font: { size: 14 } },\n        grid: { color: t.grid },\n        title: { display: true, text: \"Cumulative Items\", color: t.ink, font: { size: 16 } },\n      },\n    },\n  },\n});\n"}