{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "receive-address",
  "title": "Receive Address",
  "author": "Satchmo <https://bigblocks.dev>",
  "description": "QR code and deposit address display with clipboard copy and optional address rotation. Supports default, compact, and inline variants.",
  "dependencies": [
    "lucide-react"
  ],
  "registryDependencies": [
    "button",
    "card",
    "badge",
    "skeleton",
    "tooltip"
  ],
  "files": [
    {
      "path": "registry/new-york/blocks/receive-address/index.tsx",
      "content": "\"use client\"\n\nimport { ReceiveAddressUI } from \"./receive-address-ui\"\nimport { useReceiveAddress } from \"./use-receive-address\"\nimport type { ReactNode } from \"react\"\nimport type { ReceiveAddressVariant } from \"./receive-address-ui\"\n\n// ---------------------------------------------------------------------------\n// Re-exports\n// ---------------------------------------------------------------------------\n\nexport {\n  ReceiveAddressUI,\n  type ReceiveAddressUIProps,\n  type ReceiveAddressVariant,\n} from \"./receive-address-ui\"\nexport {\n  useReceiveAddress,\n  type UseReceiveAddressOptions,\n  type UseReceiveAddressReturn,\n} from \"./use-receive-address\"\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nexport interface ReceiveAddressProps {\n  /** The deposit address to display */\n  address: string | null\n  /** Callback to rotate to a new address */\n  onRotate?: () => Promise<string>\n  /** Callback fired after address is copied */\n  onCopy?: (address: string) => void\n  /** Visual layout variant */\n  variant?: ReceiveAddressVariant\n  /** QR code pixel dimensions (default 200) */\n  qrSize?: number\n  /** Custom QR code renderer — receives the address and returns a ReactNode */\n  renderQr?: (address: string) => ReactNode\n  /** Additional CSS classes */\n  className?: string\n}\n\n// ---------------------------------------------------------------------------\n// Composed component\n// ---------------------------------------------------------------------------\n\n/**\n * Receive address block displaying a QR code and deposit address with copy\n * and optional address rotation.\n *\n * Composes the `useReceiveAddress` hook with the `ReceiveAddressUI`\n * presentation component.\n *\n * @example\n * ```tsx\n * import { ReceiveAddress } from \"@/components/blocks/receive-address\"\n *\n * function App() {\n *   return (\n *     <ReceiveAddress\n *       address=\"1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa\"\n *       onRotate={async () => {\n *         const newAddr = await generateNewAddress()\n *         return newAddr\n *       }}\n *       onCopy={(addr) => console.log(\"Copied:\", addr)}\n *     />\n *   )\n * }\n * ```\n */\nexport function ReceiveAddress({\n  address,\n  onRotate,\n  onCopy,\n  variant,\n  qrSize,\n  renderQr,\n  className,\n}: ReceiveAddressProps) {\n  const {\n    copied,\n    isRotating,\n    rotateError,\n    copyAddress,\n    rotateAddress,\n  } = useReceiveAddress({ address, onRotate, onCopy })\n\n  return (\n    <ReceiveAddressUI\n      address={address}\n      variant={variant}\n      qrSize={qrSize}\n      copied={copied}\n      isRotating={isRotating}\n      rotateError={rotateError}\n      canRotate={!!onRotate}\n      renderQr={renderQr}\n      onCopy={copyAddress}\n      onRotate={rotateAddress}\n      className={className}\n    />\n  )\n}\n",
      "type": "registry:block",
      "target": "~/components/blocks/receive-address/index.tsx"
    },
    {
      "path": "registry/new-york/blocks/receive-address/receive-address-ui.tsx",
      "content": "\"use client\"\n\nimport { type ReactNode, useMemo } from \"react\"\nimport { cn } from \"@/lib/utils\"\nimport {\n  Card,\n  CardContent,\n  CardFooter,\n  CardHeader,\n  CardTitle,\n} from \"@/components/ui/card\"\nimport { Button } from \"@/components/ui/button\"\nimport { Badge } from \"@/components/ui/badge\"\nimport { Skeleton } from \"@/components/ui/skeleton\"\nimport {\n  Tooltip,\n  TooltipContent,\n  TooltipProvider,\n  TooltipTrigger,\n} from \"@/components/ui/tooltip\"\nimport {\n  Copy,\n  Check,\n  RefreshCw,\n  AlertCircle,\n  QrCode,\n} from \"lucide-react\"\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\n/** Visual variant for the receive address block */\nexport type ReceiveAddressVariant = \"default\" | \"compact\" | \"inline\"\n\nexport interface ReceiveAddressUIProps {\n  /** The deposit address to display */\n  address: string | null\n  /** Visual layout variant */\n  variant?: ReceiveAddressVariant\n  /** QR code pixel dimensions (default 200) */\n  qrSize?: number\n  /** Whether the address was just copied */\n  copied: boolean\n  /** Whether address rotation is in progress */\n  isRotating: boolean\n  /** Error from address rotation */\n  rotateError: Error | null\n  /** Whether a rotate callback is available */\n  canRotate: boolean\n  /** Custom QR code renderer — receives the address and returns a ReactNode */\n  renderQr?: (address: string) => ReactNode\n  /** Copy the current address */\n  onCopy: () => void\n  /** Rotate to a new address */\n  onRotate: () => void\n  /** Additional CSS classes */\n  className?: string\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\n/** Truncate a string for display: \"abc123...xyz9\" */\nfunction truncate(value: string, startLen = 8, endLen = 6): string {\n  if (value.length <= startLen + endLen + 3) return value\n  return `${value.slice(0, startLen)}...${value.slice(-endLen)}`\n}\n\n/**\n * Generate a deterministic SVG pattern from an address hash.\n * This is a placeholder visualization — consumers should provide a real QR\n * renderer via the `renderQr` prop for production use.\n */\nfunction AddressPattern({\n  address,\n  size,\n}: {\n  address: string\n  size: number\n}) {\n  const gridSize = 11\n  const cellSize = size / (gridSize + 2) // +2 for quiet zone\n\n  const cells = useMemo(() => {\n    // Simple deterministic hash to generate a grid pattern\n    const result: boolean[] = []\n    for (let i = 0; i < gridSize * gridSize; i++) {\n      const charCode = address.charCodeAt(i % address.length)\n      const nextCharCode = address.charCodeAt((i + 1) % address.length)\n      result.push((charCode * nextCharCode + i) % 3 !== 0)\n    }\n\n    // Mirror horizontally for QR-like symmetry\n    for (let row = 0; row < gridSize; row++) {\n      for (let col = 0; col < Math.floor(gridSize / 2); col++) {\n        const mirrorCol = gridSize - 1 - col\n        result[row * gridSize + mirrorCol] = result[row * gridSize + col]\n      }\n    }\n\n    // Add finder patterns (top-left, top-right, bottom-left corners)\n    const setFinder = (startRow: number, startCol: number) => {\n      for (let r = 0; r < 3; r++) {\n        for (let c = 0; c < 3; c++) {\n          const idx = (startRow + r) * gridSize + (startCol + c)\n          result[idx] = r === 0 || r === 2 || c === 0 || c === 2\n        }\n      }\n      // Center cell\n      result[(startRow + 1) * gridSize + (startCol + 1)] = true\n    }\n    setFinder(0, 0)\n    setFinder(0, gridSize - 3)\n    setFinder(gridSize - 3, 0)\n\n    return result\n  }, [address])\n\n  return (\n    <svg\n      width={size}\n      height={size}\n      viewBox={`0 0 ${size} ${size}`}\n      role=\"img\"\n      aria-label={`QR placeholder for ${truncate(address)}`}\n      className=\"rounded-md\"\n    >\n      <rect width={size} height={size} fill=\"hsl(var(--background))\" rx={4} />\n      {cells.map((filled, i) => {\n        if (!filled) return null\n        const row = Math.floor(i / gridSize)\n        const col = i % gridSize\n        return (\n          <rect\n            key={i}\n            x={cellSize + col * cellSize}\n            y={cellSize + row * cellSize}\n            width={cellSize}\n            height={cellSize}\n            fill=\"hsl(var(--foreground))\"\n          />\n        )\n      })}\n    </svg>\n  )\n}\n\n// ---------------------------------------------------------------------------\n// Skeleton loaders\n// ---------------------------------------------------------------------------\n\nfunction QrSkeleton({ size }: { size: number }) {\n  return <Skeleton className=\"rounded-md\" style={{ width: size, height: size }} />\n}\n\nfunction AddressSkeleton() {\n  return (\n    <div className=\"flex items-center gap-2\">\n      <Skeleton className=\"h-5 flex-1\" />\n      <Skeleton className=\"size-8 rounded-md\" />\n    </div>\n  )\n}\n\n// ---------------------------------------------------------------------------\n// Variant: Default\n// ---------------------------------------------------------------------------\n\nfunction DefaultVariant({\n  address,\n  qrSize,\n  copied,\n  isRotating,\n  rotateError,\n  canRotate,\n  renderQr,\n  onCopy,\n  onRotate,\n  className,\n}: ReceiveAddressUIProps) {\n  if (!address) {\n    return (\n      <Card className={cn(\"w-full\", className)}>\n        <CardContent className=\"flex flex-col items-center gap-3 py-10\">\n          <div className=\"flex size-12 items-center justify-center rounded-full bg-muted\">\n            <QrCode\n              className=\"size-6 text-muted-foreground\"\n              aria-hidden=\"true\"\n            />\n          </div>\n          <div className=\"text-center\">\n            <p className=\"text-sm font-medium text-foreground\">\n              No address available\n            </p>\n            <p className=\"text-sm text-muted-foreground\">\n              Connect a wallet to generate a receive address\n            </p>\n          </div>\n        </CardContent>\n      </Card>\n    )\n  }\n\n  const effectiveSize = qrSize ?? 200\n\n  return (\n    <Card className={cn(\"w-full\", className)}>\n      <CardHeader className=\"pb-2\">\n        <CardTitle className=\"text-base font-semibold\">\n          Receive\n        </CardTitle>\n      </CardHeader>\n\n      <CardContent className=\"flex flex-col items-center gap-4\">\n        {/* QR code area */}\n        <div className=\"flex items-center justify-center rounded-lg border border-border bg-background p-3\">\n          {renderQr ? (\n            renderQr(address)\n          ) : (\n            <AddressPattern address={address} size={effectiveSize} />\n          )}\n        </div>\n\n        {/* Address with copy */}\n        <div className=\"flex w-full items-center gap-2\">\n          <Badge\n            variant=\"secondary\"\n            className=\"min-w-0 flex-1 justify-center truncate font-mono text-xs\"\n          >\n            {truncate(address, 12, 8)}\n          </Badge>\n          <TooltipProvider delayDuration={200}>\n            <Tooltip>\n              <TooltipTrigger asChild>\n                <Button\n                  variant=\"outline\"\n                  size=\"icon\"\n                  className=\"size-8 flex-shrink-0\"\n                  onClick={onCopy}\n                  aria-label={copied ? \"Copied\" : \"Copy address\"}\n                >\n                  {copied ? (\n                    <Check\n                      className=\"text-primary\"\n                      data-icon\n                      aria-hidden=\"true\"\n                    />\n                  ) : (\n                    <Copy\n                      data-icon\n                      aria-hidden=\"true\"\n                    />\n                  )}\n                </Button>\n              </TooltipTrigger>\n              <TooltipContent>\n                <p>{copied ? \"Copied!\" : \"Copy address\"}</p>\n              </TooltipContent>\n            </Tooltip>\n          </TooltipProvider>\n        </div>\n\n        {/* Rotation error */}\n        {rotateError && (\n          <div className=\"flex w-full items-center gap-2 rounded-md border border-destructive/30 bg-destructive/5 px-3 py-2\">\n            <AlertCircle\n              className=\"size-4 flex-shrink-0 text-destructive\"\n              aria-hidden=\"true\"\n            />\n            <p className=\"text-sm text-destructive\">{rotateError.message}</p>\n          </div>\n        )}\n      </CardContent>\n\n      {canRotate && (\n        <CardFooter>\n          <Button\n            variant=\"outline\"\n            size=\"sm\"\n            className=\"w-full gap-1.5\"\n            onClick={onRotate}\n            disabled={isRotating}\n            aria-label=\"Generate new address\"\n          >\n            <RefreshCw\n              className={cn(isRotating && \"animate-spin\")}\n              data-icon\n              aria-hidden=\"true\"\n            />\n            {isRotating ? \"Generating...\" : \"New Address\"}\n          </Button>\n        </CardFooter>\n      )}\n    </Card>\n  )\n}\n\n// ---------------------------------------------------------------------------\n// Variant: Compact\n// ---------------------------------------------------------------------------\n\nfunction CompactVariant({\n  address,\n  qrSize,\n  copied,\n  renderQr,\n  onCopy,\n  className,\n}: ReceiveAddressUIProps) {\n  const effectiveSize = qrSize ?? 120\n\n  if (!address) {\n    return <QrSkeleton size={effectiveSize} />\n  }\n\n  return (\n    <div className={cn(\"flex flex-col items-center gap-2\", className)}>\n      <TooltipProvider delayDuration={200}>\n        <Tooltip>\n          <TooltipTrigger asChild>\n            <button\n              type=\"button\"\n              onClick={onCopy}\n              className=\"cursor-pointer rounded-lg border border-border bg-background p-2 transition-colors hover:border-primary/50 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring\"\n              aria-label={copied ? \"Copied\" : \"Copy address\"}\n            >\n              {renderQr ? (\n                renderQr(address)\n              ) : (\n                <AddressPattern address={address} size={effectiveSize} />\n              )}\n            </button>\n          </TooltipTrigger>\n          <TooltipContent>\n            <p>{copied ? \"Copied!\" : \"Tap to copy address\"}</p>\n          </TooltipContent>\n        </Tooltip>\n      </TooltipProvider>\n      {copied && (\n        <Badge variant=\"secondary\" className=\"gap-1 text-xs\">\n          <Check className=\"size-3\" data-icon aria-hidden=\"true\" />\n          Copied\n        </Badge>\n      )}\n    </div>\n  )\n}\n\n// ---------------------------------------------------------------------------\n// Variant: Inline\n// ---------------------------------------------------------------------------\n\nfunction InlineVariant({\n  address,\n  copied,\n  onCopy,\n  className,\n}: ReceiveAddressUIProps) {\n  if (!address) {\n    return <AddressSkeleton />\n  }\n\n  return (\n    <div className={cn(\"flex items-center gap-2\", className)}>\n      <span className=\"min-w-0 flex-1 truncate font-mono text-sm text-foreground\">\n        {truncate(address, 12, 8)}\n      </span>\n      <TooltipProvider delayDuration={200}>\n        <Tooltip>\n          <TooltipTrigger asChild>\n            <Button\n              variant=\"ghost\"\n              size=\"icon\"\n              className=\"size-8 flex-shrink-0\"\n              onClick={onCopy}\n              aria-label={copied ? \"Copied\" : \"Copy address\"}\n            >\n              {copied ? (\n                <Check\n                  className=\"text-primary\"\n                  data-icon\n                  aria-hidden=\"true\"\n                />\n              ) : (\n                <Copy\n                  data-icon\n                  aria-hidden=\"true\"\n                />\n              )}\n            </Button>\n          </TooltipTrigger>\n          <TooltipContent>\n            <p>{copied ? \"Copied!\" : \"Copy address\"}</p>\n          </TooltipContent>\n        </Tooltip>\n      </TooltipProvider>\n    </div>\n  )\n}\n\n// ---------------------------------------------------------------------------\n// Main UI\n// ---------------------------------------------------------------------------\n\n/**\n * Pure presentation component for the receive-address block.\n *\n * Displays a QR code (or deterministic placeholder) and the deposit address\n * with copy and optional rotation controls. Supports three variants:\n * - **default**: Card with QR + address + rotate button\n * - **compact**: Tappable QR only (smaller)\n * - **inline**: Address text with copy icon (no QR)\n *\n * All data and callbacks are provided via props. For a real QR code,\n * pass a `renderQr` function.\n */\nexport function ReceiveAddressUI(props: ReceiveAddressUIProps) {\n  const variant = props.variant ?? \"default\"\n\n  switch (variant) {\n    case \"compact\":\n      return <CompactVariant {...props} />\n    case \"inline\":\n      return <InlineVariant {...props} />\n    default:\n      return <DefaultVariant {...props} />\n  }\n}\n",
      "type": "registry:component",
      "target": "~/components/blocks/receive-address/receive-address-ui.tsx"
    },
    {
      "path": "registry/new-york/blocks/receive-address/use-receive-address.ts",
      "content": "import { useCallback, useEffect, useRef, useState } from \"react\"\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nexport interface UseReceiveAddressOptions {\n  /** The current deposit address to display */\n  address: string | null\n  /** Callback to rotate to a new address */\n  onRotate?: () => Promise<string>\n  /** Callback fired after address is copied */\n  onCopy?: (address: string) => void\n}\n\nexport interface UseReceiveAddressReturn {\n  /** Whether the address was just copied (2s feedback window) */\n  copied: boolean\n  /** Whether address rotation is in progress */\n  isRotating: boolean\n  /** Error from the last rotation attempt */\n  rotateError: Error | null\n  /** Copy the current address to clipboard */\n  copyAddress: () => void\n  /** Rotate to a new address via the onRotate callback */\n  rotateAddress: () => void\n  /** Clear the rotation error */\n  resetError: () => void\n}\n\n// ---------------------------------------------------------------------------\n// Constants\n// ---------------------------------------------------------------------------\n\nconst COPY_FEEDBACK_MS = 2000\n\n// ---------------------------------------------------------------------------\n// Hook\n// ---------------------------------------------------------------------------\n\n/**\n * Manages copy-to-clipboard feedback and address rotation for the\n * receive-address block. Pure logic — no UI or SDK imports.\n *\n * @example\n * ```ts\n * const { copied, isRotating, copyAddress, rotateAddress } =\n *   useReceiveAddress({ address, onRotate, onCopy })\n * ```\n */\nexport function useReceiveAddress({\n  address,\n  onRotate,\n  onCopy,\n}: UseReceiveAddressOptions): UseReceiveAddressReturn {\n  const [copied, setCopied] = useState(false)\n  const [isRotating, setIsRotating] = useState(false)\n  const [rotateError, setRotateError] = useState<Error | null>(null)\n  const timerRef = useRef<ReturnType<typeof setTimeout> | null>(null)\n\n  // Clean up copy feedback timer on unmount\n  useEffect(() => {\n    return () => {\n      if (timerRef.current) clearTimeout(timerRef.current)\n    }\n  }, [])\n\n  const copyAddress = useCallback(() => {\n    if (!address || typeof window === \"undefined\") return\n    void navigator.clipboard.writeText(address).then(() => {\n      setCopied(true)\n      onCopy?.(address)\n      if (timerRef.current) clearTimeout(timerRef.current)\n      timerRef.current = setTimeout(() => setCopied(false), COPY_FEEDBACK_MS)\n    })\n  }, [address, onCopy])\n\n  const rotateAddress = useCallback(() => {\n    if (!onRotate) return\n    setIsRotating(true)\n    setRotateError(null)\n    void onRotate()\n      .catch((err: unknown) => {\n        const e = err instanceof Error ? err : new Error(String(err))\n        setRotateError(e)\n      })\n      .finally(() => {\n        setIsRotating(false)\n      })\n  }, [onRotate])\n\n  const resetError = useCallback(() => setRotateError(null), [])\n\n  return {\n    copied,\n    isRotating,\n    rotateError,\n    copyAddress,\n    rotateAddress,\n    resetError,\n  }\n}\n",
      "type": "registry:component",
      "target": "~/components/blocks/receive-address/use-receive-address.ts"
    }
  ],
  "categories": [
    "wallet"
  ],
  "type": "registry:block"
}