{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "terminal",
  "type": "registry:ui",
  "dependencies": [
    "clsx",
    "tailwind-merge"
  ],
  "registryDependencies": [
    "https://usva.build/r/code-snippet.json"
  ],
  "files": [
    {
      "path": "terminal.tsx",
      "target": "components/ui/terminal.tsx",
      "type": "registry:ui",
      "content": "\"use client\";\nimport * as React from \"react\";\nimport { cn } from \"@/lib/utils\";\nimport { CopySnippetButton } from \"./code-snippet\";\n\nexport interface TerminalProps\n  extends Omit<React.HTMLAttributes<HTMLDivElement>, \"children\"> {\n  /** The command, without the prompt. Also what the copy button copies. */\n  command: string;\n  prompt?: string;\n  copyable?: boolean;\n  /** Runs after the clipboard write succeeds, never on a denied one. */\n  onCopied?: (value: string) => void;\n}\n\n/* Scoped packages and URLs are what the eye scans a command for. Unscoped\n   package names have no such marker, so an install verb makes the words after\n   it hot too, which is the only way `bun add react` reads like `bun add @scope/x`. */\nconst INSTALL_VERBS = new Set([\"add\", \"install\", \"i\"]);\nconst isHot = (word: string) => word.startsWith(\"@\") || /^https?:/.test(word);\n\nexport const Terminal = React.forwardRef<HTMLDivElement, TerminalProps>(\n  (\n    { command, prompt = \"$\", copyable = true, onCopied, className, ...rest },\n    ref,\n  ) => {\n    const words: { text: string; hot: boolean; at: number }[] = [];\n    let at = 0;\n    let installing = false;\n    for (const text of command.split(\" \")) {\n      words.push({\n        text,\n        hot: isHot(text) || (installing && !text.startsWith(\"-\")),\n        at,\n      });\n      if (INSTALL_VERBS.has(text)) installing = true;\n      at += text.length + 1;\n    }\n\n    return (\n      <div\n        ref={ref}\n        className={cn(\n          \"flex items-center justify-between gap-2 rounded-lg border border-border bg-sunken px-3 py-2\",\n          className,\n        )}\n        {...rest}\n      >\n        <code\n          // A scrolling region has to be reachable by keyboard (WCAG 2.1.1), which\n          // is the documented exception to this rule.\n          // biome-ignore lint/a11y/noNoninteractiveTabindex: see above\n          tabIndex={0}\n          className=\"overflow-x-auto whitespace-nowrap font-mono text-[0.74rem] text-on-sunken outline-none focus-visible:outline-2 focus-visible:-outline-offset-2 focus-visible:outline-accent [scrollbar-width:thin] [&::-webkit-scrollbar]:h-1 [&::-webkit-scrollbar-thumb]:rounded-full [&::-webkit-scrollbar-thumb]:bg-border-strong/60 [&::-webkit-scrollbar-track]:bg-transparent\"\n        >\n          <span aria-hidden=\"true\" className=\"text-faint\">\n            {prompt}{\" \"}\n          </span>\n          {words.map((word) => (\n            <React.Fragment key={word.at}>\n              {word.at > 0 ? \" \" : \"\"}\n              {word.hot ? (\n                <span className=\"text-accent-alt\">{word.text}</span>\n              ) : (\n                word.text\n              )}\n            </React.Fragment>\n          ))}\n        </code>\n        {copyable && (\n          <CopySnippetButton\n            value={command}\n            label=\"copy the command\"\n            onCopied={onCopied}\n          />\n        )}\n      </div>\n    );\n  },\n);\nTerminal.displayName = \"Terminal\";\n"
    }
  ]
}