{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "terminal",
  "title": "Terminal",
  "type": "registry:ui",
  "files": [
    {
      "path": "packages/registry-react/components/terminal/Terminal.tsx",
      "content": "'use client'\n\nimport * as React from 'react'\nimport { cn } from '@/lib/utils'\n\nexport interface TerminalLine {\n  /** Prompt prefix shown before the command. Omit for output-only lines. */\n  prompt?: string\n  /** Command text shown after the prompt. */\n  command?: string\n  /** Output lines rendered below the command. */\n  output?: string\n  /** Override the line type: 'command' renders prompt+command, 'output' renders plain text. */\n  type?: 'command' | 'output'\n}\n\nexport interface TerminalProps extends Omit<React.HTMLAttributes<HTMLDivElement>, 'title'> {\n  /** Command history to render. */\n  lines: TerminalLine[]\n  /** Window title shown in the title bar. Default 'bash'. */\n  title?: string\n  /** Prompt character. Default '$'. */\n  promptChar?: string\n  /** Color theme. Default 'dark'. */\n  theme?: 'dark' | 'light'\n  /** Auto-scroll to bottom when new lines arrive. Default true. */\n  autoScroll?: boolean\n  /** Animate lines typing in one-by-one. Default false. */\n  typing?: boolean\n  /** Typing speed in ms per line. Default 120. */\n  typingSpeed?: number\n  /** Max height before scrolling. Default '400px'. */\n  maxHeight?: string\n}\n\nconst Terminal = React.forwardRef<HTMLDivElement, TerminalProps>(\n  (\n    {\n      lines,\n      title = 'bash',\n      promptChar = '$',\n      theme = 'dark',\n      autoScroll = true,\n      typing = false,\n      typingSpeed = 120,\n      maxHeight = '400px',\n      className,\n      ...props\n    },\n    ref,\n  ) => {\n    const bodyRef = React.useRef<HTMLDivElement | null>(null)\n    const [visibleCount, setVisibleCount] = React.useState(typing ? 0 : lines.length)\n    const typingTimerRef = React.useRef<ReturnType<typeof setTimeout> | null>(null)\n\n    const resolvedLines = React.useMemo(\n      () =>\n        lines.map((l) => ({\n          ...l,\n          type: l.type ?? (l.prompt || l.command ? 'command' : 'output'),\n          prompt: l.prompt ?? (l.type === 'output' ? '' : promptChar),\n        })),\n      [lines, promptChar],\n    )\n\n    const shownLines = resolvedLines.slice(0, visibleCount)\n\n    const scrollToBottom = React.useCallback(() => {\n      if (!autoScroll || !bodyRef.current) return\n      bodyRef.current.scrollTop = bodyRef.current.scrollHeight\n    }, [autoScroll])\n\n    // Reset/advance visible count when the lines array changes.\n    React.useEffect(() => {\n      if (typing) {\n        // Reset typing animation when lines change.\n        setVisibleCount(0)\n        return\n      }\n      setVisibleCount(lines.length)\n      // Scroll on the next paint once the new lines are rendered.\n      const raf = requestAnimationFrame(scrollToBottom)\n      return () => cancelAnimationFrame(raf)\n      // eslint-disable-next-line react-hooks/exhaustive-deps\n    }, [lines.length, typing])\n\n    // Drive the typing animation whenever visibleCount is below the total.\n    React.useEffect(() => {\n      if (!typing) return\n      if (visibleCount >= lines.length) return\n\n      typingTimerRef.current = setTimeout(() => {\n        setVisibleCount((c) => c + 1)\n        scrollToBottom()\n      }, typingSpeed)\n\n      return () => {\n        if (typingTimerRef.current) {\n          clearTimeout(typingTimerRef.current)\n          typingTimerRef.current = null\n        }\n      }\n    }, [typing, visibleCount, lines.length, typingSpeed, scrollToBottom])\n\n    // Initial scroll-to-bottom for the non-typing case.\n    React.useEffect(() => {\n      if (!typing) scrollToBottom()\n      // eslint-disable-next-line react-hooks/exhaustive-deps\n    }, [])\n\n    return (\n      <div\n        ref={ref}\n        data-uipkge=\"\"\n        data-slot=\"terminal\"\n        data-theme={theme}\n        className={cn(\n          'relative overflow-hidden rounded-lg border font-mono text-sm shadow-sm',\n          theme === 'dark' ? 'border-zinc-800 bg-zinc-950 text-zinc-200' : 'border-zinc-200 bg-zinc-50 text-zinc-800',\n          className,\n        )}\n        {...props}\n      >\n        {/* Title bar */}\n        <div\n          className={cn(\n            'flex items-center gap-2 border-b px-4 py-2.5',\n            theme === 'dark' ? 'border-zinc-800 bg-zinc-900' : 'border-zinc-200 bg-zinc-100',\n          )}\n        >\n          <div className=\"flex gap-1.5\">\n            <span className=\"size-3 rounded-full bg-red-500\" />\n            <span className=\"size-3 rounded-full bg-yellow-500\" />\n            <span className=\"size-3 rounded-full bg-green-500\" />\n          </div>\n          <span className={cn('ml-2 text-xs', theme === 'dark' ? 'text-zinc-400' : 'text-zinc-500')}>{title}</span>\n        </div>\n\n        {/* Body */}\n        <div ref={bodyRef} className=\"overflow-auto p-4 leading-relaxed\" style={{ maxHeight }}>\n          {shownLines.map((line, i) => (\n            <div key={i} data-slot=\"terminal-line\" className=\"break-words whitespace-pre-wrap\">\n              {line.type === 'command' && (\n                <div data-slot=\"terminal-command\" className=\"flex flex-wrap items-baseline gap-x-1.5\">\n                  <span\n                    className={cn('shrink-0 font-semibold', theme === 'dark' ? 'text-green-400' : 'text-green-600')}\n                  >\n                    {line.prompt}\n                  </span>\n                  <span>{line.command}</span>\n                </div>\n              )}\n              {line.output && (\n                <div data-slot=\"terminal-output\" className=\"text-zinc-400\">\n                  {line.output}\n                </div>\n              )}\n            </div>\n          ))}\n        </div>\n      </div>\n    )\n  },\n)\nTerminal.displayName = 'Terminal'\n\nexport { Terminal }\n",
      "type": "registry:ui",
      "target": "~/components/ui/terminal/Terminal.tsx"
    },
    {
      "path": "packages/registry-react/components/terminal/index.ts",
      "content": "export { Terminal, type TerminalProps, type TerminalLine } from './Terminal'\n",
      "type": "registry:ui",
      "target": "~/components/ui/terminal/index.ts"
    }
  ],
  "dependencies": [],
  "devDependencies": [],
  "registryDependencies": [],
  "description": "Terminal/command-line display with a macOS-style title bar, command history, prompt character, dark/light themes, auto-scroll, optional typing animation, and a line slot for custom formatting.",
  "categories": [
    "display"
  ]
}