{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "code-block",
  "title": "Code Block",
  "description": "The shadcn Code Block component using Base UI and the Familis Nova theme.",
  "dependencies": [
    "class-variance-authority@^0.7.1",
    "cn@^0.4.0",
    "lucide-react@^1.46.0"
  ],
  "registryDependencies": [
    "Familis-co/registry/button",
    "Familis-co/registry/use-highlighted-code"
  ],
  "files": [
    {
      "path": "registry/familis/ui/code-block.tsx",
      "content": "\"use client\"\n\nimport * as React from \"react\"\nimport { cva, type VariantProps } from \"class-variance-authority\"\nimport { cn } from \"cn\"\n\nimport { Button } from \"@/registry/familis/ui/button\"\nimport { CheckIcon, CopyIcon } from \"lucide-react\"\n\nconst codeBlockVariants = cva(\n  \"group/code-block relative flex w-full min-w-0 flex-col overflow-hidden rounded-lg text-sm has-[>[data-slot=code-block-content]:focus-visible]:ring-3 has-[>[data-slot=code-block-content]:focus-visible]:ring-ring/50 data-[size=sm]:text-xs\",\n  {\n    variants: {\n      variant: {\n        default:\n          \"border border-input text-foreground has-[>[data-slot=code-block-content]:focus-visible]:border-ring dark:bg-input/30\",\n        destructive: \"border border-destructive/30 text-destructive dark:border-destructive/40\",\n      },\n    },\n    defaultVariants: {\n      variant: \"default\",\n    },\n  },\n)\n\nfunction CodeBlock({\n  className,\n  variant = \"default\",\n  size = \"default\",\n  ...props\n}: React.ComponentProps<\"div\"> &\n  VariantProps<typeof codeBlockVariants> & { size?: \"default\" | \"sm\" }) {\n  return (\n    <div\n      data-slot=\"code-block\"\n      data-variant={variant}\n      data-size={size}\n      className={cn(codeBlockVariants({ variant }), className)}\n      {...props}\n    />\n  )\n}\n\nfunction CodeBlockHeader({ className, ...props }: React.ComponentProps<\"div\">) {\n  return (\n    <div\n      data-slot=\"code-block-header\"\n      className={cn(\n        \"flex min-w-0 shrink-0 items-center gap-2 px-3 pt-3 text-muted-foreground group-data-[size=sm]/code-block:px-2.5 group-data-[size=sm]/code-block:pt-2.5 group-data-[variant=destructive]/code-block:text-destructive *:has-[>[data-slot=code-block-title]]:min-w-0\",\n        className,\n      )}\n      {...props}\n    />\n  )\n}\n\nfunction CodeBlockTitle({ className, ...props }: React.ComponentProps<\"div\">) {\n  return (\n    <div\n      data-slot=\"code-block-title\"\n      className={cn(\n        \"min-w-0 truncate font-sans text-sm font-medium text-foreground group-data-[size=sm]/code-block:text-xs group-data-[variant=destructive]/code-block:text-destructive [&_svg]:pointer-events-none [&_svg]:inline-block [&_svg]:align-middle [&_svg:not([class*='size-'])]:size-3.5 *:[svg]:me-1.5\",\n        className,\n      )}\n      {...props}\n    />\n  )\n}\n\nfunction CodeBlockActions({ className, ...props }: React.ComponentProps<\"div\">) {\n  return (\n    <div\n      data-slot=\"code-block-actions\"\n      className={cn(\n        \"-my-1 ms-auto -me-1.5 flex shrink-0 items-center gap-1 [[data-slot=code-block]>&]:absolute [[data-slot=code-block]>&]:end-2 [[data-slot=code-block]>&]:top-2 [[data-slot=code-block]>&]:z-10 [[data-slot=code-block]>&]:m-0 [[data-slot=code-block]>&]:opacity-0 [[data-slot=code-block]>&]:transition-opacity [[data-slot=code-block]>&]:group-focus-within/code-block:opacity-100 [[data-slot=code-block]>&]:group-hover/code-block:opacity-100 group-data-[size=sm]/code-block:[[data-slot=code-block]>&]:end-1.5 group-data-[size=sm]/code-block:[[data-slot=code-block]>&]:top-1.5 [[data-slot=code-block]>&]:pointer-coarse:opacity-100 [[data-slot=code-block]>&]:*:[button]:bg-background [[data-slot=code-block]>&]:*:[button]:ring-1 [[data-slot=code-block]>&]:*:[button]:ring-foreground/10\",\n        className,\n      )}\n      {...props}\n    />\n  )\n}\n\nfunction CodeBlockCopyButton({\n  value,\n  variant = \"ghost\",\n  size = \"icon-xs\",\n  className,\n  onClick,\n  ...props\n}: React.ComponentProps<typeof Button> & { value: string }) {\n  const [copied, setCopied] = React.useState(false)\n\n  React.useEffect(() => {\n    if (!copied) {\n      return\n    }\n\n    const timeout = setTimeout(() => setCopied(false), 2000)\n    return () => clearTimeout(timeout)\n  }, [copied])\n\n  return (\n    <>\n      <Button\n        data-slot=\"code-block-copy-button\"\n        data-copied={copied || undefined}\n        variant={variant}\n        size={size}\n        aria-label={copied ? \"Copied\" : \"Copy\"}\n        className={cn(className)}\n        onClick={(event) => {\n          onClick?.(event)\n          navigator.clipboard\n            .writeText(value)\n            .then(() => setCopied(true))\n            .catch(() => {})\n        }}\n        {...props}\n      >\n        {copied ? <CheckIcon /> : <CopyIcon />}\n      </Button>\n      <span aria-live=\"polite\" className=\"sr-only\">\n        {copied ? \"Copied\" : \"\"}\n      </span>\n    </>\n  )\n}\n\nfunction CodeBlockContent({\n  className,\n  children,\n  style,\n  wrap = false,\n  lineNumbers = false,\n  startLine = 1,\n  ...props\n}: React.ComponentProps<\"pre\"> & {\n  wrap?: boolean\n  lineNumbers?: boolean\n  startLine?: number\n}) {\n  return (\n    <pre\n      data-slot=\"code-block-content\"\n      data-wrap={wrap || undefined}\n      data-numbered={lineNumbers || undefined}\n      dir=\"ltr\"\n      // The content scrolls, so keyboard users need a tab stop to scroll it.\n      // oxlint-disable-next-line jsx-a11y/no-noninteractive-tabindex\n      tabIndex={0}\n      style={{ counterReset: `line ${startLine - 1}`, ...style }}\n      className={cn(\n        \"min-h-0 min-w-0 scroll-fade-x scrollbar-thin overflow-auto px-3 py-3 text-start font-mono leading-relaxed outline-none group-has-data-[slot=code-block-footer]/code-block:pb-2 group-has-data-[slot=code-block-header]/code-block:pt-2 group-data-[size=sm]/code-block:px-2.5 group-data-[size=sm]/code-block:py-2.5 group-data-[size=sm]/code-block:group-has-data-[slot=code-block-header]/code-block:pt-1.5 data-wrap:scroll-fade-y data-wrap:wrap-break-word data-wrap:whitespace-pre-wrap\",\n        className,\n      )}\n      {...props}\n    >\n      <code\n        data-slot=\"code-block-code\"\n        className=\"grid min-h-lh min-w-max content-start in-data-numbered:grid-cols-[auto_1fr] in-data-wrap:min-w-0\"\n      >\n        {lineNumbers && typeof children === \"string\"\n          ? children\n              .replace(/\\n$/, \"\")\n              .split(\"\\n\")\n              .map((line, index) => <CodeBlockLine key={index}>{line}</CodeBlockLine>)\n          : children}\n      </code>\n    </pre>\n  )\n}\n\nconst codeBlockLineVariants = cva(\n  \"relative -mx-3 block min-h-lh px-3 group-data-[size=sm]/code-block:-mx-2.5 group-data-[size=sm]/code-block:px-2.5 before:min-w-6 before:text-end before:text-muted-foreground after:absolute after:start-1 after:top-0 after:text-muted-foreground after:select-none in-data-numbered:col-span-2 in-data-numbered:grid in-data-numbered:grid-cols-subgrid in-data-numbered:gap-x-3 in-data-numbered:[counter-increment:line] in-data-numbered:before:content-[counter(line)] in-data-numbered:before:select-none in-data-numbered:supports-[content:counter(line)_/_'']:before:content-[counter(line)_/_'']\",\n  {\n    variants: {\n      variant: {\n        default: \"\",\n        added: \"bg-muted after:content-['+']\",\n        removed: \"bg-destructive/10 after:text-destructive after:content-['−']\",\n        highlighted: \"bg-muted\",\n      },\n    },\n    defaultVariants: {\n      variant: \"default\",\n    },\n  },\n)\n\nfunction CodeBlockLine({\n  className,\n  variant = \"default\",\n  children,\n  ...props\n}: React.ComponentProps<\"span\"> & VariantProps<typeof codeBlockLineVariants>) {\n  // An empty line holds a newline so copying a selection keeps blank lines.\n  const empty =\n    children === undefined ||\n    children === null ||\n    children === \"\" ||\n    (Array.isArray(children) && children.length === 0)\n\n  return (\n    <span\n      data-slot=\"code-block-line\"\n      data-variant={variant}\n      className={cn(codeBlockLineVariants({ variant }), className)}\n      {...props}\n    >\n      <span data-slot=\"code-block-line-content\" className=\"min-w-0\">\n        {empty ? \"\\n\" : children}\n      </span>\n    </span>\n  )\n}\n\nfunction CodeBlockFooter({ className, ...props }: React.ComponentProps<\"div\">) {\n  return (\n    <div\n      data-slot=\"code-block-footer\"\n      className={cn(\n        \"flex min-w-0 shrink-0 items-center gap-2 px-3 pb-3 text-muted-foreground group-data-[size=sm]/code-block:px-2.5 group-data-[size=sm]/code-block:pb-2.5\",\n        className,\n      )}\n      {...props}\n    />\n  )\n}\n\nexport {\n  CodeBlock,\n  CodeBlockHeader,\n  CodeBlockTitle,\n  CodeBlockActions,\n  CodeBlockCopyButton,\n  CodeBlockContent,\n  CodeBlockLine,\n  CodeBlockFooter,\n}\n",
      "type": "registry:ui"
    }
  ],
  "meta": {
    "base": "base",
    "storybookId": "ui-code-block--default",
    "links": {
      "docs": "https://ui.shadcn.com/docs/components/base/code-block",
      "examples": "https://ui.shadcn.com/code/apps/v4/registry/bases/base/examples/code-block-example.tsx"
    }
  },
  "type": "registry:ui"
}