{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "icon-base",
  "type": "registry:ui",
  "title": "Icon base",
  "description": "Shared React runtime for AI Canvas icons.",
  "author": "aicanvas <https://aicanvas.me>",
  "files": [
    {
      "path": "components/icons/IconBase.tsx",
      "content": "// AI Canvas icon runtime (aicanvas.me). Free under the AI Canvas Premium License; keep this notice.\nimport { Children, createElement, forwardRef, Fragment, isValidElement, type CSSProperties, type ReactNode, type SVGProps } from 'react'\n\nexport type IconNode = ReadonlyArray<readonly ['path' | 'circle', Readonly<Record<string, string>>]>\n\nexport type IconStyle = 'line' | 'duotone' | 'solid' | 'solid-duotone'\n\nexport type IconWeight = 'thin' | 'light' | 'regular' | 'bold' | 'extrabold'\n\n// x1.01: Chrome draws a stroke of exactly one device pixel as broken dots.\nconst STROKE: Record<IconWeight, number> = { thin: 0.7575, light: 1.13625, regular: 1.515, bold: 1.89375, extrabold: 2.2725 }\n\nexport interface IconProps extends Omit<SVGProps<SVGSVGElement>, 'ref' | 'strokeWidth'> {\n  size?: number | string\n  color?: string\n  weight?: IconWeight\n}\n\nexport interface IconProviderProps {\n  weight?: IconWeight\n  color?: string\n  children: ReactNode\n}\n\ntype ProviderStyle = CSSProperties & { '--aic-icon-stroke'?: number }\n\n// Sets --aic-icon-stroke; icons read it, so no context is needed.\nexport function IconProvider({ weight, color, children }: IconProviderProps) {\n  const style: ProviderStyle = { display: 'contents' }\n  if (weight !== undefined) style['--aic-icon-stroke'] = STROKE[weight]\n  if (color !== undefined) style.color = color\n  return createElement('span', { style }, children)\n}\n\nconst pascal = (name: string) => name.split('-').map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join('').replace(/^(\\d)/, 'Icon$1')\n\nconst hasTitle = (nodes: ReactNode): boolean => Children.toArray(nodes).some((child) =>\n  isValidElement<{ children?: ReactNode }>(child) && (child.type === 'title' || (child.type === Fragment && hasTitle(child.props.children))))\n\nexport function createIcon(name: string, iconStyle: IconStyle, node: IconNode) {\n  const stroked = iconStyle === 'line' || iconStyle === 'duotone'\n  const Icon = forwardRef<SVGSVGElement, IconProps>(function Icon(props, ref) {\n    const {\n      size = 24,\n      color = 'currentColor',\n      weight,\n      className,\n      children,\n      style,\n      ...rest\n    } = props\n    const titled = hasTitle(children)\n    const labelled = titled || rest['aria-label'] != null || rest['aria-labelledby'] != null || rest.role != null\n    return createElement(\n      'svg',\n      {\n        ref,\n        xmlns: 'http://www.w3.org/2000/svg',\n        // Attributes, not style, so a sizing class like size-4 still wins.\n        width: size,\n        height: size,\n        viewBox: '0 0 24 24',\n        fill: 'none',\n        color,\n        stroke: 'currentColor',\n        strokeLinecap: 'round',\n        strokeLinejoin: 'round',\n        className: ['aic-icon', 'aic-icon-' + name, iconStyle !== 'line' && 'aic-icon-' + iconStyle, className].filter(Boolean).join(' '),\n        style: stroked ? { ...style, strokeWidth: (weight && STROKE[weight]) || 'var(--aic-icon-stroke, 1.515)' } : style,\n        ...(labelled ? (titled && rest.role == null ? { role: 'img' } : {}) : { 'aria-hidden': true }),\n        ...rest,\n      },\n      ...node.map(([tag, attrs], index) => createElement(tag, { key: index, ...attrs })),\n      children,\n    )\n  })\n  Icon.displayName = pascal(name) + (iconStyle === 'line' ? '' : pascal(iconStyle))\n  return Icon\n}\n",
      "type": "registry:ui",
      "target": "components/icons/IconBase.tsx"
    }
  ]
}
