{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "heatmap",
  "type": "registry:ui",
  "title": "Heatmap",
  "description": "Grid of colored cells showing density or activity. Opacity-based intensity scaling. Supports row/column labels.",
  "dependencies": [],
  "registryDependencies": [
    "utils"
  ],
  "files": [
    {
      "path": "ui/heatmap/heatmap.tsx",
      "type": "registry:ui",
      "content": "import * as React from 'react'\nimport { cn } from '@/lib/utils'\n\nexport type HeatmapVariant = 'GREEN' | 'AMBER' | 'RED'\n\nexport interface HeatmapCell {\n  value:  number\n  label?: string\n}\n\nexport interface HeatmapProps extends React.HTMLAttributes<HTMLDivElement> {\n  /** Flat array of cells, filled left-to-right, top-to-bottom */\n  data:        HeatmapCell[] | number[]\n  columns:     number\n  rowLabels?:  string[]\n  colLabels?:  string[]\n  title?:      string\n  variant?:    HeatmapVariant\n  /** Override the max value for normalization. Defaults to max(data). */\n  max?:        number\n  /** Cell size in px. Default 16. */\n  cellSize?:   number\n}\n\nfunction accentForVariant(v: HeatmapVariant): string {\n  switch (v) {\n    case 'GREEN': return 'var(--color-green)'\n    case 'AMBER': return 'var(--color-amber)'\n    case 'RED':   return 'var(--color-red)'\n  }\n}\n\nconst Heatmap = React.forwardRef<HTMLDivElement, HeatmapProps>(\n  (\n    {\n      className,\n      data,\n      columns,\n      rowLabels,\n      colLabels,\n      title,\n      variant   = 'GREEN',\n      max,\n      cellSize  = 16,\n      style,\n      ...props\n    },\n    ref\n  ) => {\n    // Normalise to HeatmapCell[]\n    const cells: HeatmapCell[] = data.map((d) =>\n      typeof d === 'number' ? { value: d } : d\n    )\n\n    const computedMax = max ?? Math.max(...cells.map((c) => c.value), 1)\n    const accentColor  = accentForVariant(variant)\n    const rows         = Math.ceil(cells.length / columns)\n    const hasRowLabels = rowLabels && rowLabels.length > 0\n    const hasColLabels = colLabels && colLabels.length > 0\n\n    return (\n      <div\n        ref={ref}\n        className={cn(className)}\n        style={{\n          border:      '1px solid var(--border)',\n          background:  'var(--surface)',\n          fontFamily:  'var(--font-mono)',\n          overflowX:   'auto',\n          // Keep component sized to content so parent controls overflow\n          display:     'inline-block',\n          maxWidth:    '100%',\n          ...style,\n        }}\n        {...props}\n      >\n        {/* Title bar */}\n        {title && (\n          <div\n            style={{\n              padding:       '0.4rem 0.75rem',\n              borderBottom:  '1px solid var(--border)',\n              background:    'var(--surface-raised)',\n              fontSize:      '0.6rem',\n              color:         'var(--text-muted)',\n              letterSpacing: '0.12em',\n              textTransform: 'uppercase',\n            }}\n          >\n            {title}\n          </div>\n        )}\n\n        <div style={{ padding: '0.75rem' }}>\n          {/* Column labels row */}\n          {hasColLabels && (\n            <div\n              style={{\n                display:     'flex',\n                marginLeft:  hasRowLabels ? '3rem' : 0,\n                marginBottom: '0.25rem',\n                gap:          '2px',\n              }}\n            >\n              {colLabels!.slice(0, columns).map((lbl, i) => (\n                <div\n                  key={i}\n                  style={{\n                    width:         cellSize,\n                    textAlign:     'center',\n                    fontSize:      '0.5rem',\n                    color:         'var(--text-muted)',\n                    letterSpacing: '0.04em',\n                    overflow:      'hidden',\n                    whiteSpace:    'nowrap',\n                    textOverflow:  'ellipsis',\n                    flexShrink:    0,\n                  }}\n                >\n                  {lbl}\n                </div>\n              ))}\n            </div>\n          )}\n\n          {/* Grid rows */}\n          <div style={{ display: 'flex', flexDirection: 'column', gap: '2px' }}>\n            {Array.from({ length: rows }).map((_, rowIdx) => (\n              <div key={rowIdx} style={{ display: 'flex', alignItems: 'center', gap: '2px' }}>\n                {/* Row label */}\n                {hasRowLabels && (\n                  <div\n                    style={{\n                      width:         '2.75rem',\n                      fontSize:      '0.5rem',\n                      color:         'var(--text-muted)',\n                      letterSpacing: '0.04em',\n                      textTransform: 'uppercase',\n                      overflow:      'hidden',\n                      whiteSpace:    'nowrap',\n                      textOverflow:  'ellipsis',\n                      flexShrink:    0,\n                      paddingRight:  '0.25rem',\n                      textAlign:     'right',\n                    }}\n                  >\n                    {rowLabels![rowIdx] ?? ''}\n                  </div>\n                )}\n\n                {/* Cells in this row */}\n                {Array.from({ length: columns }).map((_, colIdx) => {\n                  const cellIdx  = rowIdx * columns + colIdx\n                  const cell     = cells[cellIdx]\n                  if (!cell) {\n                    // Pad with empty cell to fill the grid\n                    return (\n                      <div\n                        key={colIdx}\n                        style={{\n                          width:      cellSize,\n                          height:     cellSize,\n                          flexShrink: 0,\n                        }}\n                      />\n                    )\n                  }\n                  const alpha = 0.06 + (cell.value / computedMax) * 0.88\n\n                  return (\n                    <div\n                      key={colIdx}\n                      title={cell.label ?? String(cell.value)}\n                      style={{\n                        width:      cellSize,\n                        height:     cellSize,\n                        position:   'relative',\n                        background: 'var(--surface-raised)',\n                        flexShrink: 0,\n                        cursor:     'default',\n                      }}\n                    >\n                      {/* Color intensity layer */}\n                      <div\n                        style={{\n                          position:   'absolute',\n                          inset:      0,\n                          background: accentColor,\n                          opacity:    Math.min(alpha, 1),\n                        }}\n                      />\n                    </div>\n                  )\n                })}\n              </div>\n            ))}\n          </div>\n        </div>\n      </div>\n    )\n  }\n)\nHeatmap.displayName = 'Heatmap'\n\nexport { Heatmap }\n"
    }
  ]
}