{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "like-button",
  "title": "Like Button",
  "author": "Satchmo",
  "description": "Optimistic like/unlike toggle for BSocial content with heart or thumbs-up icon, count display, and default, compact, text variants",
  "dependencies": [
    "class-variance-authority",
    "lucide-react"
  ],
  "registryDependencies": [
    "badge",
    "button"
  ],
  "files": [
    {
      "path": "registry/new-york/blocks/like-button/index.tsx",
      "content": "\"use client\"\n\nimport { type VariantProps } from \"class-variance-authority\"\nimport { LikeButtonUI, likeButtonVariants } from \"./ui\"\nimport {\n  useLike,\n  type LikeResult,\n  type UseLikeReturn,\n  type UseLikeOptions,\n} from \"./use-like\"\n\n// ---------------------------------------------------------------------------\n// Re-exports\n// ---------------------------------------------------------------------------\n\nexport { LikeButtonUI, likeButtonVariants, type LikeButtonUIProps } from \"./ui\"\nexport {\n  useLike,\n  type LikeResult,\n  type UseLikeReturn,\n  type UseLikeOptions,\n} from \"./use-like\"\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nexport interface LikeButtonProps\n  extends VariantProps<typeof likeButtonVariants> {\n  /** Additional CSS classes */\n  className?: string\n  /** Transaction ID of the content to like */\n  txid: string\n  /** Current like count to display */\n  count?: number\n  /** Whether the current user has already liked this content */\n  liked?: boolean\n  /** Called to broadcast a like action */\n  onLike: (txid: string) => Promise<LikeResult>\n  /** Called to broadcast an unlike action */\n  onUnlike?: (txid: string) => Promise<LikeResult>\n  /** Called after a successful like/unlike */\n  onToggled?: (liked: boolean, result: LikeResult) => void\n  /** Called on error */\n  onError?: (error: Error) => void\n  /** Disable the button */\n  disabled?: boolean\n  /** Use thumbs-up icon instead of heart */\n  useThumbsUp?: boolean\n}\n\n// ---------------------------------------------------------------------------\n// Composed component\n// ---------------------------------------------------------------------------\n\n/**\n * An on-chain like/unlike toggle button for BSocial content.\n *\n * Delegates the actual transaction building and broadcasting to `onLike`\n * and `onUnlike` callbacks. Manages optimistic UI updates internally.\n *\n * @example\n * ```tsx\n * import { LikeButton } from \"@/components/blocks/like-button\"\n *\n * <LikeButton\n *   txid=\"abc123...\"\n *   count={42}\n *   liked={false}\n *   onLike={async (txid) => {\n *     // build BSocial like tx and broadcast\n *     return { txid: \"def456...\" }\n *   }}\n * />\n * ```\n */\nexport function LikeButton({\n  variant = \"default\",\n  className,\n  txid,\n  count = 0,\n  liked = false,\n  onLike,\n  onUnlike,\n  onToggled,\n  onError,\n  disabled = false,\n  useThumbsUp = false,\n}: LikeButtonProps) {\n  const hook = useLike({\n    txid,\n    count,\n    liked,\n    onLike,\n    onUnlike,\n    onToggled,\n    onError,\n  })\n\n  return (\n    <LikeButtonUI\n      variant={variant}\n      className={className}\n      isLiked={hook.isLiked}\n      displayCount={hook.displayCount}\n      isLoading={hook.isLoading}\n      onToggle={hook.handleToggle}\n      disabled={disabled}\n      useThumbsUp={useThumbsUp}\n    />\n  )\n}\n",
      "type": "registry:block",
      "target": "~/components/blocks/like-button/index.tsx"
    },
    {
      "path": "registry/new-york/blocks/like-button/ui.tsx",
      "content": "\"use client\"\n\nimport { cva, type VariantProps } from \"class-variance-authority\"\nimport { cn } from \"@/lib/utils\"\nimport { Badge } from \"@/components/ui/badge\"\nimport { Button } from \"@/components/ui/button\"\nimport { Heart, Loader2, ThumbsUp } from \"lucide-react\"\n\n// ---------------------------------------------------------------------------\n// Variant definitions\n// ---------------------------------------------------------------------------\n\nexport const likeButtonVariants = cva(\n  \"inline-flex items-center gap-1.5 font-medium transition-all select-none\",\n  {\n    variants: {\n      variant: {\n        default:\n          \"rounded-md border border-input bg-background px-3 py-1.5 text-sm shadow-sm hover:bg-accent hover:text-accent-foreground\",\n        compact:\n          \"rounded-full border border-input bg-background size-8 justify-center text-sm shadow-sm hover:bg-accent hover:text-accent-foreground p-0\",\n        text: \"text-sm text-muted-foreground hover:text-foreground bg-transparent px-1 py-0.5\",\n      },\n    },\n    defaultVariants: {\n      variant: \"default\",\n    },\n  },\n)\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nexport interface LikeButtonUIProps\n  extends VariantProps<typeof likeButtonVariants> {\n  /** Additional CSS classes */\n  className?: string\n  /** Whether the content is currently liked */\n  isLiked: boolean\n  /** Current display count */\n  displayCount: number\n  /** Whether a like/unlike action is in progress */\n  isLoading: boolean\n  /** Toggle the like state */\n  onToggle: () => void\n  /** Disable the button */\n  disabled?: boolean\n  /** Use thumbs-up icon instead of heart */\n  useThumbsUp?: boolean\n}\n\n// ---------------------------------------------------------------------------\n// Component\n// ---------------------------------------------------------------------------\n\nexport function LikeButtonUI({\n  variant = \"default\",\n  className,\n  isLiked,\n  displayCount,\n  isLoading,\n  onToggle,\n  disabled = false,\n  useThumbsUp = false,\n}: LikeButtonUIProps) {\n  const isCompact = variant === \"compact\"\n  const IconComponent = useThumbsUp ? ThumbsUp : Heart\n\n  return (\n    <Button\n      variant=\"ghost\"\n      size=\"sm\"\n      className={cn(\n        likeButtonVariants({ variant }),\n        isLiked && \"text-primary border-primary/30 bg-primary/5\",\n        isLiked && variant === \"text\" && \"bg-transparent text-primary\",\n        className,\n      )}\n      onClick={onToggle}\n      disabled={disabled || isLoading}\n      aria-label={isLiked ? \"Unlike\" : \"Like\"}\n      aria-pressed={isLiked}\n    >\n      {isLoading ? (\n        <Loader2\n          className={cn(\"animate-spin\", isLiked && \"fill-current\")}\n          data-icon=\"inline-start\"\n          aria-hidden=\"true\"\n        />\n      ) : (\n        <IconComponent\n          className={cn(\n            \"transition-transform\",\n            isLiked && \"fill-current scale-110\",\n            !isLiked && \"hover:scale-110\",\n          )}\n          data-icon=\"inline-start\"\n          aria-hidden=\"true\"\n        />\n      )}\n      {!isCompact && displayCount > 0 && (\n        <Badge variant=\"secondary\" className=\"h-5 min-w-5 justify-center px-1.5 text-xs tabular-nums\">\n          {displayCount}\n        </Badge>\n      )}\n    </Button>\n  )\n}\n",
      "type": "registry:component",
      "target": "~/components/blocks/like-button/ui.tsx"
    },
    {
      "path": "registry/new-york/blocks/like-button/use-like.ts",
      "content": "import { useCallback, useState } from \"react\"\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nexport interface LikeResult {\n  /** Transaction ID of the like/unlike action */\n  txid?: string\n  /** Raw transaction hex */\n  rawtx?: string\n  /** Error message if the action failed */\n  error?: string\n}\n\nexport interface UseLikeOptions {\n  /** Transaction ID of the content to like */\n  txid: string\n  /** Current like count to display */\n  count?: number\n  /** Whether the current user has already liked this content */\n  liked?: boolean\n  /** Called to broadcast a like action */\n  onLike: (txid: string) => Promise<LikeResult>\n  /** Called to broadcast an unlike action */\n  onUnlike?: (txid: string) => Promise<LikeResult>\n  /** Called after a successful like/unlike */\n  onToggled?: (liked: boolean, result: LikeResult) => void\n  /** Called on error */\n  onError?: (error: Error) => void\n}\n\nexport interface UseLikeReturn {\n  /** Whether the content is currently liked */\n  isLiked: boolean\n  /** Current display count */\n  displayCount: number\n  /** Whether a like/unlike action is in progress */\n  isLoading: boolean\n  /** Toggle the like state */\n  handleToggle: () => Promise<void>\n}\n\n// ---------------------------------------------------------------------------\n// Hook\n// ---------------------------------------------------------------------------\n\nexport function useLike({\n  txid,\n  count = 0,\n  liked = false,\n  onLike,\n  onUnlike,\n  onToggled,\n  onError,\n}: UseLikeOptions): UseLikeReturn {\n  const [isLiked, setIsLiked] = useState(liked)\n  const [displayCount, setDisplayCount] = useState(count)\n  const [isLoading, setIsLoading] = useState(false)\n\n  const handleToggle = useCallback(async () => {\n    if (isLoading) return\n\n    const wasLiked = isLiked\n\n    // Optimistic update\n    setIsLiked(!wasLiked)\n    setDisplayCount((prev) => (wasLiked ? Math.max(0, prev - 1) : prev + 1))\n    setIsLoading(true)\n\n    try {\n      let result: LikeResult\n\n      if (wasLiked && onUnlike) {\n        result = await onUnlike(txid)\n      } else if (wasLiked) {\n        // No unlike handler, revert optimistic update\n        setIsLiked(true)\n        setDisplayCount((prev) => prev + 1)\n        setIsLoading(false)\n        return\n      } else {\n        result = await onLike(txid)\n      }\n\n      if (result.error) {\n        // Revert optimistic update\n        setIsLiked(wasLiked)\n        setDisplayCount((prev) => (wasLiked ? prev + 1 : Math.max(0, prev - 1)))\n        onError?.(new Error(result.error))\n      } else {\n        onToggled?.(!wasLiked, result)\n      }\n    } catch (err) {\n      // Revert optimistic update\n      setIsLiked(wasLiked)\n      setDisplayCount((prev) => (wasLiked ? prev + 1 : Math.max(0, prev - 1)))\n      const error = err instanceof Error ? err : new Error(\"Failed to toggle like\")\n      onError?.(error)\n    } finally {\n      setIsLoading(false)\n    }\n  }, [isLoading, isLiked, txid, onLike, onUnlike, onToggled, onError])\n\n  return {\n    isLiked,\n    displayCount,\n    isLoading,\n    handleToggle,\n  }\n}\n",
      "type": "registry:component",
      "target": "~/components/blocks/like-button/use-like.ts"
    }
  ],
  "type": "registry:block"
}