{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "ink/pie-chart",
  "title": "Pie Chart",
  "description": "Unicode block pie chart with legend",
  "dependencies": [
    "ink"
  ],
  "registryDependencies": [
    "https://termcn.dev/r/ink/use-theme.json",
    "https://termcn.dev/r/ink/use-unicode.json"
  ],
  "files": [
    {
      "path": "registry/ui/pie-chart.tsx",
      "content": "import { Box, Text } from \"ink\";\n\nimport { useTheme } from \"@/hooks/use-theme\";\nimport { useUnicode } from \"@/hooks/use-unicode\";\n\nexport interface PieChartItem {\n  label: string;\n  value: number;\n  color?: string;\n}\n\nexport interface PieChartProps {\n  data: PieChartItem[];\n  radius?: number;\n  showLegend?: boolean;\n  showPercentages?: boolean;\n  title?: string;\n  accessibleSummary?: string;\n  \"aria-label\"?: string;\n}\n\nconst DEFAULT_COLORS = [\n  \"#7c3aed\",\n  \"#2563eb\",\n  \"#16a34a\",\n  \"#d97706\",\n  \"#dc2626\",\n  \"#0891b2\",\n  \"#be185d\",\n  \"#65a30d\",\n];\n\n/**\n * Approximate a pie chart using a 2D character grid.\n * Each cell is tested to see which segment it belongs to,\n * then rendered with that segment's color using block chars.\n */\nconst buildPieGrid = (\n  data: PieChartItem[],\n  radius: number,\n  fillCharacter: string\n): { char: string; color: string }[][] => {\n  const total = data.reduce((s, d) => s + d.value, 0);\n  if (total === 0) {\n    return [];\n  }\n\n  const cols = radius * 4;\n  const rows = radius * 2;\n  const cx = cols / 2;\n  const cy = rows / 2;\n\n  const grid: { char: string; color: string }[][] = Array.from(\n    { length: rows },\n    () => Array.from({ length: cols }, () => ({ char: \" \", color: \"\" }))\n  );\n\n  const angles: { color: string; end: number; start: number }[] = [];\n  let cumulative = 0;\n  for (const item of data) {\n    const slice = (item.value / total) * Math.PI * 2;\n    angles.push({\n      color: item.color ?? \"\",\n      end: cumulative + slice,\n      start: cumulative,\n    });\n    cumulative += slice;\n  }\n\n  for (let row = 0; row < rows; row += 1) {\n    for (let col = 0; col < cols; col += 1) {\n      const dx = (col - cx) / 2;\n      const dy = row - cy;\n      const dist = Math.hypot(dx, dy);\n\n      if (dist > radius) {\n        continue;\n      }\n\n      let angle = Math.atan2(dy, dx) + Math.PI / 2;\n      if (angle < 0) {\n        angle += Math.PI * 2;\n      }\n\n      const seg =\n        angles.find((a) => angle >= a.start && angle < a.end) ?? angles.at(-1);\n      if (seg) {\n        grid[row][col] = { char: fillCharacter, color: seg.color };\n      }\n    }\n  }\n\n  return grid;\n};\n\nexport const PieChart = ({\n  data,\n  radius = 5,\n  showLegend = true,\n  showPercentages = true,\n  title,\n  accessibleSummary,\n  \"aria-label\": ariaLabel,\n}: PieChartProps) => {\n  const theme = useTheme();\n  const unicode = useUnicode();\n  const fillCharacter = unicode ? \"█\" : \"#\";\n  const legendCharacter = unicode ? \"■\" : \"#\";\n\n  if (data.length === 0) {\n    return <Text color={theme.colors.mutedForeground}>No data</Text>;\n  }\n\n  const total = data.reduce((s, d) => s + d.value, 0);\n\n  const itemsWithColors = data.map((item, idx) => ({\n    ...item,\n    color:\n      item.color ??\n      DEFAULT_COLORS[idx % DEFAULT_COLORS.length] ??\n      theme.colors.primary,\n  }));\n\n  const grid = buildPieGrid(itemsWithColors, radius, fillCharacter);\n\n  return (\n    <Box\n      flexDirection=\"row\"\n      gap={2}\n      aria-label={\n        ariaLabel ??\n        accessibleSummary ??\n        `${title ?? \"Pie chart\"}. Total ${total}. ${data\n          .slice(0, 12)\n          .map(\n            (item) =>\n              `${item.label}: ${item.value}${total > 0 ? ` (${((item.value / total) * 100).toFixed(1)}%)` : \"\"}`\n          )\n          .join(\", \")}${data.length > 12 ? `, ${data.length - 12} more` : \"\"}.`\n      }\n    >\n      <Box flexDirection=\"column\">\n        {grid.map((row, rowIdx) => (\n          <Box key={rowIdx} flexDirection=\"row\">\n            {row.map((cell, colIdx) =>\n              cell.char === \" \" ? (\n                <Text key={colIdx}> </Text>\n              ) : (\n                <Text key={colIdx} color={cell.color || theme.colors.primary}>\n                  {cell.char}\n                </Text>\n              )\n            )}\n          </Box>\n        ))}\n      </Box>\n\n      {showLegend && (\n        <Box flexDirection=\"column\" justifyContent=\"center\">\n          {itemsWithColors.map((item, idx) => {\n            const pct =\n              total > 0 ? ((item.value / total) * 100).toFixed(1) : \"0.0\";\n            return (\n              <Box key={idx} flexDirection=\"row\" gap={1}>\n                <Text color={item.color}>{legendCharacter}</Text>\n                <Text color={theme.colors.foreground}>{item.label}</Text>\n                {showPercentages && (\n                  <Text color={theme.colors.mutedForeground}>({pct}%)</Text>\n                )}\n              </Box>\n            );\n          })}\n        </Box>\n      )}\n    </Box>\n  );\n};\n",
      "type": "registry:ui"
    }
  ],
  "categories": [
    "charts"
  ],
  "type": "registry:ui"
}