{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "chart",
  "title": "Chart",
  "description": "Recharts wired to the five chart colors, terminal tooltip and stepped reveal.",
  "dependencies": [
    "recharts"
  ],
  "registryDependencies": [
    "@afterglow/theme"
  ],
  "files": [
    {
      "path": "registry/terminal/ui/chart.tsx",
      "content": "\"use client\";\n\nimport {\n  type ComponentProps,\n  type ComponentType,\n  type CSSProperties,\n  createContext,\n  type ReactNode,\n  useContext,\n  useId,\n  useMemo,\n} from \"react\";\nimport {\n  type DefaultLegendContentProps,\n  type DefaultTooltipContentProps,\n  Legend,\n  ResponsiveContainer,\n  Tooltip,\n  type TooltipPayloadEntry,\n  type TooltipValueType,\n} from \"recharts\";\n\nimport { cn } from \"@/lib/utils\";\n\nconst THEMES = { dark: \".dark\", light: \"\" } as const;\nconst INITIAL_DIMENSION = { height: 200, width: 320 } as const;\n\ntype TooltipNameType = number | string;\n\nexport type ChartConfig = Record<\n  string,\n  {\n    icon?: ComponentType;\n    label?: ReactNode;\n  } & (\n    | { color?: string; theme?: never }\n    | { color?: never; theme: Record<keyof typeof THEMES, string> }\n  )\n>;\n\ninterface ChartContextProps {\n  config: ChartConfig;\n}\n\nconst ChartContext = createContext<ChartContextProps | null>(null);\n\nfunction useChart() {\n  const context = useContext(ChartContext);\n\n  if (!context) {\n    throw new Error(\"useChart must be used within a <ChartContainer />\");\n  }\n\n  return context;\n}\n\nfunction ChartContainer({\n  id,\n  className,\n  children,\n  config,\n  initialDimension = INITIAL_DIMENSION,\n  ...props\n}: ComponentProps<\"div\"> & {\n  children: ComponentProps<typeof ResponsiveContainer>[\"children\"];\n  config: ChartConfig;\n  initialDimension?: { height: number; width: number };\n}) {\n  const uniqueId = useId();\n  const chartId = `chart-${id ?? uniqueId.replaceAll(\":\", \"\")}`;\n\n  return (\n    <ChartContext.Provider value={{ config }}>\n      <div\n        className={cn(\n          \"flex aspect-video justify-center overflow-hidden font-mono text-xs [&_.recharts-cartesian-axis-tick_text]:fill-muted-foreground [&_.recharts-cartesian-grid_line[stroke='#ccc']]:stroke-border/60 [&_.recharts-curve.recharts-tooltip-cursor]:stroke-line-strong [&_.recharts-dot[stroke='#fff']]:stroke-transparent [&_.recharts-layer]:outline-hidden [&_.recharts-polar-grid_[stroke='#ccc']]:stroke-border/60 [&_.recharts-radial-bar-background-sector]:fill-muted [&_.recharts-rectangle.recharts-tooltip-cursor]:fill-muted/80 [&_.recharts-reference-line_[stroke='#ccc']]:stroke-border [&_.recharts-sector]:outline-hidden [&_.recharts-sector[stroke='#fff']]:stroke-transparent [&_.recharts-surface]:outline-hidden\",\n          className\n        )}\n        data-chart={chartId}\n        data-slot=\"chart\"\n        {...props}\n      >\n        <ChartStyle config={config} id={chartId} />\n        <div className=\"size-full animate-chart-in\">\n          <ResponsiveContainer initialDimension={initialDimension}>\n            {children}\n          </ResponsiveContainer>\n        </div>\n      </div>\n    </ChartContext.Provider>\n  );\n}\n\nfunction ChartStyle({ id, config }: { config: ChartConfig; id: string }) {\n  const colorConfig = Object.entries(config).filter(\n    ([, itemConfig]) => itemConfig.theme ?? itemConfig.color\n  );\n\n  if (colorConfig.length === 0) {\n    return null;\n  }\n\n  return (\n    <style>\n      {Object.entries(THEMES)\n        .map(([theme, prefix]) => {\n          const variables = colorConfig\n            .map(([key, itemConfig]) => {\n              const color =\n                itemConfig.theme?.[theme as keyof typeof itemConfig.theme] ??\n                itemConfig.color;\n              return color ? `--color-${key}: ${color};` : \"\";\n            })\n            .join(\"\");\n\n          return `${prefix} [data-chart=\"${id}\"] { ${variables} }`;\n        })\n        .join(\"\\n\")}\n    </style>\n  );\n}\n\nconst ChartTooltip = Tooltip;\n\nfunction ChartTooltipContent({\n  active,\n  payload,\n  className,\n  indicator = \"dot\",\n  hideLabel = false,\n  hideIndicator = false,\n  label,\n  labelFormatter,\n  labelClassName,\n  formatter,\n  color,\n  nameKey,\n  labelKey,\n}: ComponentProps<typeof Tooltip> &\n  ComponentProps<\"div\"> & {\n    hideIndicator?: boolean;\n    hideLabel?: boolean;\n    indicator?: \"dashed\" | \"dot\" | \"line\";\n    labelKey?: string;\n    nameKey?: string;\n  } & Omit<\n    DefaultTooltipContentProps<TooltipValueType, TooltipNameType>,\n    \"accessibilityLayer\"\n  >) {\n  const { config } = useChart();\n\n  const tooltipLabel = useMemo(() => {\n    if (hideLabel || !payload?.length) {\n      return null;\n    }\n\n    const [item] = payload;\n    const key = `${labelKey ?? item?.dataKey ?? item?.name ?? \"value\"}`;\n    const itemConfig = getPayloadConfigFromPayload(config, item, key);\n    const value =\n      !labelKey && typeof label === \"string\"\n        ? (config[label]?.label ?? label)\n        : itemConfig?.label;\n\n    if (labelFormatter) {\n      return (\n        <div className={cn(\"font-medium text-phosphor-bright\", labelClassName)}>\n          {labelFormatter(value, payload)}\n        </div>\n      );\n    }\n\n    return value ? (\n      <div className={cn(\"font-medium text-phosphor-bright\", labelClassName)}>\n        {value}\n      </div>\n    ) : null;\n  }, [\n    config,\n    hideLabel,\n    label,\n    labelClassName,\n    labelFormatter,\n    labelKey,\n    payload,\n  ]);\n\n  if (!(active && payload?.length)) {\n    return null;\n  }\n\n  const nestLabel = payload.length === 1 && indicator !== \"dot\";\n\n  return (\n    <div\n      className={cn(\n        \"grid min-w-32 items-start gap-1.5 border border-line-strong bg-popover px-2.5 py-1.5 text-xs shadow-panel\",\n        className\n      )}\n    >\n      {nestLabel ? null : <div className=\"contents\">{tooltipLabel}</div>}\n      <div className=\"grid gap-1.5\">\n        {payload\n          .filter((item) => item.type !== \"none\")\n          .map((item, index) => {\n            const key = `${nameKey ?? item.name ?? item.dataKey ?? \"value\"}`;\n\n            return (\n              <ChartTooltipItem\n                color={color}\n                config={config}\n                formatter={formatter}\n                hideIndicator={hideIndicator}\n                index={index}\n                indicator={indicator}\n                item={item}\n                key={key}\n                nestLabel={nestLabel}\n                payload={payload}\n                tooltipLabel={tooltipLabel}\n              />\n            );\n          })}\n      </div>\n    </div>\n  );\n}\n\ninterface ChartTooltipItemProps {\n  color?: string;\n  config: ChartConfig;\n  formatter: DefaultTooltipContentProps<\n    TooltipValueType,\n    TooltipNameType\n  >[\"formatter\"];\n  hideIndicator: boolean;\n  index: number;\n  indicator: \"dashed\" | \"dot\" | \"line\";\n  item: TooltipPayloadEntry<TooltipValueType, TooltipNameType>;\n  nestLabel: boolean;\n  payload: readonly TooltipPayloadEntry<TooltipValueType, TooltipNameType>[];\n  tooltipLabel: ReactNode;\n}\n\nfunction ChartTooltipItem({\n  color,\n  config,\n  formatter,\n  hideIndicator,\n  index,\n  indicator,\n  item,\n  nestLabel,\n  payload,\n  tooltipLabel,\n}: ChartTooltipItemProps) {\n  const key = `${item.name ?? item.dataKey ?? \"value\"}`;\n  const itemConfig = getPayloadConfigFromPayload(config, item, key);\n  const indicatorColor = color ?? item.payload?.fill ?? item.color;\n  const Icon = itemConfig?.icon;\n  const formattedItem = getFormattedTooltipItem({\n    formatter,\n    index,\n    item,\n    payload,\n  });\n\n  if (formattedItem) {\n    return formattedItem;\n  }\n\n  return (\n    <div\n      className={cn(\n        \"flex w-full flex-wrap items-stretch gap-2 [&>svg]:size-2.5 [&>svg]:text-muted-foreground\",\n        indicator === \"dot\" && \"items-center\"\n      )}\n    >\n      <ChartTooltipMarker\n        color={indicatorColor}\n        hidden={hideIndicator}\n        Icon={Icon}\n        indicator={indicator}\n        nested={nestLabel}\n      />\n      <div\n        className={cn(\n          \"flex flex-1 justify-between gap-4 leading-none\",\n          nestLabel ? \"items-end\" : \"items-center\"\n        )}\n      >\n        <div className=\"grid gap-1.5\">\n          {nestLabel ? <div className=\"contents\">{tooltipLabel}</div> : null}\n          <span className=\"text-muted-foreground\">\n            {itemConfig?.label ?? item.name}\n          </span>\n        </div>\n        <ChartTooltipValue value={item.value} />\n      </div>\n    </div>\n  );\n}\n\nfunction getFormattedTooltipItem({\n  formatter,\n  index,\n  item,\n  payload,\n}: Pick<ChartTooltipItemProps, \"formatter\" | \"index\" | \"item\" | \"payload\">) {\n  if (!(formatter && item.value !== undefined && item.name !== undefined)) {\n    return null;\n  }\n\n  return (\n    <div className=\"flex w-full flex-wrap items-stretch gap-2\">\n      {formatter(item.value, item.name, item, index, payload)}\n    </div>\n  );\n}\n\nfunction ChartTooltipMarker({\n  color,\n  hidden,\n  Icon,\n  indicator,\n  nested,\n}: {\n  color: string | undefined;\n  hidden: boolean;\n  Icon: ChartConfig[string][\"icon\"];\n  indicator: \"dashed\" | \"dot\" | \"line\";\n  nested: boolean;\n}) {\n  if (Icon) {\n    return <Icon />;\n  }\n\n  return (\n    <ChartTooltipIndicator\n      color={color}\n      hidden={hidden}\n      indicator={indicator}\n      nested={nested}\n    />\n  );\n}\n\nfunction ChartTooltipValue({\n  value,\n}: {\n  value: TooltipPayloadEntry<TooltipValueType, TooltipNameType>[\"value\"];\n}) {\n  if (value === null || value === undefined) {\n    return null;\n  }\n\n  return (\n    <span className=\"font-medium text-foreground tabular-nums\">\n      {typeof value === \"number\" ? value.toLocaleString() : String(value)}\n    </span>\n  );\n}\n\nfunction ChartTooltipIndicator({\n  color,\n  hidden,\n  indicator,\n  nested,\n}: {\n  color: string | undefined;\n  hidden: boolean;\n  indicator: \"dashed\" | \"dot\" | \"line\";\n  nested: boolean;\n}) {\n  if (hidden) {\n    return null;\n  }\n\n  return (\n    <div\n      className={cn(\n        \"shrink-0 border-(--color-border) bg-(--color-bg)\",\n        indicator === \"dot\" && \"size-2.5\",\n        indicator === \"line\" && \"w-1\",\n        indicator === \"dashed\" &&\n          \"w-0 border-[1.5px] border-dashed bg-transparent\",\n        nested && indicator === \"dashed\" && \"my-0.5\"\n      )}\n      style={\n        {\n          \"--color-bg\": color,\n          \"--color-border\": color,\n        } as CSSProperties\n      }\n    />\n  );\n}\n\nconst ChartLegend = Legend;\n\nfunction ChartLegendContent({\n  className,\n  hideIcon = false,\n  payload,\n  verticalAlign = \"bottom\",\n  nameKey,\n}: ComponentProps<\"div\"> & {\n  hideIcon?: boolean;\n  nameKey?: string;\n} & DefaultLegendContentProps) {\n  const { config } = useChart();\n\n  if (!payload?.length) {\n    return null;\n  }\n\n  return (\n    <div\n      className={cn(\n        \"flex items-center justify-center gap-4\",\n        verticalAlign === \"top\" ? \"pb-3\" : \"pt-3\",\n        className\n      )}\n    >\n      {payload\n        .filter((item) => item.type !== \"none\")\n        .map((item) => {\n          const key = `${nameKey ?? item.dataKey ?? \"value\"}`;\n          const itemConfig = getPayloadConfigFromPayload(config, item, key);\n\n          return (\n            <div\n              className=\"flex items-center gap-1.5 [&>svg]:size-3 [&>svg]:text-muted-foreground\"\n              key={item.value}\n            >\n              {itemConfig?.icon && !hideIcon ? (\n                <itemConfig.icon />\n              ) : (\n                <div\n                  className=\"size-2 shrink-0\"\n                  style={{ backgroundColor: item.color }}\n                />\n              )}\n              {itemConfig?.label}\n            </div>\n          );\n        })}\n    </div>\n  );\n}\n\nfunction getPayloadConfigFromPayload(\n  config: ChartConfig,\n  payload: unknown,\n  key: string\n) {\n  if (typeof payload !== \"object\" || payload === null) {\n    return;\n  }\n\n  const payloadPayload =\n    \"payload\" in payload &&\n    typeof payload.payload === \"object\" &&\n    payload.payload !== null\n      ? payload.payload\n      : undefined;\n\n  let configLabelKey = key;\n\n  if (\n    key in payload &&\n    typeof payload[key as keyof typeof payload] === \"string\"\n  ) {\n    configLabelKey = payload[key as keyof typeof payload] as string;\n  } else if (\n    payloadPayload &&\n    key in payloadPayload &&\n    typeof payloadPayload[key as keyof typeof payloadPayload] === \"string\"\n  ) {\n    configLabelKey = payloadPayload[\n      key as keyof typeof payloadPayload\n    ] as string;\n  }\n\n  return configLabelKey in config ? config[configLabelKey] : config[key];\n}\n\nexport {\n  ChartContainer,\n  ChartLegend,\n  ChartLegendContent,\n  ChartStyle,\n  ChartTooltip,\n  ChartTooltipContent,\n};\n",
      "type": "registry:ui"
    }
  ],
  "type": "registry:ui"
}