@medialane/ui 0.310.0 → 0.311.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (56) hide show
  1. package/dist/components/account/input-otp.d.cts +2 -2
  2. package/dist/components/account/input-otp.d.ts +2 -2
  3. package/dist/components/activity-card.cjs +2 -2
  4. package/dist/components/activity-card.cjs.map +1 -1
  5. package/dist/components/activity-card.js +2 -2
  6. package/dist/components/activity-card.js.map +1 -1
  7. package/dist/components/animated-token-media.cjs +2 -2
  8. package/dist/components/animated-token-media.cjs.map +1 -1
  9. package/dist/components/animated-token-media.js +2 -2
  10. package/dist/components/animated-token-media.js.map +1 -1
  11. package/dist/components/asset-card.cjs +1 -1
  12. package/dist/components/asset-card.cjs.map +1 -1
  13. package/dist/components/asset-card.js +1 -1
  14. package/dist/components/asset-card.js.map +1 -1
  15. package/dist/components/badge.d.cts +1 -1
  16. package/dist/components/badge.d.ts +1 -1
  17. package/dist/components/button.d.cts +2 -2
  18. package/dist/components/button.d.ts +2 -2
  19. package/dist/components/collection-card.cjs +1 -1
  20. package/dist/components/collection-card.cjs.map +1 -1
  21. package/dist/components/collection-card.js +1 -1
  22. package/dist/components/collection-card.js.map +1 -1
  23. package/dist/components/creators-page.cjs +2 -2
  24. package/dist/components/creators-page.cjs.map +1 -1
  25. package/dist/components/creators-page.js +2 -2
  26. package/dist/components/creators-page.js.map +1 -1
  27. package/dist/components/drop/drop-card.cjs +1 -1
  28. package/dist/components/drop/drop-card.cjs.map +1 -1
  29. package/dist/components/drop/drop-card.js +1 -1
  30. package/dist/components/drop/drop-card.js.map +1 -1
  31. package/dist/components/editions-page.cjs +1 -1
  32. package/dist/components/editions-page.cjs.map +1 -1
  33. package/dist/components/editions-page.js +1 -1
  34. package/dist/components/editions-page.js.map +1 -1
  35. package/dist/components/launchpad/edition-mint-schema.d.cts +2 -2
  36. package/dist/components/launchpad/edition-mint-schema.d.ts +2 -2
  37. package/dist/components/listing-card.cjs +4 -4
  38. package/dist/components/listing-card.cjs.map +1 -1
  39. package/dist/components/listing-card.js +4 -4
  40. package/dist/components/listing-card.js.map +1 -1
  41. package/dist/components/media-wallet/nft-strip.cjs +1 -1
  42. package/dist/components/media-wallet/nft-strip.cjs.map +1 -1
  43. package/dist/components/media-wallet/nft-strip.js +1 -1
  44. package/dist/components/media-wallet/nft-strip.js.map +1 -1
  45. package/dist/components/remix-browse-page.cjs +1 -1
  46. package/dist/components/remix-browse-page.cjs.map +1 -1
  47. package/dist/components/remix-browse-page.js +1 -1
  48. package/dist/components/remix-browse-page.js.map +1 -1
  49. package/dist/components/token-card.cjs +1 -1
  50. package/dist/components/token-card.cjs.map +1 -1
  51. package/dist/components/token-card.js +1 -1
  52. package/dist/components/token-card.js.map +1 -1
  53. package/dist/medialane.css +7 -0
  54. package/dist/utils/media-wallet/use-erc20-balance.d.cts +1 -1
  55. package/dist/utils/media-wallet/use-erc20-balance.d.ts +1 -1
  56. package/package.json +1 -1
@@ -46,7 +46,7 @@ function ListingCard({ order: rawOrder, inCart = false, onBuy, onCart, overflowM
46
46
  const assetHref = buildAssetHref("STARKNET", order.nftContract ?? "", order.nftTokenId ?? "");
47
47
  const showActionBar = !!(onBuy || onCart || overflowMenu || primaryAction);
48
48
  if (compact) {
49
- return /* @__PURE__ */ jsx(MotionCard, { className: "card-base", children: /* @__PURE__ */ jsxs(Link, { href: assetHref, className: "block", children: [
49
+ return /* @__PURE__ */ jsx(MotionCard, { className: "card-base group", children: /* @__PURE__ */ jsxs(Link, { href: assetHref, className: "block", children: [
50
50
  /* @__PURE__ */ jsx("div", { className: "relative aspect-square bg-muted overflow-hidden", children: /* @__PURE__ */ jsx(
51
51
  AnimatedTokenMedia,
52
52
  {
@@ -56,7 +56,7 @@ function ListingCard({ order: rawOrder, inCart = false, onBuy, onCart, overflowM
56
56
  alt: name,
57
57
  mode: "fill",
58
58
  sizes: "(max-width: 640px) 33vw, 20vw",
59
- className: "object-cover group-hover:scale-105 transition-transform duration-500",
59
+ className: "object-cover",
60
60
  imgError,
61
61
  onImageError: () => setImgError(true),
62
62
  fallback: /* @__PURE__ */ jsx("div", { className: "absolute inset-0 bg-gradient-to-br from-brand-purple/15 to-brand-blue/15" })
@@ -69,7 +69,7 @@ function ListingCard({ order: rawOrder, inCart = false, onBuy, onCart, overflowM
69
69
  ] })
70
70
  ] }) });
71
71
  }
72
- return /* @__PURE__ */ jsx(MotionCard, { className: "card-base", children: /* @__PURE__ */ jsxs(Link, { href: assetHref, className: "block", children: [
72
+ return /* @__PURE__ */ jsx(MotionCard, { className: "card-base group", children: /* @__PURE__ */ jsxs(Link, { href: assetHref, className: "block", children: [
73
73
  /* @__PURE__ */ jsx("div", { className: "relative aspect-square bg-muted overflow-hidden", children: /* @__PURE__ */ jsx(
74
74
  AnimatedTokenMedia,
75
75
  {
@@ -79,7 +79,7 @@ function ListingCard({ order: rawOrder, inCart = false, onBuy, onCart, overflowM
79
79
  alt: name,
80
80
  mode: "fill",
81
81
  sizes: "(max-width: 640px) 50vw, (max-width: 1024px) 33vw, 25vw",
82
- className: "object-cover group-hover:scale-105 transition-transform duration-500",
82
+ className: "object-cover",
83
83
  imgError,
84
84
  onImageError: () => setImgError(true),
85
85
  fallback: /* @__PURE__ */ jsx("div", { className: "absolute inset-0 bg-gradient-to-br from-brand-purple/15 to-brand-blue/15" })
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/components/listing-card.tsx"],"sourcesContent":["\"use client\";\n\nimport { useState } from \"react\";\nimport Link from \"./link.js\";\nimport { ShoppingCart, Check, Zap, ArrowUpRight } from \"lucide-react\";\nimport { MotionCard } from \"./motion-primitives.js\";\nimport { CurrencyIcon } from \"./currency-icon.js\";\nimport { AnimatedTokenMedia } from \"./animated-token-media.js\";\nimport { cn } from \"../utils/cn.js\";\nimport { formatDisplayPrice, isStableCurrency } from \"../utils/format.js\";\nimport { DualPrice } from \"./dual-price.js\";\nimport { timeAgo } from \"../utils/time.js\";\nimport { isLivingRenderCollection } from \"../data/living-render-collections.js\";\nimport { assetHref as buildAssetHref, type ApiOrder, type Chain } from \"@medialane/sdk\";\nimport { useImageUrl } from \"../utils/use-image-url.js\";\n\ntype OrderWithAnimation = ApiOrder & { token: (ApiOrder[\"token\"] & { animationUrl?: string | null }) | null };\n\nfunction PriceLine({\n amountFormatted, currency, usdValue,\n}: {\n amountFormatted: string | null | undefined;\n currency: string | null | undefined;\n usdValue?: string | null;\n}) {\n if (!amountFormatted) return null;\n if (!usdValue) {\n return (\n <p className=\"text-2xs font-bold price-value inline-flex items-center gap-1\">\n {currency && <CurrencyIcon symbol={currency} size={11} />}\n {formatDisplayPrice(amountFormatted)}\n </p>\n );\n }\n const stable = isStableCurrency(currency);\n return (\n <>\n <p className=\"text-2xs font-bold price-value\">\n {usdValue}\n </p>\n <p className=\"text-2xs text-muted-foreground/70 inline-flex items-center gap-1\">\n {currency && <CurrencyIcon symbol={currency} size={10} />}\n {stable ? currency : `${formatDisplayPrice(amountFormatted)} ${currency}`}\n </p>\n </>\n );\n}\n\nexport interface ListingCardProps {\n order: ApiOrder;\n inCart?: boolean;\n onBuy?: (order: ApiOrder) => void;\n onCart?: (order: ApiOrder) => void;\n\n overflowMenu?: React.ReactNode;\n\n primaryAction?: React.ReactNode;\n compact?: boolean;\n\n imageUrl?: string | null;\n\n usdValue?: string | null;\n}\n\nexport function ListingCard({ order: rawOrder, inCart = false, onBuy, onCart, overflowMenu, primaryAction, compact = false, imageUrl, usdValue }: ListingCardProps) {\n const resolveImage = useImageUrl();\n const order = rawOrder as OrderWithAnimation;\n const [imgError, setImgError] = useState(false);\n const isListing = order.offer.itemType === \"ERC721\" || order.offer.itemType === \"ERC1155\";\n const name = order.token?.name ?? `Token #${order.nftTokenId}`;\n const image = imageUrl !== undefined ? imageUrl : (order.token?.image ? resolveImage(order.token.image, { width: 480 }) : null);\n\n const animationUrl = order.token?.animationUrl ?? null;\n const live = !!(order.chain && order.nftContract) && isLivingRenderCollection(order.chain.toUpperCase() as Chain, order.nftContract!);\n const assetHref = buildAssetHref(\"STARKNET\", order.nftContract ?? \"\", order.nftTokenId ?? \"\");\n\n const showActionBar = !!(onBuy || onCart || overflowMenu || primaryAction);\n\n if (compact) {\n return (\n <MotionCard className=\"card-base\">\n <Link href={assetHref} className=\"block\">\n <div className=\"relative aspect-square bg-muted overflow-hidden\">\n <AnimatedTokenMedia\n image={image}\n animationUrl={animationUrl}\n live={live}\n alt={name}\n mode=\"fill\"\n sizes=\"(max-width: 640px) 33vw, 20vw\"\n className=\"object-cover group-hover:scale-105 transition-transform duration-500\"\n imgError={imgError}\n onImageError={() => setImgError(true)}\n fallback={\n <div className=\"absolute inset-0 bg-gradient-to-br from-brand-purple/15 to-brand-blue/15\" />\n }\n />\n </div>\n <div className=\"p-2.5 space-y-0.5\">\n <p className=\"text-xs font-semibold line-clamp-2 break-words\">{name}</p>\n <PriceLine amountFormatted={order.price?.formatted} currency={order.price?.currency} usdValue={usdValue} />\n <p className=\"text-2xs text-muted-foreground\">{timeAgo(order.createdAt)}</p>\n </div>\n </Link>\n </MotionCard>\n );\n }\n\n return (\n <MotionCard className=\"card-base\">\n <Link href={assetHref} className=\"block\">\n <div className=\"relative aspect-square bg-muted overflow-hidden\">\n <AnimatedTokenMedia\n image={image}\n animationUrl={animationUrl}\n live={live}\n alt={name}\n mode=\"fill\"\n sizes=\"(max-width: 640px) 50vw, (max-width: 1024px) 33vw, 25vw\"\n className=\"object-cover group-hover:scale-105 transition-transform duration-500\"\n imgError={imgError}\n onImageError={() => setImgError(true)}\n fallback={\n <div className=\"absolute inset-0 bg-gradient-to-br from-brand-purple/15 to-brand-blue/15\" />\n }\n />\n </div>\n\n <div className=\"p-3.5 space-y-3\">\n <div className=\"min-w-0\">\n <p className=\"text-base font-semibold tracking-tight line-clamp-2 break-words leading-snug\">{name}</p>\n </div>\n\n {order.price?.formatted && (\n <DualPrice amountFormatted={order.price.formatted} currency={order.price.currency} usdValue={usdValue} scale=\"card\" />\n )}\n\n {showActionBar && (\n <div className=\"flex items-center gap-1.5\">\n {isListing ? (\n primaryAction ? (\n primaryAction\n ) : onBuy ? (\n <div className=\"btn-border-animated p-[1.5px] rounded-[10px] flex-1 h-9\">\n <button\n className=\"w-full h-full rounded-[9px] bg-background flex items-center justify-center gap-1.5 text-xs font-semibold text-foreground hover:bg-muted/60 transition-all active:scale-[0.98]\"\n onClick={(e) => { e.preventDefault(); onBuy(order); }}\n >\n <Zap className=\"h-3.5 w-3.5 shrink-0\" /> Buy\n </button>\n </div>\n ) : (\n <Link\n href={assetHref}\n className=\"flex-1 h-9 inline-flex items-center justify-center gap-1.5 rounded-[9px] border border-border bg-background text-xs font-semibold text-foreground hover:bg-muted/60 transition-all active:scale-[0.98]\"\n >\n <ArrowUpRight className=\"h-3.5 w-3.5 shrink-0\" /> View\n </Link>\n )\n ) : (\n <Link\n href={assetHref}\n className=\"flex-1 h-9 inline-flex items-center justify-center gap-1.5 rounded-[9px] border border-border bg-background text-xs font-semibold text-foreground hover:bg-muted/60 transition-all active:scale-[0.98]\"\n >\n <ArrowUpRight className=\"h-3.5 w-3.5 shrink-0\" /> View asset\n </Link>\n )}\n {onCart && (\n <button\n className={cn(\n \"min-h-11 min-w-11 shrink-0 rounded-[9px] border flex items-center justify-center transition-colors\",\n inCart\n ? \"border-brand-orange/50 bg-brand-orange/10 text-brand-orange\"\n : \"border-border bg-background hover:bg-muted text-foreground\"\n )}\n onClick={(e) => { e.preventDefault(); if (!inCart) onCart(order); }}\n disabled={inCart}\n aria-label={inCart ? \"Added to cart\" : \"Add to cart\"}\n >\n {inCart ? <Check className=\"h-3.5 w-3.5\" /> : <ShoppingCart className=\"h-3.5 w-3.5\" />}\n </button>\n )}\n {overflowMenu}\n </div>\n )}\n </div>\n </Link>\n </MotionCard>\n );\n}\n\nexport function ListingCardSkeleton() {\n return (\n <div className=\"card-base\">\n <div className=\"aspect-square w-full bg-muted animate-pulse\" />\n <div className=\"p-3 space-y-2.5\">\n <div className=\"space-y-1\">\n <div className=\"h-4 w-3/4 bg-muted animate-pulse rounded\" />\n <div className=\"h-3 w-1/2 bg-muted animate-pulse rounded\" />\n </div>\n <div className=\"h-6 w-1/2 bg-muted animate-pulse rounded\" />\n <div className=\"h-3 w-2/3 bg-muted animate-pulse rounded\" />\n </div>\n </div>\n );\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../src/components/listing-card.tsx"],"sourcesContent":["\"use client\";\n\nimport { useState } from \"react\";\nimport Link from \"./link.js\";\nimport { ShoppingCart, Check, Zap, ArrowUpRight } from \"lucide-react\";\nimport { MotionCard } from \"./motion-primitives.js\";\nimport { CurrencyIcon } from \"./currency-icon.js\";\nimport { AnimatedTokenMedia } from \"./animated-token-media.js\";\nimport { cn } from \"../utils/cn.js\";\nimport { formatDisplayPrice, isStableCurrency } from \"../utils/format.js\";\nimport { DualPrice } from \"./dual-price.js\";\nimport { timeAgo } from \"../utils/time.js\";\nimport { isLivingRenderCollection } from \"../data/living-render-collections.js\";\nimport { assetHref as buildAssetHref, type ApiOrder, type Chain } from \"@medialane/sdk\";\nimport { useImageUrl } from \"../utils/use-image-url.js\";\n\ntype OrderWithAnimation = ApiOrder & { token: (ApiOrder[\"token\"] & { animationUrl?: string | null }) | null };\n\nfunction PriceLine({\n amountFormatted, currency, usdValue,\n}: {\n amountFormatted: string | null | undefined;\n currency: string | null | undefined;\n usdValue?: string | null;\n}) {\n if (!amountFormatted) return null;\n if (!usdValue) {\n return (\n <p className=\"text-2xs font-bold price-value inline-flex items-center gap-1\">\n {currency && <CurrencyIcon symbol={currency} size={11} />}\n {formatDisplayPrice(amountFormatted)}\n </p>\n );\n }\n const stable = isStableCurrency(currency);\n return (\n <>\n <p className=\"text-2xs font-bold price-value\">\n {usdValue}\n </p>\n <p className=\"text-2xs text-muted-foreground/70 inline-flex items-center gap-1\">\n {currency && <CurrencyIcon symbol={currency} size={10} />}\n {stable ? currency : `${formatDisplayPrice(amountFormatted)} ${currency}`}\n </p>\n </>\n );\n}\n\nexport interface ListingCardProps {\n order: ApiOrder;\n inCart?: boolean;\n onBuy?: (order: ApiOrder) => void;\n onCart?: (order: ApiOrder) => void;\n\n overflowMenu?: React.ReactNode;\n\n primaryAction?: React.ReactNode;\n compact?: boolean;\n\n imageUrl?: string | null;\n\n usdValue?: string | null;\n}\n\nexport function ListingCard({ order: rawOrder, inCart = false, onBuy, onCart, overflowMenu, primaryAction, compact = false, imageUrl, usdValue }: ListingCardProps) {\n const resolveImage = useImageUrl();\n const order = rawOrder as OrderWithAnimation;\n const [imgError, setImgError] = useState(false);\n const isListing = order.offer.itemType === \"ERC721\" || order.offer.itemType === \"ERC1155\";\n const name = order.token?.name ?? `Token #${order.nftTokenId}`;\n const image = imageUrl !== undefined ? imageUrl : (order.token?.image ? resolveImage(order.token.image, { width: 480 }) : null);\n\n const animationUrl = order.token?.animationUrl ?? null;\n const live = !!(order.chain && order.nftContract) && isLivingRenderCollection(order.chain.toUpperCase() as Chain, order.nftContract!);\n const assetHref = buildAssetHref(\"STARKNET\", order.nftContract ?? \"\", order.nftTokenId ?? \"\");\n\n const showActionBar = !!(onBuy || onCart || overflowMenu || primaryAction);\n\n if (compact) {\n return (\n <MotionCard className=\"card-base group\">\n <Link href={assetHref} className=\"block\">\n <div className=\"relative aspect-square bg-muted overflow-hidden\">\n <AnimatedTokenMedia\n image={image}\n animationUrl={animationUrl}\n live={live}\n alt={name}\n mode=\"fill\"\n sizes=\"(max-width: 640px) 33vw, 20vw\"\n className=\"object-cover\"\n imgError={imgError}\n onImageError={() => setImgError(true)}\n fallback={\n <div className=\"absolute inset-0 bg-gradient-to-br from-brand-purple/15 to-brand-blue/15\" />\n }\n />\n </div>\n <div className=\"p-2.5 space-y-0.5\">\n <p className=\"text-xs font-semibold line-clamp-2 break-words\">{name}</p>\n <PriceLine amountFormatted={order.price?.formatted} currency={order.price?.currency} usdValue={usdValue} />\n <p className=\"text-2xs text-muted-foreground\">{timeAgo(order.createdAt)}</p>\n </div>\n </Link>\n </MotionCard>\n );\n }\n\n return (\n <MotionCard className=\"card-base group\">\n <Link href={assetHref} className=\"block\">\n <div className=\"relative aspect-square bg-muted overflow-hidden\">\n <AnimatedTokenMedia\n image={image}\n animationUrl={animationUrl}\n live={live}\n alt={name}\n mode=\"fill\"\n sizes=\"(max-width: 640px) 50vw, (max-width: 1024px) 33vw, 25vw\"\n className=\"object-cover\"\n imgError={imgError}\n onImageError={() => setImgError(true)}\n fallback={\n <div className=\"absolute inset-0 bg-gradient-to-br from-brand-purple/15 to-brand-blue/15\" />\n }\n />\n </div>\n\n <div className=\"p-3.5 space-y-3\">\n <div className=\"min-w-0\">\n <p className=\"text-base font-semibold tracking-tight line-clamp-2 break-words leading-snug\">{name}</p>\n </div>\n\n {order.price?.formatted && (\n <DualPrice amountFormatted={order.price.formatted} currency={order.price.currency} usdValue={usdValue} scale=\"card\" />\n )}\n\n {showActionBar && (\n <div className=\"flex items-center gap-1.5\">\n {isListing ? (\n primaryAction ? (\n primaryAction\n ) : onBuy ? (\n <div className=\"btn-border-animated p-[1.5px] rounded-[10px] flex-1 h-9\">\n <button\n className=\"w-full h-full rounded-[9px] bg-background flex items-center justify-center gap-1.5 text-xs font-semibold text-foreground hover:bg-muted/60 transition-all active:scale-[0.98]\"\n onClick={(e) => { e.preventDefault(); onBuy(order); }}\n >\n <Zap className=\"h-3.5 w-3.5 shrink-0\" /> Buy\n </button>\n </div>\n ) : (\n <Link\n href={assetHref}\n className=\"flex-1 h-9 inline-flex items-center justify-center gap-1.5 rounded-[9px] border border-border bg-background text-xs font-semibold text-foreground hover:bg-muted/60 transition-all active:scale-[0.98]\"\n >\n <ArrowUpRight className=\"h-3.5 w-3.5 shrink-0\" /> View\n </Link>\n )\n ) : (\n <Link\n href={assetHref}\n className=\"flex-1 h-9 inline-flex items-center justify-center gap-1.5 rounded-[9px] border border-border bg-background text-xs font-semibold text-foreground hover:bg-muted/60 transition-all active:scale-[0.98]\"\n >\n <ArrowUpRight className=\"h-3.5 w-3.5 shrink-0\" /> View asset\n </Link>\n )}\n {onCart && (\n <button\n className={cn(\n \"min-h-11 min-w-11 shrink-0 rounded-[9px] border flex items-center justify-center transition-colors\",\n inCart\n ? \"border-brand-orange/50 bg-brand-orange/10 text-brand-orange\"\n : \"border-border bg-background hover:bg-muted text-foreground\"\n )}\n onClick={(e) => { e.preventDefault(); if (!inCart) onCart(order); }}\n disabled={inCart}\n aria-label={inCart ? \"Added to cart\" : \"Add to cart\"}\n >\n {inCart ? <Check className=\"h-3.5 w-3.5\" /> : <ShoppingCart className=\"h-3.5 w-3.5\" />}\n </button>\n )}\n {overflowMenu}\n </div>\n )}\n </div>\n </Link>\n </MotionCard>\n );\n}\n\nexport function ListingCardSkeleton() {\n return (\n <div className=\"card-base\">\n <div className=\"aspect-square w-full bg-muted animate-pulse\" />\n <div className=\"p-3 space-y-2.5\">\n <div className=\"space-y-1\">\n <div className=\"h-4 w-3/4 bg-muted animate-pulse rounded\" />\n <div className=\"h-3 w-1/2 bg-muted animate-pulse rounded\" />\n </div>\n <div className=\"h-6 w-1/2 bg-muted animate-pulse rounded\" />\n <div className=\"h-3 w-2/3 bg-muted animate-pulse rounded\" />\n </div>\n </div>\n );\n}\n"],"mappings":"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","names":[]}
@@ -63,7 +63,7 @@ function NftStrip({
63
63
  alt: item.name,
64
64
  fill: true,
65
65
  unoptimized: true,
66
- className: "object-cover transition-transform duration-200 group-hover:scale-[1.03]"
66
+ className: "object-cover card-media"
67
67
  }
68
68
  ) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "grid h-full w-full place-items-center", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(AssetFallbackIcon, {}) })
69
69
  },
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/components/media-wallet/nft-strip.tsx"],"sourcesContent":["\"use client\";\n\nimport Link from \"../link.js\";\nimport Image from \"../image.js\";\nimport type { NftItem } from \"../../utils/media-wallet/nft-items.js\";\n\nexport function NftStrip({\n items,\n isLoading,\n onNavigate,\n resolveImage,\n}: {\n items: NftItem[];\n isLoading: boolean;\n onNavigate?: () => void;\n resolveImage: (uri: string | null | undefined) => string | null;\n}) {\n if (isLoading) {\n return (\n <div data-testid=\"nft-strip-skeleton\" className=\"grid grid-cols-4 gap-2\">\n {Array.from({ length: 4 }).map((_, i) => (\n <div key={i} className=\"aspect-square animate-pulse rounded-xl bg-foreground/[0.06]\" />\n ))}\n </div>\n );\n }\n\n if (items.length === 0) {\n return (\n <p className=\"text-sm text-muted-foreground\">\n No NFTs yet — mint or collect your first one to see it here.\n </p>\n );\n }\n\n return (\n <div className=\"grid grid-cols-4 gap-2\">\n {items.map((item) => {\n const resolved = resolveImage(item.image);\n return (\n <Link\n key={item.key}\n href={item.href}\n title={item.name}\n onClick={onNavigate}\n className=\"group relative aspect-square overflow-hidden rounded-xl bg-foreground/[0.06] transition-opacity hover:opacity-90\"\n >\n {resolved ? (\n <Image\n src={resolved}\n alt={item.name}\n fill\n unoptimized\n className=\"object-cover transition-transform duration-200 group-hover:scale-[1.03]\"\n />\n ) : (\n <div className=\"grid h-full w-full place-items-center\">\n <AssetFallbackIcon />\n </div>\n )}\n </Link>\n );\n })}\n </div>\n );\n}\n\nfunction AssetFallbackIcon() {\n return (\n <svg width=\"18\" height=\"18\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"1.8\" className=\"text-muted-foreground\" strokeLinecap=\"round\" strokeLinejoin=\"round\">\n <rect x=\"3\" y=\"3\" width=\"18\" height=\"18\" rx=\"3\" />\n <circle cx=\"9\" cy=\"9\" r=\"1.5\" fill=\"currentColor\" stroke=\"none\" />\n <path d=\"M21 15l-5-5-9 9\" />\n </svg>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAqBU;AAnBV,kBAAiB;AACjB,mBAAkB;AAGX,SAAS,SAAS;AAAA,EACvB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAKG;AACD,MAAI,WAAW;AACb,WACE,4CAAC,SAAI,eAAY,sBAAqB,WAAU,0BAC7C,gBAAM,KAAK,EAAE,QAAQ,EAAE,CAAC,EAAE,IAAI,CAAC,GAAG,MACjC,4CAAC,SAAY,WAAU,iEAAb,CAA2E,CACtF,GACH;AAAA,EAEJ;AAEA,MAAI,MAAM,WAAW,GAAG;AACtB,WACE,4CAAC,OAAE,WAAU,iCAAgC,+EAE7C;AAAA,EAEJ;AAEA,SACE,4CAAC,SAAI,WAAU,0BACZ,gBAAM,IAAI,CAAC,SAAS;AACnB,UAAM,WAAW,aAAa,KAAK,KAAK;AACxC,WACE;AAAA,MAAC,YAAAA;AAAA,MAAA;AAAA,QAEC,MAAM,KAAK;AAAA,QACX,OAAO,KAAK;AAAA,QACZ,SAAS;AAAA,QACT,WAAU;AAAA,QAET,qBACC;AAAA,UAAC,aAAAC;AAAA,UAAA;AAAA,YACC,KAAK;AAAA,YACL,KAAK,KAAK;AAAA,YACV,MAAI;AAAA,YACJ,aAAW;AAAA,YACX,WAAU;AAAA;AAAA,QACZ,IAEA,4CAAC,SAAI,WAAU,yCACb,sDAAC,qBAAkB,GACrB;AAAA;AAAA,MAjBG,KAAK;AAAA,IAmBZ;AAAA,EAEJ,CAAC,GACH;AAEJ;AAEA,SAAS,oBAAoB;AAC3B,SACE,6CAAC,SAAI,OAAM,MAAK,QAAO,MAAK,SAAQ,aAAY,MAAK,QAAO,QAAO,gBAAe,aAAY,OAAM,WAAU,yBAAwB,eAAc,SAAQ,gBAAe,SACzK;AAAA,gDAAC,UAAK,GAAE,KAAI,GAAE,KAAI,OAAM,MAAK,QAAO,MAAK,IAAG,KAAI;AAAA,IAChD,4CAAC,YAAO,IAAG,KAAI,IAAG,KAAI,GAAE,OAAM,MAAK,gBAAe,QAAO,QAAO;AAAA,IAChE,4CAAC,UAAK,GAAE,mBAAkB;AAAA,KAC5B;AAEJ;","names":["Link","Image"]}
1
+ {"version":3,"sources":["../../../src/components/media-wallet/nft-strip.tsx"],"sourcesContent":["\"use client\";\n\nimport Link from \"../link.js\";\nimport Image from \"../image.js\";\nimport type { NftItem } from \"../../utils/media-wallet/nft-items.js\";\n\nexport function NftStrip({\n items,\n isLoading,\n onNavigate,\n resolveImage,\n}: {\n items: NftItem[];\n isLoading: boolean;\n onNavigate?: () => void;\n resolveImage: (uri: string | null | undefined) => string | null;\n}) {\n if (isLoading) {\n return (\n <div data-testid=\"nft-strip-skeleton\" className=\"grid grid-cols-4 gap-2\">\n {Array.from({ length: 4 }).map((_, i) => (\n <div key={i} className=\"aspect-square animate-pulse rounded-xl bg-foreground/[0.06]\" />\n ))}\n </div>\n );\n }\n\n if (items.length === 0) {\n return (\n <p className=\"text-sm text-muted-foreground\">\n No NFTs yet — mint or collect your first one to see it here.\n </p>\n );\n }\n\n return (\n <div className=\"grid grid-cols-4 gap-2\">\n {items.map((item) => {\n const resolved = resolveImage(item.image);\n return (\n <Link\n key={item.key}\n href={item.href}\n title={item.name}\n onClick={onNavigate}\n className=\"group relative aspect-square overflow-hidden rounded-xl bg-foreground/[0.06] transition-opacity hover:opacity-90\"\n >\n {resolved ? (\n <Image\n src={resolved}\n alt={item.name}\n fill\n unoptimized\n className=\"object-cover card-media\"\n />\n ) : (\n <div className=\"grid h-full w-full place-items-center\">\n <AssetFallbackIcon />\n </div>\n )}\n </Link>\n );\n })}\n </div>\n );\n}\n\nfunction AssetFallbackIcon() {\n return (\n <svg width=\"18\" height=\"18\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"1.8\" className=\"text-muted-foreground\" strokeLinecap=\"round\" strokeLinejoin=\"round\">\n <rect x=\"3\" y=\"3\" width=\"18\" height=\"18\" rx=\"3\" />\n <circle cx=\"9\" cy=\"9\" r=\"1.5\" fill=\"currentColor\" stroke=\"none\" />\n <path d=\"M21 15l-5-5-9 9\" />\n </svg>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAqBU;AAnBV,kBAAiB;AACjB,mBAAkB;AAGX,SAAS,SAAS;AAAA,EACvB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAKG;AACD,MAAI,WAAW;AACb,WACE,4CAAC,SAAI,eAAY,sBAAqB,WAAU,0BAC7C,gBAAM,KAAK,EAAE,QAAQ,EAAE,CAAC,EAAE,IAAI,CAAC,GAAG,MACjC,4CAAC,SAAY,WAAU,iEAAb,CAA2E,CACtF,GACH;AAAA,EAEJ;AAEA,MAAI,MAAM,WAAW,GAAG;AACtB,WACE,4CAAC,OAAE,WAAU,iCAAgC,+EAE7C;AAAA,EAEJ;AAEA,SACE,4CAAC,SAAI,WAAU,0BACZ,gBAAM,IAAI,CAAC,SAAS;AACnB,UAAM,WAAW,aAAa,KAAK,KAAK;AACxC,WACE;AAAA,MAAC,YAAAA;AAAA,MAAA;AAAA,QAEC,MAAM,KAAK;AAAA,QACX,OAAO,KAAK;AAAA,QACZ,SAAS;AAAA,QACT,WAAU;AAAA,QAET,qBACC;AAAA,UAAC,aAAAC;AAAA,UAAA;AAAA,YACC,KAAK;AAAA,YACL,KAAK,KAAK;AAAA,YACV,MAAI;AAAA,YACJ,aAAW;AAAA,YACX,WAAU;AAAA;AAAA,QACZ,IAEA,4CAAC,SAAI,WAAU,yCACb,sDAAC,qBAAkB,GACrB;AAAA;AAAA,MAjBG,KAAK;AAAA,IAmBZ;AAAA,EAEJ,CAAC,GACH;AAEJ;AAEA,SAAS,oBAAoB;AAC3B,SACE,6CAAC,SAAI,OAAM,MAAK,QAAO,MAAK,SAAQ,aAAY,MAAK,QAAO,QAAO,gBAAe,aAAY,OAAM,WAAU,yBAAwB,eAAc,SAAQ,gBAAe,SACzK;AAAA,gDAAC,UAAK,GAAE,KAAI,GAAE,KAAI,OAAM,MAAK,QAAO,MAAK,IAAG,KAAI;AAAA,IAChD,4CAAC,YAAO,IAAG,KAAI,IAAG,KAAI,GAAE,OAAM,MAAK,gBAAe,QAAO,QAAO;AAAA,IAChE,4CAAC,UAAK,GAAE,mBAAkB;AAAA,KAC5B;AAEJ;","names":["Link","Image"]}
@@ -30,7 +30,7 @@ function NftStrip({
30
30
  alt: item.name,
31
31
  fill: true,
32
32
  unoptimized: true,
33
- className: "object-cover transition-transform duration-200 group-hover:scale-[1.03]"
33
+ className: "object-cover card-media"
34
34
  }
35
35
  ) : /* @__PURE__ */ jsx("div", { className: "grid h-full w-full place-items-center", children: /* @__PURE__ */ jsx(AssetFallbackIcon, {}) })
36
36
  },
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/components/media-wallet/nft-strip.tsx"],"sourcesContent":["\"use client\";\n\nimport Link from \"../link.js\";\nimport Image from \"../image.js\";\nimport type { NftItem } from \"../../utils/media-wallet/nft-items.js\";\n\nexport function NftStrip({\n items,\n isLoading,\n onNavigate,\n resolveImage,\n}: {\n items: NftItem[];\n isLoading: boolean;\n onNavigate?: () => void;\n resolveImage: (uri: string | null | undefined) => string | null;\n}) {\n if (isLoading) {\n return (\n <div data-testid=\"nft-strip-skeleton\" className=\"grid grid-cols-4 gap-2\">\n {Array.from({ length: 4 }).map((_, i) => (\n <div key={i} className=\"aspect-square animate-pulse rounded-xl bg-foreground/[0.06]\" />\n ))}\n </div>\n );\n }\n\n if (items.length === 0) {\n return (\n <p className=\"text-sm text-muted-foreground\">\n No NFTs yet — mint or collect your first one to see it here.\n </p>\n );\n }\n\n return (\n <div className=\"grid grid-cols-4 gap-2\">\n {items.map((item) => {\n const resolved = resolveImage(item.image);\n return (\n <Link\n key={item.key}\n href={item.href}\n title={item.name}\n onClick={onNavigate}\n className=\"group relative aspect-square overflow-hidden rounded-xl bg-foreground/[0.06] transition-opacity hover:opacity-90\"\n >\n {resolved ? (\n <Image\n src={resolved}\n alt={item.name}\n fill\n unoptimized\n className=\"object-cover transition-transform duration-200 group-hover:scale-[1.03]\"\n />\n ) : (\n <div className=\"grid h-full w-full place-items-center\">\n <AssetFallbackIcon />\n </div>\n )}\n </Link>\n );\n })}\n </div>\n );\n}\n\nfunction AssetFallbackIcon() {\n return (\n <svg width=\"18\" height=\"18\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"1.8\" className=\"text-muted-foreground\" strokeLinecap=\"round\" strokeLinejoin=\"round\">\n <rect x=\"3\" y=\"3\" width=\"18\" height=\"18\" rx=\"3\" />\n <circle cx=\"9\" cy=\"9\" r=\"1.5\" fill=\"currentColor\" stroke=\"none\" />\n <path d=\"M21 15l-5-5-9 9\" />\n </svg>\n );\n}\n"],"mappings":";AAqBU,cAgDN,YAhDM;AAnBV,OAAO,UAAU;AACjB,OAAO,WAAW;AAGX,SAAS,SAAS;AAAA,EACvB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAKG;AACD,MAAI,WAAW;AACb,WACE,oBAAC,SAAI,eAAY,sBAAqB,WAAU,0BAC7C,gBAAM,KAAK,EAAE,QAAQ,EAAE,CAAC,EAAE,IAAI,CAAC,GAAG,MACjC,oBAAC,SAAY,WAAU,iEAAb,CAA2E,CACtF,GACH;AAAA,EAEJ;AAEA,MAAI,MAAM,WAAW,GAAG;AACtB,WACE,oBAAC,OAAE,WAAU,iCAAgC,+EAE7C;AAAA,EAEJ;AAEA,SACE,oBAAC,SAAI,WAAU,0BACZ,gBAAM,IAAI,CAAC,SAAS;AACnB,UAAM,WAAW,aAAa,KAAK,KAAK;AACxC,WACE;AAAA,MAAC;AAAA;AAAA,QAEC,MAAM,KAAK;AAAA,QACX,OAAO,KAAK;AAAA,QACZ,SAAS;AAAA,QACT,WAAU;AAAA,QAET,qBACC;AAAA,UAAC;AAAA;AAAA,YACC,KAAK;AAAA,YACL,KAAK,KAAK;AAAA,YACV,MAAI;AAAA,YACJ,aAAW;AAAA,YACX,WAAU;AAAA;AAAA,QACZ,IAEA,oBAAC,SAAI,WAAU,yCACb,8BAAC,qBAAkB,GACrB;AAAA;AAAA,MAjBG,KAAK;AAAA,IAmBZ;AAAA,EAEJ,CAAC,GACH;AAEJ;AAEA,SAAS,oBAAoB;AAC3B,SACE,qBAAC,SAAI,OAAM,MAAK,QAAO,MAAK,SAAQ,aAAY,MAAK,QAAO,QAAO,gBAAe,aAAY,OAAM,WAAU,yBAAwB,eAAc,SAAQ,gBAAe,SACzK;AAAA,wBAAC,UAAK,GAAE,KAAI,GAAE,KAAI,OAAM,MAAK,QAAO,MAAK,IAAG,KAAI;AAAA,IAChD,oBAAC,YAAO,IAAG,KAAI,IAAG,KAAI,GAAE,OAAM,MAAK,gBAAe,QAAO,QAAO;AAAA,IAChE,oBAAC,UAAK,GAAE,mBAAkB;AAAA,KAC5B;AAEJ;","names":[]}
1
+ {"version":3,"sources":["../../../src/components/media-wallet/nft-strip.tsx"],"sourcesContent":["\"use client\";\n\nimport Link from \"../link.js\";\nimport Image from \"../image.js\";\nimport type { NftItem } from \"../../utils/media-wallet/nft-items.js\";\n\nexport function NftStrip({\n items,\n isLoading,\n onNavigate,\n resolveImage,\n}: {\n items: NftItem[];\n isLoading: boolean;\n onNavigate?: () => void;\n resolveImage: (uri: string | null | undefined) => string | null;\n}) {\n if (isLoading) {\n return (\n <div data-testid=\"nft-strip-skeleton\" className=\"grid grid-cols-4 gap-2\">\n {Array.from({ length: 4 }).map((_, i) => (\n <div key={i} className=\"aspect-square animate-pulse rounded-xl bg-foreground/[0.06]\" />\n ))}\n </div>\n );\n }\n\n if (items.length === 0) {\n return (\n <p className=\"text-sm text-muted-foreground\">\n No NFTs yet — mint or collect your first one to see it here.\n </p>\n );\n }\n\n return (\n <div className=\"grid grid-cols-4 gap-2\">\n {items.map((item) => {\n const resolved = resolveImage(item.image);\n return (\n <Link\n key={item.key}\n href={item.href}\n title={item.name}\n onClick={onNavigate}\n className=\"group relative aspect-square overflow-hidden rounded-xl bg-foreground/[0.06] transition-opacity hover:opacity-90\"\n >\n {resolved ? (\n <Image\n src={resolved}\n alt={item.name}\n fill\n unoptimized\n className=\"object-cover card-media\"\n />\n ) : (\n <div className=\"grid h-full w-full place-items-center\">\n <AssetFallbackIcon />\n </div>\n )}\n </Link>\n );\n })}\n </div>\n );\n}\n\nfunction AssetFallbackIcon() {\n return (\n <svg width=\"18\" height=\"18\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"1.8\" className=\"text-muted-foreground\" strokeLinecap=\"round\" strokeLinejoin=\"round\">\n <rect x=\"3\" y=\"3\" width=\"18\" height=\"18\" rx=\"3\" />\n <circle cx=\"9\" cy=\"9\" r=\"1.5\" fill=\"currentColor\" stroke=\"none\" />\n <path d=\"M21 15l-5-5-9 9\" />\n </svg>\n );\n}\n"],"mappings":";AAqBU,cAgDN,YAhDM;AAnBV,OAAO,UAAU;AACjB,OAAO,WAAW;AAGX,SAAS,SAAS;AAAA,EACvB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAKG;AACD,MAAI,WAAW;AACb,WACE,oBAAC,SAAI,eAAY,sBAAqB,WAAU,0BAC7C,gBAAM,KAAK,EAAE,QAAQ,EAAE,CAAC,EAAE,IAAI,CAAC,GAAG,MACjC,oBAAC,SAAY,WAAU,iEAAb,CAA2E,CACtF,GACH;AAAA,EAEJ;AAEA,MAAI,MAAM,WAAW,GAAG;AACtB,WACE,oBAAC,OAAE,WAAU,iCAAgC,+EAE7C;AAAA,EAEJ;AAEA,SACE,oBAAC,SAAI,WAAU,0BACZ,gBAAM,IAAI,CAAC,SAAS;AACnB,UAAM,WAAW,aAAa,KAAK,KAAK;AACxC,WACE;AAAA,MAAC;AAAA;AAAA,QAEC,MAAM,KAAK;AAAA,QACX,OAAO,KAAK;AAAA,QACZ,SAAS;AAAA,QACT,WAAU;AAAA,QAET,qBACC;AAAA,UAAC;AAAA;AAAA,YACC,KAAK;AAAA,YACL,KAAK,KAAK;AAAA,YACV,MAAI;AAAA,YACJ,aAAW;AAAA,YACX,WAAU;AAAA;AAAA,QACZ,IAEA,oBAAC,SAAI,WAAU,yCACb,8BAAC,qBAAkB,GACrB;AAAA;AAAA,MAjBG,KAAK;AAAA,IAmBZ;AAAA,EAEJ,CAAC,GACH;AAEJ;AAEA,SAAS,oBAAoB;AAC3B,SACE,qBAAC,SAAI,OAAM,MAAK,QAAO,MAAK,SAAQ,aAAY,MAAK,QAAO,QAAO,gBAAe,aAAY,OAAM,WAAU,yBAAwB,eAAc,SAAQ,gBAAe,SACzK;AAAA,wBAAC,UAAK,GAAE,KAAI,GAAE,KAAI,OAAM,MAAK,QAAO,MAAK,IAAG,KAAI;AAAA,IAChD,oBAAC,YAAO,IAAG,KAAI,IAAG,KAAI,GAAE,OAAM,MAAK,gBAAe,QAAO,QAAO;AAAA,IAChE,oBAAC,UAAK,GAAE,mBAAkB;AAAA,KAC5B;AAEJ;","names":[]}
@@ -87,7 +87,7 @@ function RemixableCard({ token }) {
87
87
  src: imageUrl,
88
88
  alt: name,
89
89
  fill: true,
90
- className: "object-cover",
90
+ className: "object-cover card-media",
91
91
  onError: () => setImgError(true),
92
92
  unoptimized: true
93
93
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/components/remix-browse-page.tsx"],"sourcesContent":["\"use client\";\n\nimport { useEffect, useState } from \"react\";\nimport Link from \"./link.js\";\nimport Image from \"./image.js\";\nimport { ServiceHeader } from \"./service-header.js\";\nimport { BackButton } from \"./back-button.js\";\nimport { FadeIn, Stagger, StaggerItem } from \"./motion-primitives.js\";\nimport { Skeleton } from \"./skeleton.js\";\nimport { Button } from \"./button.js\";\nimport { useMedialaneClient } from \"../utils/use-medialane-client.js\";\nimport { useImageUrl } from \"../utils/use-image-url.js\";\nimport type { ApiToken } from \"@medialane/sdk\";\nimport { GitBranch, ImageIcon, Inbox } from \"lucide-react\";\n\nconst PAGE_SIZE = 24;\n\nfunction useRemixableTokens() {\n const client = useMedialaneClient();\n const [tokens, setTokens] = useState<ApiToken[]>([]);\n const [page, setPage] = useState(1);\n const [isLoading, setIsLoading] = useState(true);\n const [hasMore, setHasMore] = useState(false);\n const [error, setError] = useState(false);\n\n useEffect(() => {\n let cancelled = false;\n setIsLoading(true);\n client.api.tokens.list({ derivatives: \"allowed\", limit: PAGE_SIZE, page })\n .then((json) => {\n if (cancelled) return;\n setTokens((prev) => (page === 1 ? json.data : [...prev, ...json.data]));\n setHasMore(page * PAGE_SIZE < (json.meta?.total ?? 0));\n })\n .catch(() => { if (!cancelled) setError(true); })\n .finally(() => { if (!cancelled) setIsLoading(false); });\n return () => { cancelled = true; };\n }, [client, page]);\n\n return { tokens, isLoading, hasMore, error, loadMore: () => setPage((p) => p + 1) };\n}\n\nfunction RemixableCard({ token }: { token: ApiToken }) {\n const [imgError, setImgError] = useState(false);\n const imageUrl = useImageUrl()(token.metadata?.image, { width: 480 });\n const showImage = imageUrl && !imgError;\n const name = token.metadata?.name ?? `#${token.tokenId}`;\n\n return (\n <Link\n href={`/create/remix/${token.contractAddress}/${token.tokenId}`}\n className=\"group flex flex-col rounded-2xl border border-border/60 bg-card overflow-hidden transition-colors sm:hover:border-brand-blue/50 active:scale-[0.99] motion-reduce:transform-none\"\n >\n <div className=\"relative aspect-square bg-muted overflow-hidden\">\n {showImage ? (\n <Image\n src={imageUrl}\n alt={name}\n fill\n className=\"object-cover\"\n onError={() => setImgError(true)}\n unoptimized\n />\n ) : (\n <div className=\"absolute inset-0 flex items-center justify-center\">\n <ImageIcon className=\"h-8 w-8 text-muted-foreground/30\" />\n </div>\n )}\n </div>\n <div className=\"p-4 space-y-1\">\n <p className=\"font-semibold text-sm leading-tight truncate\">{name}</p>\n <p className=\"text-xs text-muted-foreground\">\n Derivatives: Allowed\n </p>\n <p className=\"text-xs font-semibold pt-1 inline-flex items-center gap-1.5\">\n <GitBranch className=\"h-3 w-3\" />\n Remix this work\n </p>\n </div>\n </Link>\n );\n}\n\nfunction RemixableCardSkeleton() {\n return (\n <div className=\"rounded-2xl border border-border/40 overflow-hidden\">\n <Skeleton className=\"aspect-square rounded-none\" />\n <div className=\"p-4 space-y-2\">\n <Skeleton className=\"h-4 w-32\" />\n <Skeleton className=\"h-3 w-24\" />\n </div>\n </div>\n );\n}\n\nexport function RemixBrowsePage() {\n const { tokens, isLoading, hasMore, error, loadMore } = useRemixableTokens();\n\n return (\n <div className=\"pb-16\">\n <section className=\"px-4 pt-10 max-w-5xl mx-auto\">\n <BackButton />\n <FadeIn>\n <div className=\"mt-6\">\n <ServiceHeader\n plain\n icon={<GitBranch className=\"h-4 w-4 text-white\" />}\n title=\"Remix\"\n subtitle=\"Pick a work that is open to remixing. Attribution and royalties flow back to the original creator.\"\n />\n </div>\n </FadeIn>\n </section>\n\n <section className=\"px-4 pt-8 max-w-5xl mx-auto space-y-6\">\n {isLoading && tokens.length === 0 ? (\n <div className=\"grid grid-cols-2 sm:grid-cols-3 lg:grid-cols-4 gap-4\">\n {Array.from({ length: 8 }).map((_, i) => (\n <RemixableCardSkeleton key={i} />\n ))}\n </div>\n ) : error && tokens.length === 0 ? (\n <div className=\"bento-cell p-10 flex flex-col items-center gap-3 text-center\">\n <p className=\"font-semibold\">Could not load works</p>\n <p className=\"text-sm text-muted-foreground\">Check your connection and try again.</p>\n <Button variant=\"outline\" size=\"sm\" onClick={() => location.reload()}>Retry</Button>\n </div>\n ) : tokens.length === 0 ? (\n <FadeIn>\n <div className=\"bento-cell p-10 flex flex-col items-center gap-4 text-center\">\n <Inbox className=\"h-10 w-10 text-muted-foreground/30\" />\n <div>\n <p className=\"font-semibold\">No works open to remixing yet</p>\n <p className=\"text-sm text-muted-foreground mt-1 max-w-sm\">\n Works appear here when their creator sets the license to allow derivatives.\n </p>\n </div>\n </div>\n </FadeIn>\n ) : (\n <>\n <Stagger className=\"grid grid-cols-2 sm:grid-cols-3 lg:grid-cols-4 gap-4\">\n {tokens.map((t) => (\n <StaggerItem key={`${t.contractAddress}-${t.tokenId}`}>\n <RemixableCard token={t} />\n </StaggerItem>\n ))}\n </Stagger>\n {hasMore && (\n <div className=\"flex justify-center\">\n <Button variant=\"outline\" onClick={loadMore} disabled={isLoading}>\n {isLoading ? \"Loading…\" : \"Load more\"}\n </Button>\n </div>\n )}\n </>\n )}\n </section>\n </div>\n );\n}\n"],"mappings":"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","names":["Link","Image"]}
1
+ {"version":3,"sources":["../../src/components/remix-browse-page.tsx"],"sourcesContent":["\"use client\";\n\nimport { useEffect, useState } from \"react\";\nimport Link from \"./link.js\";\nimport Image from \"./image.js\";\nimport { ServiceHeader } from \"./service-header.js\";\nimport { BackButton } from \"./back-button.js\";\nimport { FadeIn, Stagger, StaggerItem } from \"./motion-primitives.js\";\nimport { Skeleton } from \"./skeleton.js\";\nimport { Button } from \"./button.js\";\nimport { useMedialaneClient } from \"../utils/use-medialane-client.js\";\nimport { useImageUrl } from \"../utils/use-image-url.js\";\nimport type { ApiToken } from \"@medialane/sdk\";\nimport { GitBranch, ImageIcon, Inbox } from \"lucide-react\";\n\nconst PAGE_SIZE = 24;\n\nfunction useRemixableTokens() {\n const client = useMedialaneClient();\n const [tokens, setTokens] = useState<ApiToken[]>([]);\n const [page, setPage] = useState(1);\n const [isLoading, setIsLoading] = useState(true);\n const [hasMore, setHasMore] = useState(false);\n const [error, setError] = useState(false);\n\n useEffect(() => {\n let cancelled = false;\n setIsLoading(true);\n client.api.tokens.list({ derivatives: \"allowed\", limit: PAGE_SIZE, page })\n .then((json) => {\n if (cancelled) return;\n setTokens((prev) => (page === 1 ? json.data : [...prev, ...json.data]));\n setHasMore(page * PAGE_SIZE < (json.meta?.total ?? 0));\n })\n .catch(() => { if (!cancelled) setError(true); })\n .finally(() => { if (!cancelled) setIsLoading(false); });\n return () => { cancelled = true; };\n }, [client, page]);\n\n return { tokens, isLoading, hasMore, error, loadMore: () => setPage((p) => p + 1) };\n}\n\nfunction RemixableCard({ token }: { token: ApiToken }) {\n const [imgError, setImgError] = useState(false);\n const imageUrl = useImageUrl()(token.metadata?.image, { width: 480 });\n const showImage = imageUrl && !imgError;\n const name = token.metadata?.name ?? `#${token.tokenId}`;\n\n return (\n <Link\n href={`/create/remix/${token.contractAddress}/${token.tokenId}`}\n className=\"group flex flex-col rounded-2xl border border-border/60 bg-card overflow-hidden transition-colors sm:hover:border-brand-blue/50 active:scale-[0.99] motion-reduce:transform-none\"\n >\n <div className=\"relative aspect-square bg-muted overflow-hidden\">\n {showImage ? (\n <Image\n src={imageUrl}\n alt={name}\n fill\n className=\"object-cover card-media\"\n onError={() => setImgError(true)}\n unoptimized\n />\n ) : (\n <div className=\"absolute inset-0 flex items-center justify-center\">\n <ImageIcon className=\"h-8 w-8 text-muted-foreground/30\" />\n </div>\n )}\n </div>\n <div className=\"p-4 space-y-1\">\n <p className=\"font-semibold text-sm leading-tight truncate\">{name}</p>\n <p className=\"text-xs text-muted-foreground\">\n Derivatives: Allowed\n </p>\n <p className=\"text-xs font-semibold pt-1 inline-flex items-center gap-1.5\">\n <GitBranch className=\"h-3 w-3\" />\n Remix this work\n </p>\n </div>\n </Link>\n );\n}\n\nfunction RemixableCardSkeleton() {\n return (\n <div className=\"rounded-2xl border border-border/40 overflow-hidden\">\n <Skeleton className=\"aspect-square rounded-none\" />\n <div className=\"p-4 space-y-2\">\n <Skeleton className=\"h-4 w-32\" />\n <Skeleton className=\"h-3 w-24\" />\n </div>\n </div>\n );\n}\n\nexport function RemixBrowsePage() {\n const { tokens, isLoading, hasMore, error, loadMore } = useRemixableTokens();\n\n return (\n <div className=\"pb-16\">\n <section className=\"px-4 pt-10 max-w-5xl mx-auto\">\n <BackButton />\n <FadeIn>\n <div className=\"mt-6\">\n <ServiceHeader\n plain\n icon={<GitBranch className=\"h-4 w-4 text-white\" />}\n title=\"Remix\"\n subtitle=\"Pick a work that is open to remixing. Attribution and royalties flow back to the original creator.\"\n />\n </div>\n </FadeIn>\n </section>\n\n <section className=\"px-4 pt-8 max-w-5xl mx-auto space-y-6\">\n {isLoading && tokens.length === 0 ? (\n <div className=\"grid grid-cols-2 sm:grid-cols-3 lg:grid-cols-4 gap-4\">\n {Array.from({ length: 8 }).map((_, i) => (\n <RemixableCardSkeleton key={i} />\n ))}\n </div>\n ) : error && tokens.length === 0 ? (\n <div className=\"bento-cell p-10 flex flex-col items-center gap-3 text-center\">\n <p className=\"font-semibold\">Could not load works</p>\n <p className=\"text-sm text-muted-foreground\">Check your connection and try again.</p>\n <Button variant=\"outline\" size=\"sm\" onClick={() => location.reload()}>Retry</Button>\n </div>\n ) : tokens.length === 0 ? (\n <FadeIn>\n <div className=\"bento-cell p-10 flex flex-col items-center gap-4 text-center\">\n <Inbox className=\"h-10 w-10 text-muted-foreground/30\" />\n <div>\n <p className=\"font-semibold\">No works open to remixing yet</p>\n <p className=\"text-sm text-muted-foreground mt-1 max-w-sm\">\n Works appear here when their creator sets the license to allow derivatives.\n </p>\n </div>\n </div>\n </FadeIn>\n ) : (\n <>\n <Stagger className=\"grid grid-cols-2 sm:grid-cols-3 lg:grid-cols-4 gap-4\">\n {tokens.map((t) => (\n <StaggerItem key={`${t.contractAddress}-${t.tokenId}`}>\n <RemixableCard token={t} />\n </StaggerItem>\n ))}\n </Stagger>\n {hasMore && (\n <div className=\"flex justify-center\">\n <Button variant=\"outline\" onClick={loadMore} disabled={isLoading}>\n {isLoading ? \"Loading…\" : \"Load more\"}\n </Button>\n </div>\n )}\n </>\n )}\n </section>\n </div>\n );\n}\n"],"mappings":"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","names":["Link","Image"]}
@@ -54,7 +54,7 @@ function RemixableCard({ token }) {
54
54
  src: imageUrl,
55
55
  alt: name,
56
56
  fill: true,
57
- className: "object-cover",
57
+ className: "object-cover card-media",
58
58
  onError: () => setImgError(true),
59
59
  unoptimized: true
60
60
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/components/remix-browse-page.tsx"],"sourcesContent":["\"use client\";\n\nimport { useEffect, useState } from \"react\";\nimport Link from \"./link.js\";\nimport Image from \"./image.js\";\nimport { ServiceHeader } from \"./service-header.js\";\nimport { BackButton } from \"./back-button.js\";\nimport { FadeIn, Stagger, StaggerItem } from \"./motion-primitives.js\";\nimport { Skeleton } from \"./skeleton.js\";\nimport { Button } from \"./button.js\";\nimport { useMedialaneClient } from \"../utils/use-medialane-client.js\";\nimport { useImageUrl } from \"../utils/use-image-url.js\";\nimport type { ApiToken } from \"@medialane/sdk\";\nimport { GitBranch, ImageIcon, Inbox } from \"lucide-react\";\n\nconst PAGE_SIZE = 24;\n\nfunction useRemixableTokens() {\n const client = useMedialaneClient();\n const [tokens, setTokens] = useState<ApiToken[]>([]);\n const [page, setPage] = useState(1);\n const [isLoading, setIsLoading] = useState(true);\n const [hasMore, setHasMore] = useState(false);\n const [error, setError] = useState(false);\n\n useEffect(() => {\n let cancelled = false;\n setIsLoading(true);\n client.api.tokens.list({ derivatives: \"allowed\", limit: PAGE_SIZE, page })\n .then((json) => {\n if (cancelled) return;\n setTokens((prev) => (page === 1 ? json.data : [...prev, ...json.data]));\n setHasMore(page * PAGE_SIZE < (json.meta?.total ?? 0));\n })\n .catch(() => { if (!cancelled) setError(true); })\n .finally(() => { if (!cancelled) setIsLoading(false); });\n return () => { cancelled = true; };\n }, [client, page]);\n\n return { tokens, isLoading, hasMore, error, loadMore: () => setPage((p) => p + 1) };\n}\n\nfunction RemixableCard({ token }: { token: ApiToken }) {\n const [imgError, setImgError] = useState(false);\n const imageUrl = useImageUrl()(token.metadata?.image, { width: 480 });\n const showImage = imageUrl && !imgError;\n const name = token.metadata?.name ?? `#${token.tokenId}`;\n\n return (\n <Link\n href={`/create/remix/${token.contractAddress}/${token.tokenId}`}\n className=\"group flex flex-col rounded-2xl border border-border/60 bg-card overflow-hidden transition-colors sm:hover:border-brand-blue/50 active:scale-[0.99] motion-reduce:transform-none\"\n >\n <div className=\"relative aspect-square bg-muted overflow-hidden\">\n {showImage ? (\n <Image\n src={imageUrl}\n alt={name}\n fill\n className=\"object-cover\"\n onError={() => setImgError(true)}\n unoptimized\n />\n ) : (\n <div className=\"absolute inset-0 flex items-center justify-center\">\n <ImageIcon className=\"h-8 w-8 text-muted-foreground/30\" />\n </div>\n )}\n </div>\n <div className=\"p-4 space-y-1\">\n <p className=\"font-semibold text-sm leading-tight truncate\">{name}</p>\n <p className=\"text-xs text-muted-foreground\">\n Derivatives: Allowed\n </p>\n <p className=\"text-xs font-semibold pt-1 inline-flex items-center gap-1.5\">\n <GitBranch className=\"h-3 w-3\" />\n Remix this work\n </p>\n </div>\n </Link>\n );\n}\n\nfunction RemixableCardSkeleton() {\n return (\n <div className=\"rounded-2xl border border-border/40 overflow-hidden\">\n <Skeleton className=\"aspect-square rounded-none\" />\n <div className=\"p-4 space-y-2\">\n <Skeleton className=\"h-4 w-32\" />\n <Skeleton className=\"h-3 w-24\" />\n </div>\n </div>\n );\n}\n\nexport function RemixBrowsePage() {\n const { tokens, isLoading, hasMore, error, loadMore } = useRemixableTokens();\n\n return (\n <div className=\"pb-16\">\n <section className=\"px-4 pt-10 max-w-5xl mx-auto\">\n <BackButton />\n <FadeIn>\n <div className=\"mt-6\">\n <ServiceHeader\n plain\n icon={<GitBranch className=\"h-4 w-4 text-white\" />}\n title=\"Remix\"\n subtitle=\"Pick a work that is open to remixing. Attribution and royalties flow back to the original creator.\"\n />\n </div>\n </FadeIn>\n </section>\n\n <section className=\"px-4 pt-8 max-w-5xl mx-auto space-y-6\">\n {isLoading && tokens.length === 0 ? (\n <div className=\"grid grid-cols-2 sm:grid-cols-3 lg:grid-cols-4 gap-4\">\n {Array.from({ length: 8 }).map((_, i) => (\n <RemixableCardSkeleton key={i} />\n ))}\n </div>\n ) : error && tokens.length === 0 ? (\n <div className=\"bento-cell p-10 flex flex-col items-center gap-3 text-center\">\n <p className=\"font-semibold\">Could not load works</p>\n <p className=\"text-sm text-muted-foreground\">Check your connection and try again.</p>\n <Button variant=\"outline\" size=\"sm\" onClick={() => location.reload()}>Retry</Button>\n </div>\n ) : tokens.length === 0 ? (\n <FadeIn>\n <div className=\"bento-cell p-10 flex flex-col items-center gap-4 text-center\">\n <Inbox className=\"h-10 w-10 text-muted-foreground/30\" />\n <div>\n <p className=\"font-semibold\">No works open to remixing yet</p>\n <p className=\"text-sm text-muted-foreground mt-1 max-w-sm\">\n Works appear here when their creator sets the license to allow derivatives.\n </p>\n </div>\n </div>\n </FadeIn>\n ) : (\n <>\n <Stagger className=\"grid grid-cols-2 sm:grid-cols-3 lg:grid-cols-4 gap-4\">\n {tokens.map((t) => (\n <StaggerItem key={`${t.contractAddress}-${t.tokenId}`}>\n <RemixableCard token={t} />\n </StaggerItem>\n ))}\n </Stagger>\n {hasMore && (\n <div className=\"flex justify-center\">\n <Button variant=\"outline\" onClick={loadMore} disabled={isLoading}>\n {isLoading ? \"Loading…\" : \"Load more\"}\n </Button>\n </div>\n )}\n </>\n )}\n </section>\n </div>\n );\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../src/components/remix-browse-page.tsx"],"sourcesContent":["\"use client\";\n\nimport { useEffect, useState } from \"react\";\nimport Link from \"./link.js\";\nimport Image from \"./image.js\";\nimport { ServiceHeader } from \"./service-header.js\";\nimport { BackButton } from \"./back-button.js\";\nimport { FadeIn, Stagger, StaggerItem } from \"./motion-primitives.js\";\nimport { Skeleton } from \"./skeleton.js\";\nimport { Button } from \"./button.js\";\nimport { useMedialaneClient } from \"../utils/use-medialane-client.js\";\nimport { useImageUrl } from \"../utils/use-image-url.js\";\nimport type { ApiToken } from \"@medialane/sdk\";\nimport { GitBranch, ImageIcon, Inbox } from \"lucide-react\";\n\nconst PAGE_SIZE = 24;\n\nfunction useRemixableTokens() {\n const client = useMedialaneClient();\n const [tokens, setTokens] = useState<ApiToken[]>([]);\n const [page, setPage] = useState(1);\n const [isLoading, setIsLoading] = useState(true);\n const [hasMore, setHasMore] = useState(false);\n const [error, setError] = useState(false);\n\n useEffect(() => {\n let cancelled = false;\n setIsLoading(true);\n client.api.tokens.list({ derivatives: \"allowed\", limit: PAGE_SIZE, page })\n .then((json) => {\n if (cancelled) return;\n setTokens((prev) => (page === 1 ? json.data : [...prev, ...json.data]));\n setHasMore(page * PAGE_SIZE < (json.meta?.total ?? 0));\n })\n .catch(() => { if (!cancelled) setError(true); })\n .finally(() => { if (!cancelled) setIsLoading(false); });\n return () => { cancelled = true; };\n }, [client, page]);\n\n return { tokens, isLoading, hasMore, error, loadMore: () => setPage((p) => p + 1) };\n}\n\nfunction RemixableCard({ token }: { token: ApiToken }) {\n const [imgError, setImgError] = useState(false);\n const imageUrl = useImageUrl()(token.metadata?.image, { width: 480 });\n const showImage = imageUrl && !imgError;\n const name = token.metadata?.name ?? `#${token.tokenId}`;\n\n return (\n <Link\n href={`/create/remix/${token.contractAddress}/${token.tokenId}`}\n className=\"group flex flex-col rounded-2xl border border-border/60 bg-card overflow-hidden transition-colors sm:hover:border-brand-blue/50 active:scale-[0.99] motion-reduce:transform-none\"\n >\n <div className=\"relative aspect-square bg-muted overflow-hidden\">\n {showImage ? (\n <Image\n src={imageUrl}\n alt={name}\n fill\n className=\"object-cover card-media\"\n onError={() => setImgError(true)}\n unoptimized\n />\n ) : (\n <div className=\"absolute inset-0 flex items-center justify-center\">\n <ImageIcon className=\"h-8 w-8 text-muted-foreground/30\" />\n </div>\n )}\n </div>\n <div className=\"p-4 space-y-1\">\n <p className=\"font-semibold text-sm leading-tight truncate\">{name}</p>\n <p className=\"text-xs text-muted-foreground\">\n Derivatives: Allowed\n </p>\n <p className=\"text-xs font-semibold pt-1 inline-flex items-center gap-1.5\">\n <GitBranch className=\"h-3 w-3\" />\n Remix this work\n </p>\n </div>\n </Link>\n );\n}\n\nfunction RemixableCardSkeleton() {\n return (\n <div className=\"rounded-2xl border border-border/40 overflow-hidden\">\n <Skeleton className=\"aspect-square rounded-none\" />\n <div className=\"p-4 space-y-2\">\n <Skeleton className=\"h-4 w-32\" />\n <Skeleton className=\"h-3 w-24\" />\n </div>\n </div>\n );\n}\n\nexport function RemixBrowsePage() {\n const { tokens, isLoading, hasMore, error, loadMore } = useRemixableTokens();\n\n return (\n <div className=\"pb-16\">\n <section className=\"px-4 pt-10 max-w-5xl mx-auto\">\n <BackButton />\n <FadeIn>\n <div className=\"mt-6\">\n <ServiceHeader\n plain\n icon={<GitBranch className=\"h-4 w-4 text-white\" />}\n title=\"Remix\"\n subtitle=\"Pick a work that is open to remixing. Attribution and royalties flow back to the original creator.\"\n />\n </div>\n </FadeIn>\n </section>\n\n <section className=\"px-4 pt-8 max-w-5xl mx-auto space-y-6\">\n {isLoading && tokens.length === 0 ? (\n <div className=\"grid grid-cols-2 sm:grid-cols-3 lg:grid-cols-4 gap-4\">\n {Array.from({ length: 8 }).map((_, i) => (\n <RemixableCardSkeleton key={i} />\n ))}\n </div>\n ) : error && tokens.length === 0 ? (\n <div className=\"bento-cell p-10 flex flex-col items-center gap-3 text-center\">\n <p className=\"font-semibold\">Could not load works</p>\n <p className=\"text-sm text-muted-foreground\">Check your connection and try again.</p>\n <Button variant=\"outline\" size=\"sm\" onClick={() => location.reload()}>Retry</Button>\n </div>\n ) : tokens.length === 0 ? (\n <FadeIn>\n <div className=\"bento-cell p-10 flex flex-col items-center gap-4 text-center\">\n <Inbox className=\"h-10 w-10 text-muted-foreground/30\" />\n <div>\n <p className=\"font-semibold\">No works open to remixing yet</p>\n <p className=\"text-sm text-muted-foreground mt-1 max-w-sm\">\n Works appear here when their creator sets the license to allow derivatives.\n </p>\n </div>\n </div>\n </FadeIn>\n ) : (\n <>\n <Stagger className=\"grid grid-cols-2 sm:grid-cols-3 lg:grid-cols-4 gap-4\">\n {tokens.map((t) => (\n <StaggerItem key={`${t.contractAddress}-${t.tokenId}`}>\n <RemixableCard token={t} />\n </StaggerItem>\n ))}\n </Stagger>\n {hasMore && (\n <div className=\"flex justify-center\">\n <Button variant=\"outline\" onClick={loadMore} disabled={isLoading}>\n {isLoading ? \"Loading…\" : \"Load more\"}\n </Button>\n </div>\n )}\n </>\n )}\n </section>\n </div>\n );\n}\n"],"mappings":"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","names":[]}
@@ -85,7 +85,7 @@ function TokenCard({
85
85
  e.stopPropagation();
86
86
  onOffer?.(token);
87
87
  };
88
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "card-base relative overflow-hidden flex flex-col w-full", children: [
88
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "card-base group relative overflow-hidden flex flex-col w-full", children: [
89
89
  ipType && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "absolute top-2 right-2 z-10", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_ip_type_badge.IpTypeBadge, { ipType, size: "sm" }) }),
90
90
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_link.default, { href: assetHref, className: "flex flex-col flex-1", children: [
91
91
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "relative aspect-square bg-muted overflow-hidden shrink-0", children: [
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/components/token-card.tsx"],"sourcesContent":["\"use client\";\n\nimport Link from \"./link.js\";\nimport {\n Loader2, ArrowUpRight, MoreHorizontal, Zap, HandCoins, Tag,\n Layers, GitBranch, Flag, UserCircle2, ArrowRightLeft, XCircle,\n} from \"lucide-react\";\nimport {\n DropdownMenu, DropdownMenuContent, DropdownMenuItem,\n DropdownMenuSeparator, DropdownMenuTrigger,\n} from \"./dropdown-menu.js\";\nimport { cn } from \"../utils/cn.js\";\nimport { formatDisplayPrice } from \"../utils/format.js\";\nimport { CurrencyIcon } from \"./currency-icon.js\";\nimport { PriceChipContent } from \"./dual-price.js\";\nimport { IpTypeBadge } from \"./ip-type-badge.js\";\nimport { AnimatedTokenMedia } from \"./animated-token-media.js\";\nimport { isLivingRenderCollection } from \"../data/living-render-collections.js\";\nimport { assetHref as buildAssetHref, collectionHref as buildCollectionHref } from \"@medialane/sdk\";\nimport type { ApiToken, Chain } from \"@medialane/sdk\";\nimport { tokenProfileLink } from \"../utils/token-profile-link.js\";\nimport { useImageUrl } from \"../utils/use-image-url.js\";\n\n\nconst BTN_BASE = \"h-8 rounded-[9px] flex items-center justify-center gap-1.5 text-xs font-semibold transition-all active:scale-[0.98] shadow-none border-0\";\nconst BTN_OUTLINE = cn(BTN_BASE, \"border border-border/60 text-foreground hover:bg-muted/60\");\n\nexport interface TokenCardProps {\n token: ApiToken;\n isOwner?: boolean;\n onList?: (token: ApiToken) => void;\n onTransfer?: (token: ApiToken) => void;\n onCancel?: (token: ApiToken) => void;\n onBuy?: (token: ApiToken) => void;\n onOffer?: (token: ApiToken) => void;\n onReport?: (token: ApiToken) => void;\n\n usdValue?: string | null;\n}\n\nexport function TokenCard({\n token,\n isOwner = false,\n onList,\n onTransfer,\n onCancel,\n onBuy,\n onOffer,\n onReport,\n usdValue,\n}: TokenCardProps) {\n const resolveImage = useImageUrl();\n const name = token.metadata?.name || `Token #${token.tokenId}`;\n const image = resolveImage(token.metadata?.image, { width: 480 });\n const animationUrl = token.metadata?.animationUrl ?? null;\n const ipType = token.metadata?.ipType;\n\n const listingOrder = token.activeOrders?.find(\n (o) => o.offer.itemType === \"ERC721\" || o.offer.itemType === \"ERC1155\"\n );\n\n const profileLink = tokenProfileLink(token);\n const hasPrimaryAction = isOwner ? !listingOrder : listingOrder ? !!onBuy : !!onOffer;\n\n const chain = token.chain.toUpperCase() as Chain;\n const live = isLivingRenderCollection(chain, token.contractAddress);\n const assetHref = buildAssetHref(chain, token.contractAddress, token.tokenId);\n const collectionHref = buildCollectionHref(chain, token.contractAddress);\n const remixHref = `/create/remix/${token.contractAddress}/${token.tokenId}`;\n\n const handleBuy = (e: React.MouseEvent) => {\n e.preventDefault();\n e.stopPropagation();\n onBuy?.(token);\n };\n\n const handleOffer = (e: React.MouseEvent) => {\n e.preventDefault();\n e.stopPropagation();\n onOffer?.(token);\n };\n\n return (\n <div className=\"card-base relative overflow-hidden flex flex-col w-full\">\n {ipType && (\n <div className=\"absolute top-2 right-2 z-10\">\n <IpTypeBadge ipType={ipType as any} size=\"sm\" />\n </div>\n )}\n\n <Link href={assetHref} className=\"flex flex-col flex-1\">\n\n <div className=\"relative aspect-square bg-muted overflow-hidden shrink-0\">\n <AnimatedTokenMedia\n image={image}\n animationUrl={animationUrl}\n live={live}\n alt={name}\n mode=\"fill\"\n sizes=\"(max-width: 640px) 50vw, (max-width: 1024px) 33vw, 22vw\"\n className=\"object-cover\"\n fallback={\n <div className=\"absolute inset-0 bg-gradient-to-br from-brand-purple/15 to-brand-blue/15\" />\n }\n />\n\n {listingOrder && (\n <div className=\"absolute bottom-2 right-2 z-10\">\n <span className=\"inline-flex items-center gap-1.5 px-2.5 py-1.5 rounded-full text-sm font-bold bg-background/90 backdrop-blur-sm border border-border/40 shadow-sm tabular-nums\">\n <PriceChipContent\n amountFormatted={listingOrder.price.formatted}\n currency={listingOrder.price.currency}\n usdValue={usdValue}\n tone=\"card\"\n />\n </span>\n </div>\n )}\n\n {(token.metadataStatus === \"PENDING\" || token.metadataStatus === \"FETCHING\") && (\n <div className=\"absolute bottom-0 inset-x-0 flex items-center justify-center gap-1.5 bg-black/50 backdrop-blur-sm py-1.5\">\n <Loader2 className=\"h-3 w-3 animate-spin text-white/70\" />\n <span className=\"text-2xs text-white/70\">Indexing…</span>\n </div>\n )}\n </div>\n\n <div className=\"px-3 pt-2.5 pb-4 flex-1\">\n <p className=\"text-base font-bold line-clamp-2 leading-snug\">{name}</p>\n </div>\n </Link>\n\n <div className=\"px-3 pb-3 flex items-center gap-1.5\">\n\n <Link href={assetHref} className={cn(BTN_OUTLINE, \"px-2.5\", hasPrimaryAction ? \"shrink-0\" : \"flex-1\")}>\n <ArrowUpRight className=\"h-3.5 w-3.5\" />\n View\n </Link>\n\n {isOwner ? (\n !listingOrder ? (\n <button\n className={cn(BTN_OUTLINE, \"flex-1 text-brand-purple border-brand-purple/30 hover:bg-brand-purple/10 hover:text-brand-purple\")}\n onClick={(e) => { e.preventDefault(); e.stopPropagation(); onList?.(token); }}\n >\n <Tag className=\"h-3.5 w-3.5 mr-1.5\" />\n List\n </button>\n ) : null\n ) : listingOrder && onBuy ? (\n <div className=\"btn-border-animated p-[1.5px] rounded-[10px] flex-1 h-8\">\n <button\n className=\"w-full h-full rounded-[9px] bg-background flex items-center justify-center gap-1.5 text-xs font-semibold text-foreground hover:bg-muted/60 transition-all active:scale-[0.98]\"\n onClick={handleBuy}\n >\n <Zap className=\"h-3.5 w-3.5 shrink-0\" />\n Buy\n </button>\n </div>\n ) : !listingOrder && onOffer ? (\n <button\n className={cn(BTN_OUTLINE, \"flex-1 text-brand-orange border-brand-orange/30 hover:bg-brand-orange/10 hover:text-brand-orange\")}\n onClick={handleOffer}\n >\n <HandCoins className=\"h-3.5 w-3.5 mr-1.5\" />\n Offer\n </button>\n ) : null}\n\n <DropdownMenu>\n <DropdownMenuTrigger asChild>\n <button\n className={cn(BTN_OUTLINE, \"w-8 p-0 shrink-0\")}\n aria-label=\"More actions\"\n >\n <MoreHorizontal className=\"h-3.5 w-3.5\" />\n </button>\n </DropdownMenuTrigger>\n <DropdownMenuContent align=\"end\" className=\"w-52\">\n\n <DropdownMenuItem asChild>\n <Link href={assetHref} className=\"flex items-center gap-2\">\n <ArrowUpRight className=\"h-3.5 w-3.5 text-muted-foreground\" />\n View asset\n </Link>\n </DropdownMenuItem>\n\n <DropdownMenuSeparator />\n\n {isOwner ? (\n <>\n {!listingOrder && onList && (\n <DropdownMenuItem\n className=\"flex items-center gap-2 text-brand-purple focus:text-brand-purple\"\n onClick={() => onList(token)}\n >\n <Tag className=\"h-3.5 w-3.5\" />\n List\n </DropdownMenuItem>\n )}\n {onTransfer && (\n <DropdownMenuItem\n className=\"flex items-center gap-2\"\n onClick={() => onTransfer(token)}\n >\n <ArrowRightLeft className=\"h-3.5 w-3.5 text-muted-foreground\" />\n Transfer\n </DropdownMenuItem>\n )}\n {listingOrder && onCancel && (\n <DropdownMenuItem\n className=\"flex items-center gap-2 text-brand-orange focus:text-brand-orange\"\n onClick={() => onCancel(token)}\n >\n <XCircle className=\"h-3.5 w-3.5\" />\n Cancel listing\n </DropdownMenuItem>\n )}\n </>\n ) : (\n <>\n {listingOrder && onBuy && (\n <DropdownMenuItem\n className=\"flex items-center gap-2 text-brand-blue focus:text-brand-blue\"\n onClick={handleBuy}\n >\n <Zap className=\"h-3.5 w-3.5\" />\n <span className=\"flex items-center gap-1\">\n Buy —\n <CurrencyIcon symbol={listingOrder.price.currency} size={12} />\n {formatDisplayPrice(listingOrder.price.formatted)}\n </span>\n </DropdownMenuItem>\n )}\n {onOffer && (\n <DropdownMenuItem\n className=\"flex items-center gap-2 text-brand-orange focus:text-brand-orange\"\n onClick={handleOffer}\n >\n <HandCoins className=\"h-3.5 w-3.5\" />\n Make offer\n </DropdownMenuItem>\n )}\n </>\n )}\n\n <DropdownMenuItem asChild>\n <Link href={remixHref} className=\"flex items-center gap-2 text-brand-purple focus:text-brand-purple\">\n <GitBranch className=\"h-3.5 w-3.5\" />\n Remix\n </Link>\n </DropdownMenuItem>\n\n <DropdownMenuSeparator />\n\n <DropdownMenuItem asChild>\n <Link href={collectionHref} className=\"flex items-center gap-2\">\n <Layers className=\"h-3.5 w-3.5 text-muted-foreground\" />\n View collection\n </Link>\n </DropdownMenuItem>\n\n {profileLink && (\n <DropdownMenuItem asChild>\n <Link href={profileLink.href} className=\"flex items-center gap-2\">\n <UserCircle2 className=\"h-3.5 w-3.5 text-muted-foreground\" />\n {profileLink.label}\n </Link>\n </DropdownMenuItem>\n )}\n\n {onReport && (\n <>\n <DropdownMenuSeparator />\n <DropdownMenuItem\n className=\"flex items-center gap-2 text-muted-foreground focus:text-muted-foreground\"\n onClick={() => onReport(token)}\n >\n <Flag className=\"h-3.5 w-3.5\" />\n Report\n </DropdownMenuItem>\n </>\n )}\n\n </DropdownMenuContent>\n </DropdownMenu>\n </div>\n </div>\n );\n}\n\nexport function TokenCardSkeleton() {\n return (\n <div className=\"card-base overflow-hidden\">\n <div className=\"aspect-square w-full animate-pulse bg-muted\" />\n <div className=\"px-3 pt-2.5 pb-3 space-y-1.5\">\n <div className=\"h-4 w-3/4 rounded-md animate-pulse bg-muted\" />\n </div>\n <div className=\"px-3 pb-3 flex gap-1.5\">\n <div className=\"h-8 w-8 rounded-[9px] animate-pulse bg-muted shrink-0\" />\n <div className=\"h-8 flex-1 rounded-[9px] animate-pulse bg-muted\" />\n <div className=\"h-8 w-8 rounded-[9px] animate-pulse bg-muted shrink-0\" />\n </div>\n </div>\n );\n}\n"],"mappings":"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","names":["buildAssetHref","buildCollectionHref","Link"]}
1
+ {"version":3,"sources":["../../src/components/token-card.tsx"],"sourcesContent":["\"use client\";\n\nimport Link from \"./link.js\";\nimport {\n Loader2, ArrowUpRight, MoreHorizontal, Zap, HandCoins, Tag,\n Layers, GitBranch, Flag, UserCircle2, ArrowRightLeft, XCircle,\n} from \"lucide-react\";\nimport {\n DropdownMenu, DropdownMenuContent, DropdownMenuItem,\n DropdownMenuSeparator, DropdownMenuTrigger,\n} from \"./dropdown-menu.js\";\nimport { cn } from \"../utils/cn.js\";\nimport { formatDisplayPrice } from \"../utils/format.js\";\nimport { CurrencyIcon } from \"./currency-icon.js\";\nimport { PriceChipContent } from \"./dual-price.js\";\nimport { IpTypeBadge } from \"./ip-type-badge.js\";\nimport { AnimatedTokenMedia } from \"./animated-token-media.js\";\nimport { isLivingRenderCollection } from \"../data/living-render-collections.js\";\nimport { assetHref as buildAssetHref, collectionHref as buildCollectionHref } from \"@medialane/sdk\";\nimport type { ApiToken, Chain } from \"@medialane/sdk\";\nimport { tokenProfileLink } from \"../utils/token-profile-link.js\";\nimport { useImageUrl } from \"../utils/use-image-url.js\";\n\n\nconst BTN_BASE = \"h-8 rounded-[9px] flex items-center justify-center gap-1.5 text-xs font-semibold transition-all active:scale-[0.98] shadow-none border-0\";\nconst BTN_OUTLINE = cn(BTN_BASE, \"border border-border/60 text-foreground hover:bg-muted/60\");\n\nexport interface TokenCardProps {\n token: ApiToken;\n isOwner?: boolean;\n onList?: (token: ApiToken) => void;\n onTransfer?: (token: ApiToken) => void;\n onCancel?: (token: ApiToken) => void;\n onBuy?: (token: ApiToken) => void;\n onOffer?: (token: ApiToken) => void;\n onReport?: (token: ApiToken) => void;\n\n usdValue?: string | null;\n}\n\nexport function TokenCard({\n token,\n isOwner = false,\n onList,\n onTransfer,\n onCancel,\n onBuy,\n onOffer,\n onReport,\n usdValue,\n}: TokenCardProps) {\n const resolveImage = useImageUrl();\n const name = token.metadata?.name || `Token #${token.tokenId}`;\n const image = resolveImage(token.metadata?.image, { width: 480 });\n const animationUrl = token.metadata?.animationUrl ?? null;\n const ipType = token.metadata?.ipType;\n\n const listingOrder = token.activeOrders?.find(\n (o) => o.offer.itemType === \"ERC721\" || o.offer.itemType === \"ERC1155\"\n );\n\n const profileLink = tokenProfileLink(token);\n const hasPrimaryAction = isOwner ? !listingOrder : listingOrder ? !!onBuy : !!onOffer;\n\n const chain = token.chain.toUpperCase() as Chain;\n const live = isLivingRenderCollection(chain, token.contractAddress);\n const assetHref = buildAssetHref(chain, token.contractAddress, token.tokenId);\n const collectionHref = buildCollectionHref(chain, token.contractAddress);\n const remixHref = `/create/remix/${token.contractAddress}/${token.tokenId}`;\n\n const handleBuy = (e: React.MouseEvent) => {\n e.preventDefault();\n e.stopPropagation();\n onBuy?.(token);\n };\n\n const handleOffer = (e: React.MouseEvent) => {\n e.preventDefault();\n e.stopPropagation();\n onOffer?.(token);\n };\n\n return (\n <div className=\"card-base group relative overflow-hidden flex flex-col w-full\">\n {ipType && (\n <div className=\"absolute top-2 right-2 z-10\">\n <IpTypeBadge ipType={ipType as any} size=\"sm\" />\n </div>\n )}\n\n <Link href={assetHref} className=\"flex flex-col flex-1\">\n\n <div className=\"relative aspect-square bg-muted overflow-hidden shrink-0\">\n <AnimatedTokenMedia\n image={image}\n animationUrl={animationUrl}\n live={live}\n alt={name}\n mode=\"fill\"\n sizes=\"(max-width: 640px) 50vw, (max-width: 1024px) 33vw, 22vw\"\n className=\"object-cover\"\n fallback={\n <div className=\"absolute inset-0 bg-gradient-to-br from-brand-purple/15 to-brand-blue/15\" />\n }\n />\n\n {listingOrder && (\n <div className=\"absolute bottom-2 right-2 z-10\">\n <span className=\"inline-flex items-center gap-1.5 px-2.5 py-1.5 rounded-full text-sm font-bold bg-background/90 backdrop-blur-sm border border-border/40 shadow-sm tabular-nums\">\n <PriceChipContent\n amountFormatted={listingOrder.price.formatted}\n currency={listingOrder.price.currency}\n usdValue={usdValue}\n tone=\"card\"\n />\n </span>\n </div>\n )}\n\n {(token.metadataStatus === \"PENDING\" || token.metadataStatus === \"FETCHING\") && (\n <div className=\"absolute bottom-0 inset-x-0 flex items-center justify-center gap-1.5 bg-black/50 backdrop-blur-sm py-1.5\">\n <Loader2 className=\"h-3 w-3 animate-spin text-white/70\" />\n <span className=\"text-2xs text-white/70\">Indexing…</span>\n </div>\n )}\n </div>\n\n <div className=\"px-3 pt-2.5 pb-4 flex-1\">\n <p className=\"text-base font-bold line-clamp-2 leading-snug\">{name}</p>\n </div>\n </Link>\n\n <div className=\"px-3 pb-3 flex items-center gap-1.5\">\n\n <Link href={assetHref} className={cn(BTN_OUTLINE, \"px-2.5\", hasPrimaryAction ? \"shrink-0\" : \"flex-1\")}>\n <ArrowUpRight className=\"h-3.5 w-3.5\" />\n View\n </Link>\n\n {isOwner ? (\n !listingOrder ? (\n <button\n className={cn(BTN_OUTLINE, \"flex-1 text-brand-purple border-brand-purple/30 hover:bg-brand-purple/10 hover:text-brand-purple\")}\n onClick={(e) => { e.preventDefault(); e.stopPropagation(); onList?.(token); }}\n >\n <Tag className=\"h-3.5 w-3.5 mr-1.5\" />\n List\n </button>\n ) : null\n ) : listingOrder && onBuy ? (\n <div className=\"btn-border-animated p-[1.5px] rounded-[10px] flex-1 h-8\">\n <button\n className=\"w-full h-full rounded-[9px] bg-background flex items-center justify-center gap-1.5 text-xs font-semibold text-foreground hover:bg-muted/60 transition-all active:scale-[0.98]\"\n onClick={handleBuy}\n >\n <Zap className=\"h-3.5 w-3.5 shrink-0\" />\n Buy\n </button>\n </div>\n ) : !listingOrder && onOffer ? (\n <button\n className={cn(BTN_OUTLINE, \"flex-1 text-brand-orange border-brand-orange/30 hover:bg-brand-orange/10 hover:text-brand-orange\")}\n onClick={handleOffer}\n >\n <HandCoins className=\"h-3.5 w-3.5 mr-1.5\" />\n Offer\n </button>\n ) : null}\n\n <DropdownMenu>\n <DropdownMenuTrigger asChild>\n <button\n className={cn(BTN_OUTLINE, \"w-8 p-0 shrink-0\")}\n aria-label=\"More actions\"\n >\n <MoreHorizontal className=\"h-3.5 w-3.5\" />\n </button>\n </DropdownMenuTrigger>\n <DropdownMenuContent align=\"end\" className=\"w-52\">\n\n <DropdownMenuItem asChild>\n <Link href={assetHref} className=\"flex items-center gap-2\">\n <ArrowUpRight className=\"h-3.5 w-3.5 text-muted-foreground\" />\n View asset\n </Link>\n </DropdownMenuItem>\n\n <DropdownMenuSeparator />\n\n {isOwner ? (\n <>\n {!listingOrder && onList && (\n <DropdownMenuItem\n className=\"flex items-center gap-2 text-brand-purple focus:text-brand-purple\"\n onClick={() => onList(token)}\n >\n <Tag className=\"h-3.5 w-3.5\" />\n List\n </DropdownMenuItem>\n )}\n {onTransfer && (\n <DropdownMenuItem\n className=\"flex items-center gap-2\"\n onClick={() => onTransfer(token)}\n >\n <ArrowRightLeft className=\"h-3.5 w-3.5 text-muted-foreground\" />\n Transfer\n </DropdownMenuItem>\n )}\n {listingOrder && onCancel && (\n <DropdownMenuItem\n className=\"flex items-center gap-2 text-brand-orange focus:text-brand-orange\"\n onClick={() => onCancel(token)}\n >\n <XCircle className=\"h-3.5 w-3.5\" />\n Cancel listing\n </DropdownMenuItem>\n )}\n </>\n ) : (\n <>\n {listingOrder && onBuy && (\n <DropdownMenuItem\n className=\"flex items-center gap-2 text-brand-blue focus:text-brand-blue\"\n onClick={handleBuy}\n >\n <Zap className=\"h-3.5 w-3.5\" />\n <span className=\"flex items-center gap-1\">\n Buy —\n <CurrencyIcon symbol={listingOrder.price.currency} size={12} />\n {formatDisplayPrice(listingOrder.price.formatted)}\n </span>\n </DropdownMenuItem>\n )}\n {onOffer && (\n <DropdownMenuItem\n className=\"flex items-center gap-2 text-brand-orange focus:text-brand-orange\"\n onClick={handleOffer}\n >\n <HandCoins className=\"h-3.5 w-3.5\" />\n Make offer\n </DropdownMenuItem>\n )}\n </>\n )}\n\n <DropdownMenuItem asChild>\n <Link href={remixHref} className=\"flex items-center gap-2 text-brand-purple focus:text-brand-purple\">\n <GitBranch className=\"h-3.5 w-3.5\" />\n Remix\n </Link>\n </DropdownMenuItem>\n\n <DropdownMenuSeparator />\n\n <DropdownMenuItem asChild>\n <Link href={collectionHref} className=\"flex items-center gap-2\">\n <Layers className=\"h-3.5 w-3.5 text-muted-foreground\" />\n View collection\n </Link>\n </DropdownMenuItem>\n\n {profileLink && (\n <DropdownMenuItem asChild>\n <Link href={profileLink.href} className=\"flex items-center gap-2\">\n <UserCircle2 className=\"h-3.5 w-3.5 text-muted-foreground\" />\n {profileLink.label}\n </Link>\n </DropdownMenuItem>\n )}\n\n {onReport && (\n <>\n <DropdownMenuSeparator />\n <DropdownMenuItem\n className=\"flex items-center gap-2 text-muted-foreground focus:text-muted-foreground\"\n onClick={() => onReport(token)}\n >\n <Flag className=\"h-3.5 w-3.5\" />\n Report\n </DropdownMenuItem>\n </>\n )}\n\n </DropdownMenuContent>\n </DropdownMenu>\n </div>\n </div>\n );\n}\n\nexport function TokenCardSkeleton() {\n return (\n <div className=\"card-base overflow-hidden\">\n <div className=\"aspect-square w-full animate-pulse bg-muted\" />\n <div className=\"px-3 pt-2.5 pb-3 space-y-1.5\">\n <div className=\"h-4 w-3/4 rounded-md animate-pulse bg-muted\" />\n </div>\n <div className=\"px-3 pb-3 flex gap-1.5\">\n <div className=\"h-8 w-8 rounded-[9px] animate-pulse bg-muted shrink-0\" />\n <div className=\"h-8 flex-1 rounded-[9px] animate-pulse bg-muted\" />\n <div className=\"h-8 w-8 rounded-[9px] animate-pulse bg-muted shrink-0\" />\n </div>\n </div>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAsFU;AApFV,kBAAiB;AACjB,0BAGO;AACP,2BAGO;AACP,gBAAmB;AACnB,oBAAmC;AACnC,2BAA6B;AAC7B,wBAAiC;AACjC,2BAA4B;AAC5B,kCAAmC;AACnC,uCAAyC;AACzC,iBAAmF;AAEnF,gCAAiC;AACjC,2BAA4B;AAG5B,MAAM,WAAW;AACjB,MAAM,kBAAc,cAAG,UAAU,2DAA2D;AAerF,SAAS,UAAU;AAAA,EACxB;AAAA,EACA,UAAU;AAAA,EACV;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAAmB;AACjB,QAAM,mBAAe,kCAAY;AACjC,QAAM,OAAO,MAAM,UAAU,QAAQ,UAAU,MAAM,OAAO;AAC5D,QAAM,QAAQ,aAAa,MAAM,UAAU,OAAO,EAAE,OAAO,IAAI,CAAC;AAChE,QAAM,eAAe,MAAM,UAAU,gBAAgB;AACrD,QAAM,SAAS,MAAM,UAAU;AAE/B,QAAM,eAAe,MAAM,cAAc;AAAA,IACvC,CAAC,MAAM,EAAE,MAAM,aAAa,YAAY,EAAE,MAAM,aAAa;AAAA,EAC/D;AAEA,QAAM,kBAAc,4CAAiB,KAAK;AAC1C,QAAM,mBAAmB,UAAU,CAAC,eAAe,eAAe,CAAC,CAAC,QAAQ,CAAC,CAAC;AAE9E,QAAM,QAAQ,MAAM,MAAM,YAAY;AACtC,QAAM,WAAO,2DAAyB,OAAO,MAAM,eAAe;AAClE,QAAM,gBAAY,WAAAA,WAAe,OAAO,MAAM,iBAAiB,MAAM,OAAO;AAC5E,QAAM,qBAAiB,WAAAC,gBAAoB,OAAO,MAAM,eAAe;AACvE,QAAM,YAAY,iBAAiB,MAAM,eAAe,IAAI,MAAM,OAAO;AAEzE,QAAM,YAAY,CAAC,MAAwB;AACzC,MAAE,eAAe;AACjB,MAAE,gBAAgB;AAClB,YAAQ,KAAK;AAAA,EACf;AAEA,QAAM,cAAc,CAAC,MAAwB;AAC3C,MAAE,eAAe;AACjB,MAAE,gBAAgB;AAClB,cAAU,KAAK;AAAA,EACjB;AAEA,SACE,6CAAC,SAAI,WAAU,iEACZ;AAAA,cACC,4CAAC,SAAI,WAAU,+BACb,sDAAC,oCAAY,QAAuB,MAAK,MAAK,GAChD;AAAA,IAGF,6CAAC,YAAAC,SAAA,EAAK,MAAM,WAAW,WAAU,wBAE/B;AAAA,mDAAC,SAAI,WAAU,4DACb;AAAA;AAAA,UAAC;AAAA;AAAA,YACC;AAAA,YACA;AAAA,YACA;AAAA,YACA,KAAK;AAAA,YACL,MAAK;AAAA,YACL,OAAM;AAAA,YACN,WAAU;AAAA,YACV,UACE,4CAAC,SAAI,WAAU,4EAA2E;AAAA;AAAA,QAE9F;AAAA,QAEC,gBACC,4CAAC,SAAI,WAAU,kCACb,sDAAC,UAAK,WAAU,kKACd;AAAA,UAAC;AAAA;AAAA,YACC,iBAAiB,aAAa,MAAM;AAAA,YACpC,UAAU,aAAa,MAAM;AAAA,YAC7B;AAAA,YACA,MAAK;AAAA;AAAA,QACP,GACF,GACF;AAAA,SAGA,MAAM,mBAAmB,aAAa,MAAM,mBAAmB,eAC/D,6CAAC,SAAI,WAAU,4GACb;AAAA,sDAAC,+BAAQ,WAAU,sCAAqC;AAAA,UACxD,4CAAC,UAAK,WAAU,0BAAyB,4BAAS;AAAA,WACpD;AAAA,SAEJ;AAAA,MAEA,4CAAC,SAAI,WAAU,2BACb,sDAAC,OAAE,WAAU,iDAAiD,gBAAK,GACrE;AAAA,OACF;AAAA,IAEA,6CAAC,SAAI,WAAU,uCAEb;AAAA,mDAAC,YAAAA,SAAA,EAAK,MAAM,WAAW,eAAW,cAAG,aAAa,UAAU,mBAAmB,aAAa,QAAQ,GAClG;AAAA,oDAAC,oCAAa,WAAU,eAAc;AAAA,QAAE;AAAA,SAE1C;AAAA,MAEC,UACC,CAAC,eACC;AAAA,QAAC;AAAA;AAAA,UACC,eAAW,cAAG,aAAa,kGAAkG;AAAA,UAC7H,SAAS,CAAC,MAAM;AAAE,cAAE,eAAe;AAAG,cAAE,gBAAgB;AAAG,qBAAS,KAAK;AAAA,UAAG;AAAA,UAE5E;AAAA,wDAAC,2BAAI,WAAU,sBAAqB;AAAA,YAAE;AAAA;AAAA;AAAA,MAExC,IACE,OACF,gBAAgB,QAClB,4CAAC,SAAI,WAAU,2DACb;AAAA,QAAC;AAAA;AAAA,UACC,WAAU;AAAA,UACV,SAAS;AAAA,UAET;AAAA,wDAAC,2BAAI,WAAU,wBAAuB;AAAA,YAAE;AAAA;AAAA;AAAA,MAE1C,GACF,IACE,CAAC,gBAAgB,UACnB;AAAA,QAAC;AAAA;AAAA,UACC,eAAW,cAAG,aAAa,kGAAkG;AAAA,UAC7H,SAAS;AAAA,UAET;AAAA,wDAAC,iCAAU,WAAU,sBAAqB;AAAA,YAAE;AAAA;AAAA;AAAA,MAE9C,IACE;AAAA,MAEJ,6CAAC,qCACC;AAAA,oDAAC,4CAAoB,SAAO,MAC1B;AAAA,UAAC;AAAA;AAAA,YACC,eAAW,cAAG,aAAa,kBAAkB;AAAA,YAC7C,cAAW;AAAA,YAEX,sDAAC,sCAAe,WAAU,eAAc;AAAA;AAAA,QAC1C,GACF;AAAA,QACA,6CAAC,4CAAoB,OAAM,OAAM,WAAU,QAEzC;AAAA,sDAAC,yCAAiB,SAAO,MACvB,uDAAC,YAAAA,SAAA,EAAK,MAAM,WAAW,WAAU,2BAC/B;AAAA,wDAAC,oCAAa,WAAU,qCAAoC;AAAA,YAAE;AAAA,aAEhE,GACF;AAAA,UAEA,4CAAC,8CAAsB;AAAA,UAEtB,UACC,4EACG;AAAA,aAAC,gBAAgB,UAChB;AAAA,cAAC;AAAA;AAAA,gBACC,WAAU;AAAA,gBACV,SAAS,MAAM,OAAO,KAAK;AAAA,gBAE3B;AAAA,8DAAC,2BAAI,WAAU,eAAc;AAAA,kBAAE;AAAA;AAAA;AAAA,YAEjC;AAAA,YAED,cACC;AAAA,cAAC;AAAA;AAAA,gBACC,WAAU;AAAA,gBACV,SAAS,MAAM,WAAW,KAAK;AAAA,gBAE/B;AAAA,8DAAC,sCAAe,WAAU,qCAAoC;AAAA,kBAAE;AAAA;AAAA;AAAA,YAElE;AAAA,YAED,gBAAgB,YACf;AAAA,cAAC;AAAA;AAAA,gBACC,WAAU;AAAA,gBACV,SAAS,MAAM,SAAS,KAAK;AAAA,gBAE7B;AAAA,8DAAC,+BAAQ,WAAU,eAAc;AAAA,kBAAE;AAAA;AAAA;AAAA,YAErC;AAAA,aAEJ,IAEA,4EACG;AAAA,4BAAgB,SACf;AAAA,cAAC;AAAA;AAAA,gBACC,WAAU;AAAA,gBACV,SAAS;AAAA,gBAET;AAAA,8DAAC,2BAAI,WAAU,eAAc;AAAA,kBAC7B,6CAAC,UAAK,WAAU,2BAA0B;AAAA;AAAA,oBAExC,4CAAC,qCAAa,QAAQ,aAAa,MAAM,UAAU,MAAM,IAAI;AAAA,wBAC5D,kCAAmB,aAAa,MAAM,SAAS;AAAA,qBAClD;AAAA;AAAA;AAAA,YACF;AAAA,YAED,WACC;AAAA,cAAC;AAAA;AAAA,gBACC,WAAU;AAAA,gBACV,SAAS;AAAA,gBAET;AAAA,8DAAC,iCAAU,WAAU,eAAc;AAAA,kBAAE;AAAA;AAAA;AAAA,YAEvC;AAAA,aAEJ;AAAA,UAGF,4CAAC,yCAAiB,SAAO,MACvB,uDAAC,YAAAA,SAAA,EAAK,MAAM,WAAW,WAAU,qEAC/B;AAAA,wDAAC,iCAAU,WAAU,eAAc;AAAA,YAAE;AAAA,aAEvC,GACF;AAAA,UAEA,4CAAC,8CAAsB;AAAA,UAEvB,4CAAC,yCAAiB,SAAO,MACvB,uDAAC,YAAAA,SAAA,EAAK,MAAM,gBAAgB,WAAU,2BACpC;AAAA,wDAAC,8BAAO,WAAU,qCAAoC;AAAA,YAAE;AAAA,aAE1D,GACF;AAAA,UAEC,eACC,4CAAC,yCAAiB,SAAO,MACvB,uDAAC,YAAAA,SAAA,EAAK,MAAM,YAAY,MAAM,WAAU,2BACtC;AAAA,wDAAC,mCAAY,WAAU,qCAAoC;AAAA,YAC1D,YAAY;AAAA,aACf,GACF;AAAA,UAGD,YACC,4EACE;AAAA,wDAAC,8CAAsB;AAAA,YACvB;AAAA,cAAC;AAAA;AAAA,gBACC,WAAU;AAAA,gBACV,SAAS,MAAM,SAAS,KAAK;AAAA,gBAE7B;AAAA,8DAAC,4BAAK,WAAU,eAAc;AAAA,kBAAE;AAAA;AAAA;AAAA,YAElC;AAAA,aACF;AAAA,WAGJ;AAAA,SACF;AAAA,OACF;AAAA,KACF;AAEJ;AAEO,SAAS,oBAAoB;AAClC,SACE,6CAAC,SAAI,WAAU,6BACb;AAAA,gDAAC,SAAI,WAAU,+CAA8C;AAAA,IAC7D,4CAAC,SAAI,WAAU,gCACb,sDAAC,SAAI,WAAU,+CAA8C,GAC/D;AAAA,IACA,6CAAC,SAAI,WAAU,0BACb;AAAA,kDAAC,SAAI,WAAU,yDAAwD;AAAA,MACvE,4CAAC,SAAI,WAAU,mDAAkD;AAAA,MACjE,4CAAC,SAAI,WAAU,yDAAwD;AAAA,OACzE;AAAA,KACF;AAEJ;","names":["buildAssetHref","buildCollectionHref","Link"]}
@@ -70,7 +70,7 @@ function TokenCard({
70
70
  e.stopPropagation();
71
71
  onOffer?.(token);
72
72
  };
73
- return /* @__PURE__ */ jsxs("div", { className: "card-base relative overflow-hidden flex flex-col w-full", children: [
73
+ return /* @__PURE__ */ jsxs("div", { className: "card-base group relative overflow-hidden flex flex-col w-full", children: [
74
74
  ipType && /* @__PURE__ */ jsx("div", { className: "absolute top-2 right-2 z-10", children: /* @__PURE__ */ jsx(IpTypeBadge, { ipType, size: "sm" }) }),
75
75
  /* @__PURE__ */ jsxs(Link, { href: assetHref, className: "flex flex-col flex-1", children: [
76
76
  /* @__PURE__ */ jsxs("div", { className: "relative aspect-square bg-muted overflow-hidden shrink-0", children: [
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/components/token-card.tsx"],"sourcesContent":["\"use client\";\n\nimport Link from \"./link.js\";\nimport {\n Loader2, ArrowUpRight, MoreHorizontal, Zap, HandCoins, Tag,\n Layers, GitBranch, Flag, UserCircle2, ArrowRightLeft, XCircle,\n} from \"lucide-react\";\nimport {\n DropdownMenu, DropdownMenuContent, DropdownMenuItem,\n DropdownMenuSeparator, DropdownMenuTrigger,\n} from \"./dropdown-menu.js\";\nimport { cn } from \"../utils/cn.js\";\nimport { formatDisplayPrice } from \"../utils/format.js\";\nimport { CurrencyIcon } from \"./currency-icon.js\";\nimport { PriceChipContent } from \"./dual-price.js\";\nimport { IpTypeBadge } from \"./ip-type-badge.js\";\nimport { AnimatedTokenMedia } from \"./animated-token-media.js\";\nimport { isLivingRenderCollection } from \"../data/living-render-collections.js\";\nimport { assetHref as buildAssetHref, collectionHref as buildCollectionHref } from \"@medialane/sdk\";\nimport type { ApiToken, Chain } from \"@medialane/sdk\";\nimport { tokenProfileLink } from \"../utils/token-profile-link.js\";\nimport { useImageUrl } from \"../utils/use-image-url.js\";\n\n\nconst BTN_BASE = \"h-8 rounded-[9px] flex items-center justify-center gap-1.5 text-xs font-semibold transition-all active:scale-[0.98] shadow-none border-0\";\nconst BTN_OUTLINE = cn(BTN_BASE, \"border border-border/60 text-foreground hover:bg-muted/60\");\n\nexport interface TokenCardProps {\n token: ApiToken;\n isOwner?: boolean;\n onList?: (token: ApiToken) => void;\n onTransfer?: (token: ApiToken) => void;\n onCancel?: (token: ApiToken) => void;\n onBuy?: (token: ApiToken) => void;\n onOffer?: (token: ApiToken) => void;\n onReport?: (token: ApiToken) => void;\n\n usdValue?: string | null;\n}\n\nexport function TokenCard({\n token,\n isOwner = false,\n onList,\n onTransfer,\n onCancel,\n onBuy,\n onOffer,\n onReport,\n usdValue,\n}: TokenCardProps) {\n const resolveImage = useImageUrl();\n const name = token.metadata?.name || `Token #${token.tokenId}`;\n const image = resolveImage(token.metadata?.image, { width: 480 });\n const animationUrl = token.metadata?.animationUrl ?? null;\n const ipType = token.metadata?.ipType;\n\n const listingOrder = token.activeOrders?.find(\n (o) => o.offer.itemType === \"ERC721\" || o.offer.itemType === \"ERC1155\"\n );\n\n const profileLink = tokenProfileLink(token);\n const hasPrimaryAction = isOwner ? !listingOrder : listingOrder ? !!onBuy : !!onOffer;\n\n const chain = token.chain.toUpperCase() as Chain;\n const live = isLivingRenderCollection(chain, token.contractAddress);\n const assetHref = buildAssetHref(chain, token.contractAddress, token.tokenId);\n const collectionHref = buildCollectionHref(chain, token.contractAddress);\n const remixHref = `/create/remix/${token.contractAddress}/${token.tokenId}`;\n\n const handleBuy = (e: React.MouseEvent) => {\n e.preventDefault();\n e.stopPropagation();\n onBuy?.(token);\n };\n\n const handleOffer = (e: React.MouseEvent) => {\n e.preventDefault();\n e.stopPropagation();\n onOffer?.(token);\n };\n\n return (\n <div className=\"card-base relative overflow-hidden flex flex-col w-full\">\n {ipType && (\n <div className=\"absolute top-2 right-2 z-10\">\n <IpTypeBadge ipType={ipType as any} size=\"sm\" />\n </div>\n )}\n\n <Link href={assetHref} className=\"flex flex-col flex-1\">\n\n <div className=\"relative aspect-square bg-muted overflow-hidden shrink-0\">\n <AnimatedTokenMedia\n image={image}\n animationUrl={animationUrl}\n live={live}\n alt={name}\n mode=\"fill\"\n sizes=\"(max-width: 640px) 50vw, (max-width: 1024px) 33vw, 22vw\"\n className=\"object-cover\"\n fallback={\n <div className=\"absolute inset-0 bg-gradient-to-br from-brand-purple/15 to-brand-blue/15\" />\n }\n />\n\n {listingOrder && (\n <div className=\"absolute bottom-2 right-2 z-10\">\n <span className=\"inline-flex items-center gap-1.5 px-2.5 py-1.5 rounded-full text-sm font-bold bg-background/90 backdrop-blur-sm border border-border/40 shadow-sm tabular-nums\">\n <PriceChipContent\n amountFormatted={listingOrder.price.formatted}\n currency={listingOrder.price.currency}\n usdValue={usdValue}\n tone=\"card\"\n />\n </span>\n </div>\n )}\n\n {(token.metadataStatus === \"PENDING\" || token.metadataStatus === \"FETCHING\") && (\n <div className=\"absolute bottom-0 inset-x-0 flex items-center justify-center gap-1.5 bg-black/50 backdrop-blur-sm py-1.5\">\n <Loader2 className=\"h-3 w-3 animate-spin text-white/70\" />\n <span className=\"text-2xs text-white/70\">Indexing…</span>\n </div>\n )}\n </div>\n\n <div className=\"px-3 pt-2.5 pb-4 flex-1\">\n <p className=\"text-base font-bold line-clamp-2 leading-snug\">{name}</p>\n </div>\n </Link>\n\n <div className=\"px-3 pb-3 flex items-center gap-1.5\">\n\n <Link href={assetHref} className={cn(BTN_OUTLINE, \"px-2.5\", hasPrimaryAction ? \"shrink-0\" : \"flex-1\")}>\n <ArrowUpRight className=\"h-3.5 w-3.5\" />\n View\n </Link>\n\n {isOwner ? (\n !listingOrder ? (\n <button\n className={cn(BTN_OUTLINE, \"flex-1 text-brand-purple border-brand-purple/30 hover:bg-brand-purple/10 hover:text-brand-purple\")}\n onClick={(e) => { e.preventDefault(); e.stopPropagation(); onList?.(token); }}\n >\n <Tag className=\"h-3.5 w-3.5 mr-1.5\" />\n List\n </button>\n ) : null\n ) : listingOrder && onBuy ? (\n <div className=\"btn-border-animated p-[1.5px] rounded-[10px] flex-1 h-8\">\n <button\n className=\"w-full h-full rounded-[9px] bg-background flex items-center justify-center gap-1.5 text-xs font-semibold text-foreground hover:bg-muted/60 transition-all active:scale-[0.98]\"\n onClick={handleBuy}\n >\n <Zap className=\"h-3.5 w-3.5 shrink-0\" />\n Buy\n </button>\n </div>\n ) : !listingOrder && onOffer ? (\n <button\n className={cn(BTN_OUTLINE, \"flex-1 text-brand-orange border-brand-orange/30 hover:bg-brand-orange/10 hover:text-brand-orange\")}\n onClick={handleOffer}\n >\n <HandCoins className=\"h-3.5 w-3.5 mr-1.5\" />\n Offer\n </button>\n ) : null}\n\n <DropdownMenu>\n <DropdownMenuTrigger asChild>\n <button\n className={cn(BTN_OUTLINE, \"w-8 p-0 shrink-0\")}\n aria-label=\"More actions\"\n >\n <MoreHorizontal className=\"h-3.5 w-3.5\" />\n </button>\n </DropdownMenuTrigger>\n <DropdownMenuContent align=\"end\" className=\"w-52\">\n\n <DropdownMenuItem asChild>\n <Link href={assetHref} className=\"flex items-center gap-2\">\n <ArrowUpRight className=\"h-3.5 w-3.5 text-muted-foreground\" />\n View asset\n </Link>\n </DropdownMenuItem>\n\n <DropdownMenuSeparator />\n\n {isOwner ? (\n <>\n {!listingOrder && onList && (\n <DropdownMenuItem\n className=\"flex items-center gap-2 text-brand-purple focus:text-brand-purple\"\n onClick={() => onList(token)}\n >\n <Tag className=\"h-3.5 w-3.5\" />\n List\n </DropdownMenuItem>\n )}\n {onTransfer && (\n <DropdownMenuItem\n className=\"flex items-center gap-2\"\n onClick={() => onTransfer(token)}\n >\n <ArrowRightLeft className=\"h-3.5 w-3.5 text-muted-foreground\" />\n Transfer\n </DropdownMenuItem>\n )}\n {listingOrder && onCancel && (\n <DropdownMenuItem\n className=\"flex items-center gap-2 text-brand-orange focus:text-brand-orange\"\n onClick={() => onCancel(token)}\n >\n <XCircle className=\"h-3.5 w-3.5\" />\n Cancel listing\n </DropdownMenuItem>\n )}\n </>\n ) : (\n <>\n {listingOrder && onBuy && (\n <DropdownMenuItem\n className=\"flex items-center gap-2 text-brand-blue focus:text-brand-blue\"\n onClick={handleBuy}\n >\n <Zap className=\"h-3.5 w-3.5\" />\n <span className=\"flex items-center gap-1\">\n Buy —\n <CurrencyIcon symbol={listingOrder.price.currency} size={12} />\n {formatDisplayPrice(listingOrder.price.formatted)}\n </span>\n </DropdownMenuItem>\n )}\n {onOffer && (\n <DropdownMenuItem\n className=\"flex items-center gap-2 text-brand-orange focus:text-brand-orange\"\n onClick={handleOffer}\n >\n <HandCoins className=\"h-3.5 w-3.5\" />\n Make offer\n </DropdownMenuItem>\n )}\n </>\n )}\n\n <DropdownMenuItem asChild>\n <Link href={remixHref} className=\"flex items-center gap-2 text-brand-purple focus:text-brand-purple\">\n <GitBranch className=\"h-3.5 w-3.5\" />\n Remix\n </Link>\n </DropdownMenuItem>\n\n <DropdownMenuSeparator />\n\n <DropdownMenuItem asChild>\n <Link href={collectionHref} className=\"flex items-center gap-2\">\n <Layers className=\"h-3.5 w-3.5 text-muted-foreground\" />\n View collection\n </Link>\n </DropdownMenuItem>\n\n {profileLink && (\n <DropdownMenuItem asChild>\n <Link href={profileLink.href} className=\"flex items-center gap-2\">\n <UserCircle2 className=\"h-3.5 w-3.5 text-muted-foreground\" />\n {profileLink.label}\n </Link>\n </DropdownMenuItem>\n )}\n\n {onReport && (\n <>\n <DropdownMenuSeparator />\n <DropdownMenuItem\n className=\"flex items-center gap-2 text-muted-foreground focus:text-muted-foreground\"\n onClick={() => onReport(token)}\n >\n <Flag className=\"h-3.5 w-3.5\" />\n Report\n </DropdownMenuItem>\n </>\n )}\n\n </DropdownMenuContent>\n </DropdownMenu>\n </div>\n </div>\n );\n}\n\nexport function TokenCardSkeleton() {\n return (\n <div className=\"card-base overflow-hidden\">\n <div className=\"aspect-square w-full animate-pulse bg-muted\" />\n <div className=\"px-3 pt-2.5 pb-3 space-y-1.5\">\n <div className=\"h-4 w-3/4 rounded-md animate-pulse bg-muted\" />\n </div>\n <div className=\"px-3 pb-3 flex gap-1.5\">\n <div className=\"h-8 w-8 rounded-[9px] animate-pulse bg-muted shrink-0\" />\n <div className=\"h-8 flex-1 rounded-[9px] animate-pulse bg-muted\" />\n <div className=\"h-8 w-8 rounded-[9px] animate-pulse bg-muted shrink-0\" />\n </div>\n </div>\n );\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../src/components/token-card.tsx"],"sourcesContent":["\"use client\";\n\nimport Link from \"./link.js\";\nimport {\n Loader2, ArrowUpRight, MoreHorizontal, Zap, HandCoins, Tag,\n Layers, GitBranch, Flag, UserCircle2, ArrowRightLeft, XCircle,\n} from \"lucide-react\";\nimport {\n DropdownMenu, DropdownMenuContent, DropdownMenuItem,\n DropdownMenuSeparator, DropdownMenuTrigger,\n} from \"./dropdown-menu.js\";\nimport { cn } from \"../utils/cn.js\";\nimport { formatDisplayPrice } from \"../utils/format.js\";\nimport { CurrencyIcon } from \"./currency-icon.js\";\nimport { PriceChipContent } from \"./dual-price.js\";\nimport { IpTypeBadge } from \"./ip-type-badge.js\";\nimport { AnimatedTokenMedia } from \"./animated-token-media.js\";\nimport { isLivingRenderCollection } from \"../data/living-render-collections.js\";\nimport { assetHref as buildAssetHref, collectionHref as buildCollectionHref } from \"@medialane/sdk\";\nimport type { ApiToken, Chain } from \"@medialane/sdk\";\nimport { tokenProfileLink } from \"../utils/token-profile-link.js\";\nimport { useImageUrl } from \"../utils/use-image-url.js\";\n\n\nconst BTN_BASE = \"h-8 rounded-[9px] flex items-center justify-center gap-1.5 text-xs font-semibold transition-all active:scale-[0.98] shadow-none border-0\";\nconst BTN_OUTLINE = cn(BTN_BASE, \"border border-border/60 text-foreground hover:bg-muted/60\");\n\nexport interface TokenCardProps {\n token: ApiToken;\n isOwner?: boolean;\n onList?: (token: ApiToken) => void;\n onTransfer?: (token: ApiToken) => void;\n onCancel?: (token: ApiToken) => void;\n onBuy?: (token: ApiToken) => void;\n onOffer?: (token: ApiToken) => void;\n onReport?: (token: ApiToken) => void;\n\n usdValue?: string | null;\n}\n\nexport function TokenCard({\n token,\n isOwner = false,\n onList,\n onTransfer,\n onCancel,\n onBuy,\n onOffer,\n onReport,\n usdValue,\n}: TokenCardProps) {\n const resolveImage = useImageUrl();\n const name = token.metadata?.name || `Token #${token.tokenId}`;\n const image = resolveImage(token.metadata?.image, { width: 480 });\n const animationUrl = token.metadata?.animationUrl ?? null;\n const ipType = token.metadata?.ipType;\n\n const listingOrder = token.activeOrders?.find(\n (o) => o.offer.itemType === \"ERC721\" || o.offer.itemType === \"ERC1155\"\n );\n\n const profileLink = tokenProfileLink(token);\n const hasPrimaryAction = isOwner ? !listingOrder : listingOrder ? !!onBuy : !!onOffer;\n\n const chain = token.chain.toUpperCase() as Chain;\n const live = isLivingRenderCollection(chain, token.contractAddress);\n const assetHref = buildAssetHref(chain, token.contractAddress, token.tokenId);\n const collectionHref = buildCollectionHref(chain, token.contractAddress);\n const remixHref = `/create/remix/${token.contractAddress}/${token.tokenId}`;\n\n const handleBuy = (e: React.MouseEvent) => {\n e.preventDefault();\n e.stopPropagation();\n onBuy?.(token);\n };\n\n const handleOffer = (e: React.MouseEvent) => {\n e.preventDefault();\n e.stopPropagation();\n onOffer?.(token);\n };\n\n return (\n <div className=\"card-base group relative overflow-hidden flex flex-col w-full\">\n {ipType && (\n <div className=\"absolute top-2 right-2 z-10\">\n <IpTypeBadge ipType={ipType as any} size=\"sm\" />\n </div>\n )}\n\n <Link href={assetHref} className=\"flex flex-col flex-1\">\n\n <div className=\"relative aspect-square bg-muted overflow-hidden shrink-0\">\n <AnimatedTokenMedia\n image={image}\n animationUrl={animationUrl}\n live={live}\n alt={name}\n mode=\"fill\"\n sizes=\"(max-width: 640px) 50vw, (max-width: 1024px) 33vw, 22vw\"\n className=\"object-cover\"\n fallback={\n <div className=\"absolute inset-0 bg-gradient-to-br from-brand-purple/15 to-brand-blue/15\" />\n }\n />\n\n {listingOrder && (\n <div className=\"absolute bottom-2 right-2 z-10\">\n <span className=\"inline-flex items-center gap-1.5 px-2.5 py-1.5 rounded-full text-sm font-bold bg-background/90 backdrop-blur-sm border border-border/40 shadow-sm tabular-nums\">\n <PriceChipContent\n amountFormatted={listingOrder.price.formatted}\n currency={listingOrder.price.currency}\n usdValue={usdValue}\n tone=\"card\"\n />\n </span>\n </div>\n )}\n\n {(token.metadataStatus === \"PENDING\" || token.metadataStatus === \"FETCHING\") && (\n <div className=\"absolute bottom-0 inset-x-0 flex items-center justify-center gap-1.5 bg-black/50 backdrop-blur-sm py-1.5\">\n <Loader2 className=\"h-3 w-3 animate-spin text-white/70\" />\n <span className=\"text-2xs text-white/70\">Indexing…</span>\n </div>\n )}\n </div>\n\n <div className=\"px-3 pt-2.5 pb-4 flex-1\">\n <p className=\"text-base font-bold line-clamp-2 leading-snug\">{name}</p>\n </div>\n </Link>\n\n <div className=\"px-3 pb-3 flex items-center gap-1.5\">\n\n <Link href={assetHref} className={cn(BTN_OUTLINE, \"px-2.5\", hasPrimaryAction ? \"shrink-0\" : \"flex-1\")}>\n <ArrowUpRight className=\"h-3.5 w-3.5\" />\n View\n </Link>\n\n {isOwner ? (\n !listingOrder ? (\n <button\n className={cn(BTN_OUTLINE, \"flex-1 text-brand-purple border-brand-purple/30 hover:bg-brand-purple/10 hover:text-brand-purple\")}\n onClick={(e) => { e.preventDefault(); e.stopPropagation(); onList?.(token); }}\n >\n <Tag className=\"h-3.5 w-3.5 mr-1.5\" />\n List\n </button>\n ) : null\n ) : listingOrder && onBuy ? (\n <div className=\"btn-border-animated p-[1.5px] rounded-[10px] flex-1 h-8\">\n <button\n className=\"w-full h-full rounded-[9px] bg-background flex items-center justify-center gap-1.5 text-xs font-semibold text-foreground hover:bg-muted/60 transition-all active:scale-[0.98]\"\n onClick={handleBuy}\n >\n <Zap className=\"h-3.5 w-3.5 shrink-0\" />\n Buy\n </button>\n </div>\n ) : !listingOrder && onOffer ? (\n <button\n className={cn(BTN_OUTLINE, \"flex-1 text-brand-orange border-brand-orange/30 hover:bg-brand-orange/10 hover:text-brand-orange\")}\n onClick={handleOffer}\n >\n <HandCoins className=\"h-3.5 w-3.5 mr-1.5\" />\n Offer\n </button>\n ) : null}\n\n <DropdownMenu>\n <DropdownMenuTrigger asChild>\n <button\n className={cn(BTN_OUTLINE, \"w-8 p-0 shrink-0\")}\n aria-label=\"More actions\"\n >\n <MoreHorizontal className=\"h-3.5 w-3.5\" />\n </button>\n </DropdownMenuTrigger>\n <DropdownMenuContent align=\"end\" className=\"w-52\">\n\n <DropdownMenuItem asChild>\n <Link href={assetHref} className=\"flex items-center gap-2\">\n <ArrowUpRight className=\"h-3.5 w-3.5 text-muted-foreground\" />\n View asset\n </Link>\n </DropdownMenuItem>\n\n <DropdownMenuSeparator />\n\n {isOwner ? (\n <>\n {!listingOrder && onList && (\n <DropdownMenuItem\n className=\"flex items-center gap-2 text-brand-purple focus:text-brand-purple\"\n onClick={() => onList(token)}\n >\n <Tag className=\"h-3.5 w-3.5\" />\n List\n </DropdownMenuItem>\n )}\n {onTransfer && (\n <DropdownMenuItem\n className=\"flex items-center gap-2\"\n onClick={() => onTransfer(token)}\n >\n <ArrowRightLeft className=\"h-3.5 w-3.5 text-muted-foreground\" />\n Transfer\n </DropdownMenuItem>\n )}\n {listingOrder && onCancel && (\n <DropdownMenuItem\n className=\"flex items-center gap-2 text-brand-orange focus:text-brand-orange\"\n onClick={() => onCancel(token)}\n >\n <XCircle className=\"h-3.5 w-3.5\" />\n Cancel listing\n </DropdownMenuItem>\n )}\n </>\n ) : (\n <>\n {listingOrder && onBuy && (\n <DropdownMenuItem\n className=\"flex items-center gap-2 text-brand-blue focus:text-brand-blue\"\n onClick={handleBuy}\n >\n <Zap className=\"h-3.5 w-3.5\" />\n <span className=\"flex items-center gap-1\">\n Buy —\n <CurrencyIcon symbol={listingOrder.price.currency} size={12} />\n {formatDisplayPrice(listingOrder.price.formatted)}\n </span>\n </DropdownMenuItem>\n )}\n {onOffer && (\n <DropdownMenuItem\n className=\"flex items-center gap-2 text-brand-orange focus:text-brand-orange\"\n onClick={handleOffer}\n >\n <HandCoins className=\"h-3.5 w-3.5\" />\n Make offer\n </DropdownMenuItem>\n )}\n </>\n )}\n\n <DropdownMenuItem asChild>\n <Link href={remixHref} className=\"flex items-center gap-2 text-brand-purple focus:text-brand-purple\">\n <GitBranch className=\"h-3.5 w-3.5\" />\n Remix\n </Link>\n </DropdownMenuItem>\n\n <DropdownMenuSeparator />\n\n <DropdownMenuItem asChild>\n <Link href={collectionHref} className=\"flex items-center gap-2\">\n <Layers className=\"h-3.5 w-3.5 text-muted-foreground\" />\n View collection\n </Link>\n </DropdownMenuItem>\n\n {profileLink && (\n <DropdownMenuItem asChild>\n <Link href={profileLink.href} className=\"flex items-center gap-2\">\n <UserCircle2 className=\"h-3.5 w-3.5 text-muted-foreground\" />\n {profileLink.label}\n </Link>\n </DropdownMenuItem>\n )}\n\n {onReport && (\n <>\n <DropdownMenuSeparator />\n <DropdownMenuItem\n className=\"flex items-center gap-2 text-muted-foreground focus:text-muted-foreground\"\n onClick={() => onReport(token)}\n >\n <Flag className=\"h-3.5 w-3.5\" />\n Report\n </DropdownMenuItem>\n </>\n )}\n\n </DropdownMenuContent>\n </DropdownMenu>\n </div>\n </div>\n );\n}\n\nexport function TokenCardSkeleton() {\n return (\n <div className=\"card-base overflow-hidden\">\n <div className=\"aspect-square w-full animate-pulse bg-muted\" />\n <div className=\"px-3 pt-2.5 pb-3 space-y-1.5\">\n <div className=\"h-4 w-3/4 rounded-md animate-pulse bg-muted\" />\n </div>\n <div className=\"px-3 pb-3 flex gap-1.5\">\n <div className=\"h-8 w-8 rounded-[9px] animate-pulse bg-muted shrink-0\" />\n <div className=\"h-8 flex-1 rounded-[9px] animate-pulse bg-muted\" />\n <div className=\"h-8 w-8 rounded-[9px] animate-pulse bg-muted shrink-0\" />\n </div>\n </div>\n );\n}\n"],"mappings":"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","names":[]}
@@ -329,3 +329,10 @@ input[type="number"] { -moz-appearance: textfield; }
329
329
  @media (prefers-reduced-motion: reduce) {
330
330
  .ml-gbtn::before { animation: none; }
331
331
  }
332
+
333
+ .card-media {
334
+ transition: transform 500ms cubic-bezier(0.22, 1, 0.36, 1), opacity 300ms ease;
335
+ }
336
+ @media (hover: hover) and (prefers-reduced-motion: no-preference) {
337
+ .group:hover .card-media { transform: scale(1.04); }
338
+ }
@@ -7,7 +7,7 @@ declare function useTokenBalance(symbol: string | null, holderAddress: string |
7
7
  rawBalance: bigint | null;
8
8
  isLoading: boolean;
9
9
  error: any;
10
- decimals: 8 | 6 | 18;
10
+ decimals: 6 | 8 | 18;
11
11
  };
12
12
  declare function hasSufficientBalance(rawBalance: bigint | null, requiredHuman: string, decimals: number): boolean | null;
13
13
 
@@ -7,7 +7,7 @@ declare function useTokenBalance(symbol: string | null, holderAddress: string |
7
7
  rawBalance: bigint | null;
8
8
  isLoading: boolean;
9
9
  error: any;
10
- decimals: 8 | 6 | 18;
10
+ decimals: 6 | 8 | 18;
11
11
  };
12
12
  declare function hasSufficientBalance(rawBalance: bigint | null, requiredHuman: string, decimals: number): boolean | null;
13
13
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@medialane/ui",
3
- "version": "0.310.0",
3
+ "version": "0.311.0",
4
4
  "description": "Shared UI components for Medialane apps",
5
5
  "type": "module",
6
6
  "sideEffects": false,