{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "web-preview",
  "registryDependencies": [
    "https://taki-ui.com/r/button.json",
    "https://taki-ui.com/r/disclosure.json",
    "https://taki-ui.com/r/input.json",
    "https://taki-ui.com/r/tooltip.json",
    "https://taki-ui.com/r/badge.json"
  ],
  "files": [
    {
      "path": "registry/new-york/ai-elements/web-preview.tsx",
      "content": "\"use client\"\n\nimport type { ComponentProps, ReactNode } from \"react\"\nimport { createContext, useContext, useEffect, useState } from \"react\"\nimport { ChevronDownIcon } from \"lucide-react\"\n\nimport { cn } from \"@/lib/utils\"\nimport { Button } from \"@/registry/new-york/ui/button\"\nimport {\n  Disclosure,\n  DisclosureHeader,\n  DisclosurePanel,\n} from \"@/registry/new-york/ui/disclosure\"\nimport { Input } from \"@/registry/new-york/ui/input\"\nimport { Tooltip, TooltipTrigger } from \"@/registry/new-york/ui/tooltip\"\n\nexport type WebPreviewContextValue = {\n  url: string\n  setUrl: (url: string) => void\n  consoleOpen: boolean\n  setConsoleOpen: (open: boolean) => void\n}\n\nconst WebPreviewContext = createContext<WebPreviewContextValue | null>(null)\n\nconst useWebPreview = () => {\n  const context = useContext(WebPreviewContext)\n  if (!context) {\n    throw new Error(\"WebPreview components must be used within a WebPreview\")\n  }\n  return context\n}\n\nexport type WebPreviewProps = ComponentProps<\"div\"> & {\n  defaultUrl?: string\n  onUrlChange?: (url: string) => void\n}\n\nexport const WebPreview = ({\n  className,\n  children,\n  defaultUrl = \"\",\n  onUrlChange,\n  ...props\n}: WebPreviewProps) => {\n  const [url, setUrl] = useState(defaultUrl)\n  const [consoleOpen, setConsoleOpen] = useState(false)\n\n  const handleUrlChange = (newUrl: string) => {\n    setUrl(newUrl)\n    onUrlChange?.(newUrl)\n  }\n\n  const contextValue: WebPreviewContextValue = {\n    url,\n    setUrl: handleUrlChange,\n    consoleOpen,\n    setConsoleOpen,\n  }\n\n  return (\n    <WebPreviewContext.Provider value={contextValue}>\n      <div\n        className={cn(\n          \"bg-card flex size-full flex-col rounded-lg border\",\n          className\n        )}\n        {...props}\n      >\n        {children}\n      </div>\n    </WebPreviewContext.Provider>\n  )\n}\n\nexport type WebPreviewNavigationProps = ComponentProps<\"div\">\n\nexport const WebPreviewNavigation = ({\n  className,\n  children,\n  ...props\n}: WebPreviewNavigationProps) => (\n  <div\n    className={cn(\"flex items-center gap-1 border-b p-2\", className)}\n    {...props}\n  >\n    {children}\n  </div>\n)\n\nexport type WebPreviewNavigationButtonProps = ComponentProps<typeof Button> & {\n  tooltip?: string\n}\n\nexport const WebPreviewNavigationButton = ({\n  onClick: onPress,\n  disabled,\n  tooltip,\n  children,\n  ...props\n}: WebPreviewNavigationButtonProps) => (\n  <TooltipTrigger>\n    <Button\n      className=\"hover:text-foreground h-8 w-8 p-0\"\n      disabled={disabled}\n      onPress={onPress}\n      size=\"sm\"\n      variant=\"ghost\"\n      {...props}\n    >\n      {children}\n    </Button>\n    <Tooltip>{tooltip}</Tooltip>\n  </TooltipTrigger>\n)\n\nexport type WebPreviewUrlProps = ComponentProps<typeof Input>\n\nexport const WebPreviewUrl = ({\n  value,\n  onChange,\n  onKeyDown,\n  ...props\n}: WebPreviewUrlProps) => {\n  const { url, setUrl } = useWebPreview()\n  const [inputValue, setInputValue] = useState(url)\n\n  // Sync input value with context URL when it changes externally\n  useEffect(() => {\n    setInputValue(url)\n  }, [url])\n\n  const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {\n    setInputValue(event.target.value)\n    onChange?.(event)\n  }\n\n  const handleKeyDown = (event: React.KeyboardEvent<HTMLInputElement>) => {\n    if (event.key === \"Enter\") {\n      const target = event.target as HTMLInputElement\n      setUrl(target.value)\n    }\n    onKeyDown?.(event)\n  }\n\n  return (\n    <Input\n      className=\"h-8 flex-1 text-sm\"\n      onChange={onChange ?? handleChange}\n      onKeyDown={handleKeyDown}\n      placeholder=\"Enter URL...\"\n      value={value ?? inputValue}\n      {...props}\n    />\n  )\n}\n\nexport type WebPreviewBodyProps = ComponentProps<\"iframe\"> & {\n  loading?: ReactNode\n}\n\nexport const WebPreviewBody = ({\n  className,\n  loading,\n  src,\n  ...props\n}: WebPreviewBodyProps) => {\n  const { url } = useWebPreview()\n\n  return (\n    <div className=\"flex-1\">\n      <iframe\n        className={cn(\"size-full\", className)}\n        sandbox=\"allow-scripts allow-same-origin allow-forms allow-popups allow-presentation\"\n        src={(src ?? url) || undefined}\n        title=\"Preview\"\n        {...props}\n      />\n      {loading}\n    </div>\n  )\n}\n\nexport type WebPreviewConsoleProps = ComponentProps<\"div\"> & {\n  logs?: Array<{\n    level: \"log\" | \"warn\" | \"error\"\n    message: string\n    timestamp: Date\n  }>\n}\n\nexport const WebPreviewConsole = ({\n  className,\n  logs = [],\n  children,\n  ...props\n}: WebPreviewConsoleProps) => {\n  const { consoleOpen, setConsoleOpen } = useWebPreview()\n\n  return (\n    <Disclosure\n      className={cn(\"bg-muted/50 border-t font-mono text-sm\", className)}\n      onOpenChange={setConsoleOpen}\n      open={consoleOpen}\n      {...props}\n    >\n      <DisclosureHeader asChild>\n        <Button\n          className=\"hover:bg-muted/50 flex w-full items-center justify-between p-4 text-left font-medium\"\n          variant=\"ghost\"\n        >\n          Console\n          <ChevronDownIcon\n            className={cn(\n              \"h-4 w-4 transition-transform duration-200\",\n              consoleOpen && \"rotate-180\"\n            )}\n          />\n        </Button>\n      </DisclosureHeader>\n      <DisclosurePanel\n        className={cn(\n          \"px-4 pb-4\",\n          \"data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-[state=closed]:animate-out data-[state=open]:animate-in outline-none\"\n        )}\n      >\n        <div className=\"max-h-48 space-y-1 overflow-y-auto\">\n          {logs.length === 0 ? (\n            <p className=\"text-muted-foreground\">No console output</p>\n          ) : (\n            logs.map((log, index) => (\n              <div\n                className={cn(\n                  \"text-xs\",\n                  log.level === \"error\" && \"text-destructive\",\n                  log.level === \"warn\" && \"text-yellow-600\",\n                  log.level === \"log\" && \"text-foreground\"\n                )}\n                key={`${log.timestamp.getTime()}-${index}`}\n              >\n                <span className=\"text-muted-foreground\">\n                  {log.timestamp.toLocaleTimeString()}\n                </span>{\" \"}\n                {log.message}\n              </div>\n            ))\n          )}\n          {children}\n        </div>\n      </DisclosurePanel>\n    </Disclosure>\n  )\n}\n",
      "type": "registry:ui"
    }
  ],
  "type": "registry:ui"
}