{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "pixel-paragraph-words",
  "type": "registry:ui",
  "description": "Paragraph where specific words render in an interactive pixel font that swaps or cycles on hover",
  "dependencies": [
    "geist"
  ],
  "files": [
    {
      "path": "registry/default/ui/pixel-paragraph-words.tsx",
      "content": "/**\n * @module PixelParagraph\n *\n * Renders a paragraph where specific words / phrases use a pixel font\n * while the rest of the text stays in the normal font.\n *\n * Setup — Geist Pixel Fonts with Tailwind CSS\n * =============================================\n *\n * All Geist fonts can be used through CSS variables:\n *\n *   GeistSans:          --font-geist-sans\n *   GeistMono:          --font-geist-mono\n *   GeistPixelSquare:   --font-geist-pixel-square\n *   GeistPixelGrid:     --font-geist-pixel-grid\n *   GeistPixelCircle:   --font-geist-pixel-circle\n *   GeistPixelTriangle: --font-geist-pixel-triangle\n *   GeistPixelLine:     --font-geist-pixel-line\n *\n * 1. Register the font variables in app/layout.js:\n *\n *   ```js\n *   import { GeistSans } from \"geist/font/sans\";\n *   import { GeistMono } from \"geist/font/mono\";\n *   import { GeistPixelSquare } from \"geist/font/pixel\";\n *\n *   export default function RootLayout({ children }) {\n *     return (\n *       <html\n *         lang=\"en\"\n *         className={`${GeistSans.variable} ${GeistMono.variable} ${GeistPixelSquare.variable}`}\n *       >\n *         <body>{children}</body>\n *       </html>\n *     );\n *   }\n *   ```\n *\n * 2. Map the CSS variables in your Tailwind CSS v4 theme (tailwind.css):\n *\n *   ```css\n *   @theme {\n *     --font-sans: var(--font-geist-sans);\n *     --font-mono: var(--font-geist-mono);\n *     --font-pixel-square: var(--font-geist-pixel-square);\n *     --font-pixel-grid: var(--font-geist-pixel-grid);\n *     --font-pixel-circle: var(--font-geist-pixel-circle);\n *     --font-pixel-triangle: var(--font-geist-pixel-triangle);\n *     --font-pixel-line: var(--font-geist-pixel-line);\n *   }\n *   ```\n *\n * Once configured, the `font-pixel-*` utility classes used by this\n * component will resolve correctly.\n *\n * @example\n * <PixelParagraph\n *   text=\"54+ animated components and effects. Free, open source, and built to drop into any shadcn/ui project.\"\n *   pixelWords={[\"animated\", \"shadcn/ui\"]}\n *   font=\"square\"\n *   className=\"text-lg text-muted-foreground\"\n * />\n */\n\nimport { cn } from \"@/lib/utils\"\n\n/* ------------------------------------------------------------------ */\n/* Pixel-font constants                                                */\n/* ------------------------------------------------------------------ */\n\ntype PixelFont = \"square\" | \"grid\" | \"circle\" | \"triangle\" | \"line\"\n\nconst PIXEL_FONT_MAP: Record<PixelFont, string> = {\n  square: \"font-pixel-square\",\n  grid: \"font-pixel-grid\",\n  circle: \"font-pixel-circle\",\n  triangle: \"font-pixel-triangle\",\n  line: \"font-pixel-line\",\n}\n\n/* ------------------------------------------------------------------ */\n/* Text-splitting helper                                               */\n/* ------------------------------------------------------------------ */\n\ntype Segment = { type: \"plain\"; text: string } | { type: \"pixel\"; text: string }\n\n/**\n * Splits `text` into alternating plain / pixel segments based on the\n * provided `pixelWords`.  Longer phrases are matched first so that\n * \"shadcn/ui\" wins over a hypothetical \"ui\" match.\n */\nfunction splitTextByPixelWords(text: string, pixelWords: string[]): Segment[] {\n  if (pixelWords.length === 0) return [{ type: \"plain\", text }]\n\n  // Sort by length descending so longer matches take priority\n  const sorted = [...pixelWords].sort((a, b) => b.length - a.length)\n\n  // Escape regex-special characters in each word\n  const escaped = sorted.map((w) => w.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\"))\n\n  const pattern = new RegExp(`(${escaped.join(\"|\")})`, \"g\")\n\n  const segments: Segment[] = []\n  let lastIndex = 0\n\n  for (const match of text.matchAll(pattern)) {\n    const matchStart = match.index ?? 0\n    if (matchStart > lastIndex) {\n      segments.push({ type: \"plain\", text: text.slice(lastIndex, matchStart) })\n    }\n    segments.push({ type: \"pixel\", text: match[0] })\n    lastIndex = matchStart + match[0].length\n  }\n\n  if (lastIndex < text.length) {\n    segments.push({ type: \"plain\", text: text.slice(lastIndex) })\n  }\n\n  return segments\n}\n\n/* ------------------------------------------------------------------ */\n/* PixelParagraph                                                      */\n/* ------------------------------------------------------------------ */\n\nexport interface PixelParagraphProps extends React.ComponentProps<\"p\"> {\n  /** The paragraph text to render. */\n  text: string\n  /**\n   * Words or phrases within `text` to render in a pixel font.\n   * Matching is case-sensitive and longest-match-first.\n   */\n  pixelWords?: string[]\n  /** The wrapper element to render. @default \"p\" */\n  as?: \"p\" | \"span\" | \"div\"\n  /** The pixel font for highlighted words. @default \"square\" */\n  font?: PixelFont\n  /** Extra className applied to each pixel-word span. */\n  pixelWordClassName?: string\n}\n\n/**\n * Paragraph that renders specific words / phrases in a pixel font\n * while the rest stays in the normal typeface.\n *\n * @example\n * <PixelParagraph\n *   text=\"54+ animated components and effects. Free, open source, and built to drop into any shadcn/ui project.\"\n *   pixelWords={[\"animated\", \"shadcn/ui\"]}\n *   font=\"square\"\n *   className=\"text-lg text-muted-foreground\"\n * />\n */\nexport function PixelParagraph({\n  text,\n  pixelWords = [],\n  as: Tag = \"p\",\n  className,\n  font = \"square\",\n  pixelWordClassName,\n  ...props\n}: PixelParagraphProps) {\n  const segments = splitTextByPixelWords(text, pixelWords)\n  const fontClass = PIXEL_FONT_MAP[font]\n\n  return (\n    <Tag data-slot=\"pixel-paragraph\" className={cn(className)} {...props}>\n      {segments.map((segment, index) => {\n        const key = `${segment.type}-${segment.text}-${index}`\n        return segment.type === \"pixel\" ? (\n          <span\n            key={key}\n            data-slot=\"pixel-word\"\n            className={cn(fontClass, pixelWordClassName)}\n          >\n            {segment.text}\n          </span>\n        ) : (\n          <span key={key}>{segment.text}</span>\n        )\n      })}\n    </Tag>\n  )\n}\n",
      "type": "registry:ui"
    }
  ]
}