{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "ai-prompt-input",
  "title": "AI Prompt Input",
  "description": "Input component for AI prompts with context selection and options.",
  "registryDependencies": [
    "button",
    "card",
    "dropdown-menu",
    "popover",
    "select",
    "textarea",
    "toggle",
    "tooltip"
  ],
  "files": [
    {
      "path": "registry/new-york/blocks/ai/ai-prompt-input.tsx",
      "content": "\"use client\";\n\nimport {\n  Check,\n  Globe,\n  Lightbulb,\n  Mic,\n  MoreHorizontal,\n  Paperclip,\n  Plus,\n  Send,\n  X,\n} from \"lucide-react\";\nimport Image from \"next/image\";\nimport { useCallback, useState } from \"react\";\nimport { Button } from \"@/registry/new-york/ui/button\";\nimport { Card, CardContent } from \"@/registry/new-york/ui/card\";\nimport {\n  DropdownMenu,\n  DropdownMenuContent,\n  DropdownMenuItem,\n  DropdownMenuSeparator,\n  DropdownMenuTrigger,\n} from \"@/registry/new-york/ui/dropdown-menu\";\nimport {\n  Popover,\n  PopoverContent,\n  PopoverTrigger,\n} from \"@/registry/new-york/ui/popover\";\nimport {\n  Select,\n  SelectContent,\n  SelectItem,\n  SelectTrigger,\n  SelectValue,\n} from \"@/registry/new-york/ui/select\";\nimport { Textarea } from \"@/registry/new-york/ui/textarea\";\nimport { Toggle } from \"@/registry/new-york/ui/toggle\";\nimport {\n  Tooltip,\n  TooltipContent,\n  TooltipProvider,\n  TooltipTrigger,\n} from \"@/registry/new-york/ui/tooltip\";\n\nconst mockUsers = [\n  {\n    id: \"shadcn\",\n    name: \"shadcn\",\n    avatar: \"https://github.com/shadcn.png\",\n  },\n  {\n    id: \"preetsuthar17\",\n    name: \"preetsuthar17\",\n    avatar: \"https://github.com/preetsuthar17.png\",\n  },\n  {\n    id: \"evilrabbit\",\n    name: \"evilrabbit\",\n    avatar: \"https://github.com/evilrabbit.png\",\n  },\n];\n\nconst mockPages = [\n  { id: \"analytics-report\", name: \"Analytics Report\", icon: \"📊\" },\n  { id: \"technical-specs\", name: \"Technical Specs\", icon: \"📑\" },\n  { id: \"user-guide\", name: \"User Guide\", icon: \"💡\" },\n  { id: \"team-directory\", name: \"Team Directory\", icon: \"👥\" },\n  { id: \"attachments\", name: \"Attachments\", icon: \"📎\" },\n  { id: \"new-feature\", name: \"New Feature Plan\", icon: \"✨\" },\n];\n\nconst models = [\n  { value: \"claude-sonnet-4-preview\", label: \"Claude Sonnet 4 (Preview)\" },\n  { value: \"claude-sonnet-3.7\", label: \"Claude Sonnet 3.7\" },\n  { value: \"claude-sonnet-3.5\", label: \"Claude Sonnet 3.5\" },\n  { value: \"gemini-2.5-pro-preview\", label: \"Gemini 2.5 pro (Preview)\" },\n  { value: \"gpt-3.5-turbo\", label: \"GPT-3.5 Turbo\" },\n  { value: \"gpt-4\", label: \"GPT-4\" },\n  { value: \"gpt-4o\", label: \"GPT-4o\" },\n  { value: \"gpt-o4-mini\", label: \"o4-mini (Preview)\" },\n];\n\nconst agentModes = [\n  { value: \"auto\", label: \"Auto\" },\n  { value: \"agent\", label: \"Agent\" },\n  { value: \"plan\", label: \"Plan\" },\n];\n\nconst hideScrollbar = {\n  msOverflowStyle: \"none\",\n  scrollbarWidth: \"none\" as const,\n} as React.CSSProperties;\n\ninterface ContextBadgeProps {\n  label: string;\n  icon?: string;\n  avatar?: string;\n  onRemove: () => void;\n}\n\nfunction ContextBadge({ label, icon, avatar, onRemove }: ContextBadgeProps) {\n  return (\n    <span\n      className=\"inline-flex items-center gap-1 rounded-xl border bg-secondary p-0.5 font-medium text-muted-foreground text-xs\"\n      style={{ whiteSpace: \"nowrap\", flex: \"0 0 auto\" }}\n    >\n      {icon && (\n        <span className=\"relative inline-block size-4 overflow-hidden rounded-full bg-white p-0.5 align-middle\">\n          <span\n            style={{\n              display: \"flex\",\n              alignItems: \"center\",\n              justifyContent: \"center\",\n              width: \"100%\",\n              height: \"100%\",\n              fontSize: \"12px\",\n              lineHeight: 1,\n              objectFit: \"cover\",\n              borderRadius: \"9999px\",\n            }}\n          >\n            {icon}\n          </span>\n        </span>\n      )}\n      {avatar && (\n        <span className=\"relative inline-block h-4 w-4\">\n          <Image\n            alt={label}\n            className=\"rounded-full\"\n            fill\n            priority\n            sizes=\"16px\"\n            src={avatar}\n            style={{ objectFit: \"cover\" }}\n          />\n        </span>\n      )}\n      {label}\n      <button\n        aria-label={`Remove ${label}`}\n        className=\"rounded-full p-0.5 hover:bg-muted-foreground/10 focus:outline-none focus:ring-1 focus:ring-muted-foreground\"\n        onClick={onRemove}\n        tabIndex={0}\n        type=\"button\"\n      >\n        <X className=\"h-3 w-3 text-muted-foreground/60\" />\n      </button>\n    </span>\n  );\n}\n\ninterface ContextPopoverProps {\n  open: boolean;\n  onOpenChange: (open: boolean) => void;\n  selectedUsers: string[];\n  selectedPages: string[];\n  onUserToggle: (userId: string) => void;\n  onPageToggle: (pageId: string) => void;\n}\n\nfunction ContextPopover({\n  open,\n  onOpenChange,\n  selectedUsers,\n  selectedPages,\n  onUserToggle,\n  onPageToggle,\n}: ContextPopoverProps) {\n  return (\n    <Popover onOpenChange={onOpenChange} open={open}>\n      <PopoverTrigger asChild>\n        <button\n          aria-label=\"Add context\"\n          className=\"inline-flex items-center gap-1 rounded-xl border bg-secondary px-2 py-0.5 font-medium text-muted-foreground text-xs transition\"\n          style={{\n            whiteSpace: \"nowrap\",\n            flex: \"0 0 auto\",\n            height: \"auto\",\n            appearance: \"none\",\n            background: \"none\",\n          }}\n          type=\"button\"\n        >\n          <Plus className=\"h-3 w-3 text-muted-foreground\" />\n          <span>Add context</span>\n        </button>\n      </PopoverTrigger>\n      <PopoverContent align=\"start\" className=\"z-30 w-max p-2\">\n        <div className=\"flex flex-col gap-3\">\n          <div className=\"flex flex-col gap-1\">\n            <div className=\"px-1 font-semibold text-muted-foreground text-xs uppercase tracking-wide\">\n              Pages\n            </div>\n            <div className=\"flex flex-col gap-1\">\n              {mockPages.map((page) => {\n                const isSelected = selectedPages.includes(page.id);\n                return (\n                  <button\n                    aria-pressed={isSelected}\n                    className={`inline-flex items-center justify-start gap-1 rounded-xl border px-2 py-0.5 font-medium text-xs transition ${\n                      isSelected\n                        ? \"bg-secondary text-muted-foreground\"\n                        : \"border-transparent bg-background text-foreground\"\n                    }`}\n                    key={page.id}\n                    onClick={() => onPageToggle(page.id)}\n                    style={{ fontWeight: 400, whiteSpace: \"nowrap\" }}\n                    type=\"button\"\n                  >\n                    {page.icon} {page.name}\n                  </button>\n                );\n              })}\n            </div>\n          </div>\n          <div className=\"flex flex-col gap-1\">\n            <div className=\"px-1 font-semibold text-muted-foreground text-xs uppercase tracking-wide\">\n              Users\n            </div>\n            <div className=\"flex flex-col gap-1\">\n              {mockUsers.map((user) => {\n                const isSelected = selectedUsers.includes(user.id);\n                return (\n                  <button\n                    aria-pressed={isSelected}\n                    className={`inline-flex items-center justify-start gap-1 rounded-xl border p-0.5 font-medium text-xs transition ${\n                      isSelected\n                        ? \"bg-secondary text-muted-foreground\"\n                        : \"border-transparent bg-background text-foreground hover:bg-accent hover:text-accent-foreground\"\n                    }`}\n                    key={user.id}\n                    onClick={() => onUserToggle(user.id)}\n                    style={{ fontWeight: 400, whiteSpace: \"nowrap\" }}\n                    type=\"button\"\n                  >\n                    <Image\n                      alt={user.name}\n                      className=\"h-4 w-4 rounded-full\"\n                      height={16}\n                      src={user.avatar}\n                      style={{ display: \"inline-block\" }}\n                      width={16}\n                    />\n                    {user.name}\n                  </button>\n                );\n              })}\n            </div>\n          </div>\n        </div>\n      </PopoverContent>\n    </Popover>\n  );\n}\n\ninterface ContextSelectorProps {\n  selectedUsers: string[];\n  selectedPages: string[];\n  onUserToggle: (userId: string) => void;\n  onPageToggle: (pageId: string) => void;\n  onRemoveUser: (userId: string) => void;\n  onRemovePage: (pageId: string) => void;\n}\n\nfunction ContextSelector({\n  selectedUsers,\n  selectedPages,\n  onUserToggle,\n  onPageToggle,\n  onRemoveUser,\n  onRemovePage,\n}: ContextSelectorProps) {\n  const [popoverOpen, setPopoverOpen] = useState(false);\n  const hasSelectedContext =\n    selectedPages.length > 0 || selectedUsers.length > 0;\n\n  return (\n    <div className=\"flex w-full flex-col gap-2\">\n      <div className=\"flex w-full items-center gap-1\">\n        <ContextPopover\n          onOpenChange={setPopoverOpen}\n          onPageToggle={onPageToggle}\n          onUserToggle={onUserToggle}\n          open={popoverOpen}\n          selectedPages={selectedPages}\n          selectedUsers={selectedUsers}\n        />\n        <div\n          aria-label=\"Selected context\"\n          className=\"scroll-hide hidden flex-1 gap-1 overflow-x-auto sm:flex\"\n          style={{\n            ...hideScrollbar,\n            WebkitOverflowScrolling: \"touch\",\n            minWidth: 0,\n          }}\n          tabIndex={0}\n        >\n          <style>\n            {`.scroll-hide::-webkit-scrollbar {\n              display: none !important;\n              height: 0 !important;\n              width: 0 !important;\n              background: transparent !important;\n            }`}\n          </style>\n          {selectedPages.map((id) => {\n            const page = mockPages.find((p) => p.id === id);\n            if (!page) return null;\n            return (\n              <ContextBadge\n                icon={page.icon}\n                key={`page-${id}`}\n                label={page.name}\n                onRemove={() => onRemovePage(id)}\n              />\n            );\n          })}\n          {selectedUsers.map((id) => {\n            const user = mockUsers.find((u) => u.id === id);\n            if (!user) return null;\n            return (\n              <ContextBadge\n                avatar={user.avatar}\n                key={`user-${id}`}\n                label={user.name}\n                onRemove={() => onRemoveUser(id)}\n              />\n            );\n          })}\n        </div>\n      </div>\n      {hasSelectedContext && (\n        <div\n          aria-label=\"Selected context (mobile)\"\n          className=\"scroll-hide flex w-[18rem] gap-1 overflow-x-auto sm:hidden sm:w-full\"\n          style={{\n            ...hideScrollbar,\n            WebkitOverflowScrolling: \"touch\",\n            minWidth: 0,\n          }}\n          tabIndex={0}\n        >\n          {selectedPages.map((id) => {\n            const page = mockPages.find((p) => p.id === id);\n            if (!page) return null;\n            return (\n              <ContextBadge\n                icon={page.icon}\n                key={`page-${id}`}\n                label={page.name}\n                onRemove={() => onRemovePage(id)}\n              />\n            );\n          })}\n          {selectedUsers.map((id) => {\n            const user = mockUsers.find((u) => u.id === id);\n            if (!user) return null;\n            return (\n              <ContextBadge\n                avatar={user.avatar}\n                key={`user-${id}`}\n                label={user.name}\n                onRemove={() => onRemoveUser(id)}\n              />\n            );\n          })}\n        </div>\n      )}\n    </div>\n  );\n}\n\ninterface ModelSelectProps {\n  value: string;\n  onValueChange: (value: string) => void;\n}\n\nfunction ModelSelect({ value, onValueChange }: ModelSelectProps) {\n  return (\n    <Select onValueChange={onValueChange} value={value}>\n      <SelectTrigger className=\"font-medium\" size=\"default\">\n        <SelectValue placeholder=\"Select Model\" />\n      </SelectTrigger>\n      <SelectContent className=\"text-sm\">\n        {models.map((model) => (\n          <SelectItem key={model.value} value={model.value}>\n            {model.label}\n          </SelectItem>\n        ))}\n      </SelectContent>\n    </Select>\n  );\n}\n\ninterface AgentModeSelectProps {\n  value: string;\n  onValueChange: (value: string) => void;\n}\n\nfunction AgentModeSelect({ value, onValueChange }: AgentModeSelectProps) {\n  return (\n    <Select onValueChange={onValueChange} value={value}>\n      <SelectTrigger aria-label=\"Agent mode\" className=\"font-medium\">\n        <SelectValue placeholder=\"Agent\" />\n      </SelectTrigger>\n      <SelectContent className=\"text-sm\">\n        {agentModes.map((mode) => (\n          <SelectItem key={mode.value} value={mode.value}>\n            {mode.label}\n          </SelectItem>\n        ))}\n      </SelectContent>\n    </Select>\n  );\n}\n\ninterface LeftActionButtonsProps {\n  webSearchEnabled: boolean;\n  thinkingEnabled: boolean;\n  onWebSearchToggle: (enabled: boolean) => void;\n  onThinkingToggle: (enabled: boolean) => void;\n}\n\nfunction LeftActionButtons({\n  webSearchEnabled,\n  thinkingEnabled,\n  onWebSearchToggle,\n  onThinkingToggle,\n}: LeftActionButtonsProps) {\n  return (\n    <>\n      <div className=\"hidden items-center justify-center gap-1 sm:flex\">\n        <Tooltip>\n          <TooltipTrigger asChild>\n            <Button\n              aria-label=\"Attach file\"\n              size=\"icon\"\n              type=\"button\"\n              variant=\"ghost\"\n            >\n              <Paperclip />\n            </Button>\n          </TooltipTrigger>\n          <TooltipContent side=\"top\">Attach file</TooltipContent>\n        </Tooltip>\n\n        <Tooltip>\n          <TooltipTrigger asChild>\n            <Toggle\n              aria-label=\"Enable Thinking\"\n              onPressedChange={onThinkingToggle}\n              pressed={thinkingEnabled}\n              size=\"default\"\n              variant={thinkingEnabled ? \"outline\" : \"default\"}\n            >\n              <Lightbulb />\n            </Toggle>\n          </TooltipTrigger>\n          <TooltipContent side=\"top\">\n            {thinkingEnabled ? \"Disable Thinking\" : \"Enable Thinking\"}\n          </TooltipContent>\n        </Tooltip>\n\n        <Tooltip>\n          <TooltipTrigger asChild>\n            <Toggle\n              aria-label=\"Enable web search\"\n              className=\"relative flex w-9 min-w-9 items-center justify-center overflow-hidden p-0\"\n              onPressedChange={onWebSearchToggle}\n              pressed={webSearchEnabled}\n              size=\"default\"\n              variant={webSearchEnabled ? \"outline\" : \"default\"}\n            >\n              <span className=\"flex h-full w-full items-center justify-center opacity-100 transition-opacity duration-200\">\n                <Globe />\n              </span>\n              <span\n                className={`absolute left-8 whitespace-nowrap font-medium text-sm transition-all duration-200 ${\n                  webSearchEnabled\n                    ? \"visible left-9 ml-2 opacity-100\"\n                    : \"invisible left-4 opacity-0\"\n                }`}\n                style={{\n                  width: webSearchEnabled ? \"auto\" : 0,\n                  pointerEvents: webSearchEnabled ? \"auto\" : \"none\",\n                }}\n              >\n                Search\n              </span>\n            </Toggle>\n          </TooltipTrigger>\n          <TooltipContent side=\"top\">\n            {webSearchEnabled ? \"Disable web search\" : \"Enable web search\"}\n          </TooltipContent>\n        </Tooltip>\n      </div>\n\n      <DropdownMenu>\n        <DropdownMenuTrigger asChild>\n          <Button\n            aria-label=\"More options\"\n            className=\"sm:hidden\"\n            size=\"icon\"\n            type=\"button\"\n            variant=\"ghost\"\n          >\n            <MoreHorizontal />\n          </Button>\n        </DropdownMenuTrigger>\n        <DropdownMenuContent align=\"start\">\n          <DropdownMenuItem onClick={() => {}}>\n            <Paperclip className=\"size-4\" />\n            <span>Attach file</span>\n          </DropdownMenuItem>\n          <DropdownMenuSeparator />\n          <DropdownMenuItem onClick={() => onThinkingToggle(!thinkingEnabled)}>\n            <Lightbulb className=\"size-4\" />\n            <span>Thinking</span>\n            {thinkingEnabled && <Check className=\"ml-auto size-4\" />}\n          </DropdownMenuItem>\n          <DropdownMenuItem\n            onClick={() => onWebSearchToggle(!webSearchEnabled)}\n          >\n            <Globe className=\"size-4\" />\n            <span>Web search</span>\n            {webSearchEnabled && <Check className=\"ml-auto size-4\" />}\n          </DropdownMenuItem>\n        </DropdownMenuContent>\n      </DropdownMenu>\n    </>\n  );\n}\n\ninterface RightActionButtonsProps {\n  input: string;\n}\n\nfunction RightActionButtons({ input }: RightActionButtonsProps) {\n  const hasInput = input.trim().length > 0;\n\n  return (\n    <Tooltip>\n      <TooltipTrigger asChild>\n        <Button\n          aria-label={hasInput ? \"Send message\" : \"Start voice input\"}\n          size=\"icon\"\n          type={hasInput ? \"submit\" : \"button\"}\n        >\n          {hasInput ? <Send /> : <Mic />}\n        </Button>\n      </TooltipTrigger>\n      <TooltipContent side=\"top\">\n        {hasInput ? \"Send message\" : \"Start voice input\"}\n      </TooltipContent>\n    </Tooltip>\n  );\n}\n\nexport default function AIPromptInput() {\n  const [input, setInput] = useState(\"\");\n  const [model, setModel] = useState(\"gpt-3.5-turbo\");\n  const [agentMode, setAgentMode] = useState(\"auto\");\n  const [contextOptions, setContextOptions] = useState<string[]>([]);\n  const [webSearchEnabled, setWebSearchEnabled] = useState(false);\n  const [thinkingEnabled, setThinkingEnabled] = useState(false);\n  const [selectedUsers, setSelectedUsers] = useState<string[]>([]);\n  const [selectedPages, setSelectedPages] = useState<string[]>([]);\n\n  const handleUserToggle = (userId: string) => {\n    setSelectedUsers((prev) =>\n      prev.includes(userId)\n        ? prev.filter((id) => id !== userId)\n        : [...prev, userId]\n    );\n    if (!contextOptions.includes(\"users\")) {\n      setContextOptions((prev) => [...prev, \"users\"]);\n    }\n  };\n\n  const handlePageToggle = (pageId: string) => {\n    setSelectedPages((prev) =>\n      prev.includes(pageId)\n        ? prev.filter((id) => id !== pageId)\n        : [...prev, pageId]\n    );\n    if (!contextOptions.includes(\"pages\")) {\n      setContextOptions((prev) => [...prev, \"pages\"]);\n    }\n  };\n\n  const handleRemoveUser = (userId: string) => {\n    setSelectedUsers((prev) => prev.filter((id) => id !== userId));\n  };\n\n  const handleRemovePage = (pageId: string) => {\n    setSelectedPages((prev) => prev.filter((id) => id !== pageId));\n  };\n\n  const handleSubmit = useCallback(\n    (e?: React.FormEvent) => {\n      e?.preventDefault();\n      if (!input.trim()) return;\n      console.log(\"Submitted:\", {\n        input,\n        model,\n        agentMode,\n        contextOptions,\n        webSearchEnabled,\n        thinkingEnabled,\n        selectedUsers,\n        selectedPages,\n      });\n      setInput(\"\");\n    },\n    [\n      input,\n      model,\n      agentMode,\n      contextOptions,\n      webSearchEnabled,\n      thinkingEnabled,\n      selectedUsers,\n      selectedPages,\n    ]\n  );\n\n  return (\n    <TooltipProvider>\n      <div className=\"flex w-full flex-col items-center gap-2\">\n        <Card className=\"w-full p-0 shadow-xs\">\n          <CardContent className=\"p-0\">\n            <form\n              aria-label=\"AI chat input\"\n              className=\"flex flex-col gap-3\"\n              onSubmit={handleSubmit}\n            >\n              <div className=\"flex w-full items-start justify-between p-4 pb-0\">\n                <ContextSelector\n                  onPageToggle={handlePageToggle}\n                  onRemovePage={handleRemovePage}\n                  onRemoveUser={handleRemoveUser}\n                  onUserToggle={handleUserToggle}\n                  selectedPages={selectedPages}\n                  selectedUsers={selectedUsers}\n                />\n              </div>\n\n              <Textarea\n                className=\"resize-none border-none p-4 shadow-none focus-visible:ring-0 focus-visible:ring-offset-0\"\n                onChange={(e) => setInput(e.target.value)}\n                onKeyDown={(e) => {\n                  if ((e.metaKey || e.ctrlKey) && e.key === \"Enter\") {\n                    handleSubmit();\n                  }\n                }}\n                placeholder=\"Type your message…\"\n                rows={4}\n                value={input}\n              />\n\n              <div className=\"flex w-full items-center justify-between gap-2 p-4\">\n                <div className=\"flex items-center justify-center gap-1\">\n                  <LeftActionButtons\n                    onThinkingToggle={setThinkingEnabled}\n                    onWebSearchToggle={setWebSearchEnabled}\n                    thinkingEnabled={thinkingEnabled}\n                    webSearchEnabled={webSearchEnabled}\n                  />\n                  <div className=\"hidden sm:block\">\n                    <AgentModeSelect\n                      onValueChange={setAgentMode}\n                      value={agentMode}\n                    />\n                  </div>\n                </div>\n                <RightActionButtons input={input} />\n              </div>\n            </form>\n          </CardContent>\n        </Card>\n\n        <div className=\"flex w-full items-center justify-between gap-2\">\n          <ModelSelect onValueChange={setModel} value={model} />\n          <div className=\"sm:hidden\">\n            <AgentModeSelect onValueChange={setAgentMode} value={agentMode} />\n          </div>\n        </div>\n      </div>\n    </TooltipProvider>\n  );\n}\n",
      "type": "registry:ui"
    }
  ],
  "categories": [
    "block",
    "ai"
  ],
  "type": "registry:ui"
}