{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "code-editor",
  "type": "registry:ui",
  "title": "Code Editor",
  "description": "A code editor component featuring syntax highlighting and animation.",
  "dependencies": [
    "motion",
    "shiki"
  ],
  "registryDependencies": [
    "https://animate-ui.com/r/copy-button"
  ],
  "files": [
    {
      "path": "registry/components/code-editor/index.tsx",
      "content": "'use client';\n\nimport * as React from 'react';\nimport { useInView, type UseInViewOptions } from 'motion/react';\nimport { useTheme } from 'next-themes';\n\nimport { cn } from '@/lib/utils';\nimport { CopyButton } from '@/components/animate-ui/buttons/copy';\n\ntype CodeEditorProps = Omit<React.ComponentProps<'div'>, 'onCopy'> & {\n  children: string;\n  lang: string;\n  themes?: {\n    light: string;\n    dark: string;\n  };\n  duration?: number;\n  delay?: number;\n  header?: boolean;\n  dots?: boolean;\n  icon?: React.ReactNode;\n  cursor?: boolean;\n  inView?: boolean;\n  inViewMargin?: UseInViewOptions['margin'];\n  inViewOnce?: boolean;\n  copyButton?: boolean;\n  writing?: boolean;\n  title?: string;\n  onDone?: () => void;\n  onCopy?: (content: string) => void;\n};\n\nfunction CodeEditor({\n  children: code,\n  lang,\n  themes = {\n    light: 'github-light',\n    dark: 'github-dark',\n  },\n  duration = 5,\n  delay = 0,\n  className,\n  header = true,\n  dots = true,\n  icon,\n  cursor = false,\n  inView = false,\n  inViewMargin = '0px',\n  inViewOnce = true,\n  copyButton = false,\n  writing = true,\n  title,\n  onDone,\n  onCopy,\n  ...props\n}: CodeEditorProps) {\n  const { resolvedTheme } = useTheme();\n\n  const editorRef = React.useRef<HTMLDivElement>(null);\n  const [visibleCode, setVisibleCode] = React.useState('');\n  const [highlightedCode, setHighlightedCode] = React.useState('');\n  const [isDone, setIsDone] = React.useState(false);\n\n  const inViewResult = useInView(editorRef, {\n    once: inViewOnce,\n    margin: inViewMargin,\n  });\n  const isInView = !inView || inViewResult;\n\n  React.useEffect(() => {\n    if (!visibleCode.length || !isInView) return;\n\n    const loadHighlightedCode = async () => {\n      try {\n        const { codeToHtml } = await import('shiki');\n\n        const highlighted = await codeToHtml(visibleCode, {\n          lang,\n          themes: {\n            light: themes.light,\n            dark: themes.dark,\n          },\n          defaultColor: resolvedTheme === 'dark' ? 'dark' : 'light',\n        });\n\n        setHighlightedCode(highlighted);\n      } catch (e) {\n        console.error(`Language \"${lang}\" could not be loaded.`, e);\n      }\n    };\n\n    loadHighlightedCode();\n  }, [\n    lang,\n    themes,\n    writing,\n    isInView,\n    duration,\n    delay,\n    visibleCode,\n    resolvedTheme,\n  ]);\n\n  React.useEffect(() => {\n    if (!writing) {\n      setVisibleCode(code);\n      onDone?.();\n      return;\n    }\n\n    if (!code.length || !isInView) return;\n\n    const characters = Array.from(code);\n    let index = 0;\n    const totalDuration = duration * 1000;\n    const interval = totalDuration / characters.length;\n    let intervalId: NodeJS.Timeout;\n\n    const timeout = setTimeout(() => {\n      intervalId = setInterval(() => {\n        if (index < characters.length) {\n          setVisibleCode((prev) => {\n            const currentIndex = index;\n            index += 1;\n            return prev + characters[currentIndex];\n          });\n          editorRef.current?.scrollTo({\n            top: editorRef.current?.scrollHeight,\n            behavior: 'smooth',\n          });\n        } else {\n          clearInterval(intervalId);\n          setIsDone(true);\n          onDone?.();\n        }\n      }, interval);\n    }, delay * 1000);\n\n    return () => {\n      clearTimeout(timeout);\n      clearInterval(intervalId);\n    };\n  }, [code, duration, delay, isInView, writing, onDone]);\n\n  return (\n    <div\n      data-slot=\"code-editor\"\n      className={cn(\n        'relative bg-muted/50 w-[600px] h-[400px] border border-border overflow-hidden flex flex-col rounded-xl',\n        className,\n      )}\n      {...props}\n    >\n      {header ? (\n        <div className=\"bg-muted border-b border-border/75 dark:border-border/50 relative flex flex-row items-center justify-between gap-y-2 h-10 px-4\">\n          {dots && (\n            <div className=\"flex flex-row gap-x-2\">\n              <div className=\"size-2 rounded-full bg-red-500\"></div>\n              <div className=\"size-2 rounded-full bg-yellow-500\"></div>\n              <div className=\"size-2 rounded-full bg-green-500\"></div>\n            </div>\n          )}\n\n          {title && (\n            <div\n              className={cn(\n                'flex flex-row items-center gap-2',\n                dots &&\n                  'absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2',\n              )}\n            >\n              {icon ? (\n                <div\n                  className=\"text-muted-foreground [&_svg]:size-3.5\"\n                  dangerouslySetInnerHTML={\n                    typeof icon === 'string' ? { __html: icon } : undefined\n                  }\n                >\n                  {typeof icon !== 'string' ? icon : null}\n                </div>\n              ) : null}\n              <figcaption className=\"flex-1 truncate text-muted-foreground text-[13px]\">\n                {title}\n              </figcaption>\n            </div>\n          )}\n\n          {copyButton ? (\n            <CopyButton\n              content={code}\n              size=\"sm\"\n              variant=\"ghost\"\n              className=\"-me-2 bg-transparent hover:bg-black/5 dark:hover:bg-white/10\"\n              onCopy={onCopy}\n            />\n          ) : null}\n        </div>\n      ) : (\n        copyButton && (\n          <CopyButton\n            content={code}\n            size=\"sm\"\n            variant=\"ghost\"\n            className=\"absolute right-2 top-2 z-[2] backdrop-blur-md bg-transparent hover:bg-black/5 dark:hover:bg-white/10\"\n            onCopy={onCopy}\n          />\n        )\n      )}\n      <div\n        ref={editorRef}\n        className=\"h-[calc(100%-2.75rem)] w-full text-sm p-4 font-mono relative overflow-auto flex-1\"\n      >\n        <div\n          className={cn(\n            '[&>pre,_&_code]:!bg-transparent [&>pre,_&_code]:[background:transparent_!important] [&>pre,_&_code]:border-none [&_code]:!text-[13px]',\n            cursor &&\n              !isDone &&\n              \"[&_.line:last-of-type::after]:content-['|'] [&_.line:last-of-type::after]:animate-pulse [&_.line:last-of-type::after]:inline-block [&_.line:last-of-type::after]:w-[1ch] [&_.line:last-of-type::after]:-translate-px\",\n          )}\n          dangerouslySetInnerHTML={{ __html: highlightedCode }}\n        />\n      </div>\n    </div>\n  );\n}\n\nexport { CodeEditor, type CodeEditorProps };\n",
      "type": "registry:ui",
      "target": "components/animate-ui/components/code-editor.tsx"
    }
  ]
}