@medialane/ui 0.107.1 → 0.109.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 (49) hide show
  1. package/dist/components/listing-card.cjs +4 -2
  2. package/dist/components/listing-card.cjs.map +1 -1
  3. package/dist/components/listing-card.d.cts +8 -1
  4. package/dist/components/listing-card.d.ts +8 -1
  5. package/dist/components/listing-card.js +4 -2
  6. package/dist/components/listing-card.js.map +1 -1
  7. package/dist/components/remixes-tab.cjs +91 -0
  8. package/dist/components/remixes-tab.cjs.map +1 -0
  9. package/dist/components/remixes-tab.d.cts +11 -0
  10. package/dist/components/remixes-tab.d.ts +11 -0
  11. package/dist/components/remixes-tab.js +57 -0
  12. package/dist/components/remixes-tab.js.map +1 -0
  13. package/dist/data/notification.cjs +17 -0
  14. package/dist/data/notification.cjs.map +1 -0
  15. package/dist/data/notification.d.cts +34 -0
  16. package/dist/data/notification.d.ts +34 -0
  17. package/dist/data/notification.js +1 -0
  18. package/dist/data/notification.js.map +1 -0
  19. package/dist/index.cjs +31 -2
  20. package/dist/index.cjs.map +1 -1
  21. package/dist/index.d.cts +6 -0
  22. package/dist/index.d.ts +6 -0
  23. package/dist/index.js +26 -1
  24. package/dist/index.js.map +1 -1
  25. package/dist/utils/api-fetch.cjs +65 -0
  26. package/dist/utils/api-fetch.cjs.map +1 -0
  27. package/dist/utils/api-fetch.d.cts +38 -0
  28. package/dist/utils/api-fetch.d.ts +38 -0
  29. package/dist/utils/api-fetch.js +40 -0
  30. package/dist/utils/api-fetch.js.map +1 -0
  31. package/dist/utils/use-notifications.cjs +169 -0
  32. package/dist/utils/use-notifications.cjs.map +1 -0
  33. package/dist/utils/use-notifications.d.cts +12 -0
  34. package/dist/utils/use-notifications.d.ts +12 -0
  35. package/dist/utils/use-notifications.js +140 -0
  36. package/dist/utils/use-notifications.js.map +1 -0
  37. package/dist/utils/use-orders.cjs +153 -0
  38. package/dist/utils/use-orders.cjs.map +1 -0
  39. package/dist/utils/use-orders.d.cts +55 -0
  40. package/dist/utils/use-orders.d.ts +55 -0
  41. package/dist/utils/use-orders.js +113 -0
  42. package/dist/utils/use-orders.js.map +1 -0
  43. package/dist/utils/use-remix-offers.cjs +49 -0
  44. package/dist/utils/use-remix-offers.cjs.map +1 -0
  45. package/dist/utils/use-remix-offers.d.cts +19 -0
  46. package/dist/utils/use-remix-offers.d.ts +19 -0
  47. package/dist/utils/use-remix-offers.js +15 -0
  48. package/dist/utils/use-remix-offers.js.map +1 -0
  49. package/package.json +1 -1
@@ -45,7 +45,7 @@ var import_ipfs = require("../utils/ipfs.js");
45
45
  var import_format = require("../utils/format.js");
46
46
  var import_time = require("../utils/time.js");
47
47
  var import_living_render_collections = require("../data/living-render-collections.js");
48
- function ListingCard({ order: rawOrder, inCart = false, onBuy, onCart, overflowMenu, primaryAction, compact = false, imageUrl }) {
48
+ function ListingCard({ order: rawOrder, inCart = false, onBuy, onCart, overflowMenu, primaryAction, compact = false, imageUrl, usdValue }) {
49
49
  const order = rawOrder;
50
50
  const [imgError, setImgError] = (0, import_react.useState)(false);
51
51
  const isListing = order.offer.itemType === "ERC721" || order.offer.itemType === "ERC1155";
@@ -81,6 +81,7 @@ function ListingCard({ order: rawOrder, inCart = false, onBuy, onCart, overflowM
81
81
  order.price.currency && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_currency_icon.CurrencyIcon, { symbol: order.price.currency, size: 11 }),
82
82
  (0, import_format.formatDisplayPrice)(order.price.formatted)
83
83
  ] }),
84
+ usdValue && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "text-2xs text-muted-foreground/70", children: usdValue }),
84
85
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "text-2xs text-muted-foreground", children: (0, import_time.timeAgo)(order.createdAt) })
85
86
  ] })
86
87
  ] }) });
@@ -116,7 +117,8 @@ function ListingCard({ order: rawOrder, inCart = false, onBuy, onCart, overflowM
116
117
  order.price.currency && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_currency_icon.CurrencyIcon, { symbol: order.price.currency, size: 18 }),
117
118
  (0, import_format.formatDisplayPrice)(order.price.formatted),
118
119
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "sr-only", children: order.price.currency })
119
- ] })
120
+ ] }),
121
+ usdValue && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "text-2xs text-muted-foreground/60 mt-0.5", children: usdValue })
120
122
  ] }),
121
123
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "text-2xs text-muted-foreground/60 whitespace-nowrap shrink-0", children: (0, import_time.timeAgo)(order.createdAt) })
122
124
  ] }),
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/components/listing-card.tsx"],"sourcesContent":["\"use client\";\n\nimport { useState } from \"react\";\nimport Link from \"next/link\";\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 { ipfsToHttp } from \"../utils/ipfs.js\";\nimport { formatDisplayPrice } from \"../utils/format.js\";\nimport { timeAgo } from \"../utils/time.js\";\nimport { isLivingRenderCollection } from \"../data/living-render-collections.js\";\nimport type { ApiOrder, Chain } from \"@medialane/sdk\";\n\n/** ui's pinned SDK lags the apps — extend structurally for fields newer SDKs carry. */\ntype OrderWithAnimation = ApiOrder & { token: (ApiOrder[\"token\"] & { animationUrl?: string | null }) | null };\n\nexport interface ListingCardProps {\n order: ApiOrder;\n inCart?: boolean;\n onBuy?: (order: ApiOrder) => void;\n onCart?: (order: ApiOrder) => void;\n /** App passes a fully constructed <DropdownMenu> here */\n overflowMenu?: React.ReactNode;\n /**\n * Optional flex-1 primary control for listings, replacing the default\n * Buy/View button — lets a host inject its own action (e.g. an owner\n * \"Cancel\" with an app-specific, auth-coupled handler) while keeping this\n * card's layout. Ignored for offers (which always render \"View asset\").\n */\n primaryAction?: React.ReactNode;\n compact?: boolean;\n /**\n * Pre-resolved, browser-loadable image URL. When provided (including null),\n * overrides the card's internal ipfsToHttp resolution — apps that proxy or\n * resize images (e.g. a same-origin /api/ipfs?w= proxy) pass it here.\n * Omit for the default gateway resolution; null renders the no-image fallback.\n */\n imageUrl?: string | null;\n}\n\nexport function ListingCard({ order: rawOrder, inCart = false, onBuy, onCart, overflowMenu, primaryAction, compact = false, imageUrl }: ListingCardProps) {\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 ? ipfsToHttp(order.token.image) : null);\n // Not resolved through ipfsToHttp — animation_url is legitimately a data: URI for\n // fully on-chain-rendered collections (ipfsToHttp rejects data: by design).\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 = `/asset/${order.nftContract}/${order.nftTokenId}`;\n\n // Show the action bar for listings (Buy/View) and for offers (View asset),\n // whenever the host wired any action or an overflow menu.\n const showActionBar = !!(onBuy || onCart || overflowMenu || primaryAction);\n\n // ─── Compact variant ──────────────────────────────────────────────────────\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 flex items-center justify-center bg-gradient-to-br from-brand-purple/15 to-brand-blue/15\">\n <span className=\"text-xl font-mono text-muted-foreground\">#{order.nftTokenId}</span>\n </div>\n }\n />\n </div>\n <div className=\"p-2.5 space-y-0.5\">\n <p className=\"text-xs font-semibold truncate\">{name}</p>\n {order.price?.formatted && (\n <p className=\"text-2xs font-bold price-value inline-flex items-center gap-1\">\n {order.price.currency && <CurrencyIcon symbol={order.price.currency} size={11} />}\n {formatDisplayPrice(order.price.formatted)}\n </p>\n )}\n <p className=\"text-2xs text-muted-foreground\">{timeAgo(order.createdAt)}</p>\n </div>\n </Link>\n </MotionCard>\n );\n }\n\n // ─── Full variant ─────────────────────────────────────────────────────────\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 flex items-center justify-center bg-gradient-to-br from-brand-purple/15 to-brand-blue/15\">\n <span className=\"text-2xl font-mono text-muted-foreground\">#{order.nftTokenId}</span>\n </div>\n }\n />\n </div>\n\n <div className=\"p-3.5 space-y-3\">\n <div className=\"min-w-0\">\n <p className=\"font-semibold text-base truncate leading-snug\">{name}</p>\n <p className=\"text-2xs text-muted-foreground truncate leading-snug mt-0.5\">\n {order.token?.description ? order.token.description : `#${order.nftTokenId}`}\n </p>\n </div>\n\n {/* Price / Offer + age */}\n {order.price?.formatted && (\n <div className=\"flex items-end justify-between gap-2\">\n <div className=\"min-w-0\">\n <p className=\"text-2xs uppercase tracking-widest text-muted-foreground/55 leading-none\">\n {isListing ? \"Price\" : \"Offer\"}\n </p>\n <p className=\"text-lg font-bold price-value leading-none inline-flex items-center gap-1.5 mt-1.5\">\n {order.price.currency && <CurrencyIcon symbol={order.price.currency} size={18} />}\n {formatDisplayPrice(order.price.formatted)}\n <span className=\"sr-only\">{order.price.currency}</span>\n </p>\n </div>\n <p className=\"text-2xs text-muted-foreground/60 whitespace-nowrap shrink-0\">\n {timeAgo(order.createdAt)}\n </p>\n </div>\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":["Link"]}
1
+ {"version":3,"sources":["../../src/components/listing-card.tsx"],"sourcesContent":["\"use client\";\n\nimport { useState } from \"react\";\nimport Link from \"next/link\";\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 { ipfsToHttp } from \"../utils/ipfs.js\";\nimport { formatDisplayPrice } from \"../utils/format.js\";\nimport { timeAgo } from \"../utils/time.js\";\nimport { isLivingRenderCollection } from \"../data/living-render-collections.js\";\nimport type { ApiOrder, Chain } from \"@medialane/sdk\";\n\n/** ui's pinned SDK lags the apps — extend structurally for fields newer SDKs carry. */\ntype OrderWithAnimation = ApiOrder & { token: (ApiOrder[\"token\"] & { animationUrl?: string | null }) | null };\n\nexport interface ListingCardProps {\n order: ApiOrder;\n inCart?: boolean;\n onBuy?: (order: ApiOrder) => void;\n onCart?: (order: ApiOrder) => void;\n /** App passes a fully constructed <DropdownMenu> here */\n overflowMenu?: React.ReactNode;\n /**\n * Optional flex-1 primary control for listings, replacing the default\n * Buy/View button — lets a host inject its own action (e.g. an owner\n * \"Cancel\" with an app-specific, auth-coupled handler) while keeping this\n * card's layout. Ignored for offers (which always render \"View asset\").\n */\n primaryAction?: React.ReactNode;\n compact?: boolean;\n /**\n * Pre-resolved, browser-loadable image URL. When provided (including null),\n * overrides the card's internal ipfsToHttp resolution — apps that proxy or\n * resize images (e.g. a same-origin /api/ipfs?w= proxy) pass it here.\n * Omit for the default gateway resolution; null renders the no-image fallback.\n */\n imageUrl?: string | null;\n /**\n * Pre-formatted USD equivalent of order.price (e.g. \"$12.34\"), computed by\n * the host from its own live rate feed — this package has no price-feed\n * access by design. Omit (or pass null) to render no USD line, e.g. when\n * the host has no live rate for this order's currency.\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 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 ? ipfsToHttp(order.token.image) : null);\n // Not resolved through ipfsToHttp — animation_url is legitimately a data: URI for\n // fully on-chain-rendered collections (ipfsToHttp rejects data: by design).\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 = `/asset/${order.nftContract}/${order.nftTokenId}`;\n\n // Show the action bar for listings (Buy/View) and for offers (View asset),\n // whenever the host wired any action or an overflow menu.\n const showActionBar = !!(onBuy || onCart || overflowMenu || primaryAction);\n\n // ─── Compact variant ──────────────────────────────────────────────────────\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 flex items-center justify-center bg-gradient-to-br from-brand-purple/15 to-brand-blue/15\">\n <span className=\"text-xl font-mono text-muted-foreground\">#{order.nftTokenId}</span>\n </div>\n }\n />\n </div>\n <div className=\"p-2.5 space-y-0.5\">\n <p className=\"text-xs font-semibold truncate\">{name}</p>\n {order.price?.formatted && (\n <p className=\"text-2xs font-bold price-value inline-flex items-center gap-1\">\n {order.price.currency && <CurrencyIcon symbol={order.price.currency} size={11} />}\n {formatDisplayPrice(order.price.formatted)}\n </p>\n )}\n {usdValue && <p className=\"text-2xs text-muted-foreground/70\">{usdValue}</p>}\n <p className=\"text-2xs text-muted-foreground\">{timeAgo(order.createdAt)}</p>\n </div>\n </Link>\n </MotionCard>\n );\n }\n\n // ─── Full variant ─────────────────────────────────────────────────────────\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 flex items-center justify-center bg-gradient-to-br from-brand-purple/15 to-brand-blue/15\">\n <span className=\"text-2xl font-mono text-muted-foreground\">#{order.nftTokenId}</span>\n </div>\n }\n />\n </div>\n\n <div className=\"p-3.5 space-y-3\">\n <div className=\"min-w-0\">\n <p className=\"font-semibold text-base truncate leading-snug\">{name}</p>\n <p className=\"text-2xs text-muted-foreground truncate leading-snug mt-0.5\">\n {order.token?.description ? order.token.description : `#${order.nftTokenId}`}\n </p>\n </div>\n\n {/* Price / Offer + age */}\n {order.price?.formatted && (\n <div className=\"flex items-end justify-between gap-2\">\n <div className=\"min-w-0\">\n <p className=\"text-2xs uppercase tracking-widest text-muted-foreground/55 leading-none\">\n {isListing ? \"Price\" : \"Offer\"}\n </p>\n <p className=\"text-lg font-bold price-value leading-none inline-flex items-center gap-1.5 mt-1.5\">\n {order.price.currency && <CurrencyIcon symbol={order.price.currency} size={18} />}\n {formatDisplayPrice(order.price.formatted)}\n <span className=\"sr-only\">{order.price.currency}</span>\n </p>\n {usdValue && <p className=\"text-2xs text-muted-foreground/60 mt-0.5\">{usdValue}</p>}\n </div>\n <p className=\"text-2xs text-muted-foreground/60 whitespace-nowrap shrink-0\">\n {timeAgo(order.createdAt)}\n </p>\n </div>\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":["Link"]}
@@ -23,8 +23,15 @@ interface ListingCardProps {
23
23
  * Omit for the default gateway resolution; null renders the no-image fallback.
24
24
  */
25
25
  imageUrl?: string | null;
26
+ /**
27
+ * Pre-formatted USD equivalent of order.price (e.g. "$12.34"), computed by
28
+ * the host from its own live rate feed — this package has no price-feed
29
+ * access by design. Omit (or pass null) to render no USD line, e.g. when
30
+ * the host has no live rate for this order's currency.
31
+ */
32
+ usdValue?: string | null;
26
33
  }
27
- declare function ListingCard({ order: rawOrder, inCart, onBuy, onCart, overflowMenu, primaryAction, compact, imageUrl }: ListingCardProps): react_jsx_runtime.JSX.Element;
34
+ declare function ListingCard({ order: rawOrder, inCart, onBuy, onCart, overflowMenu, primaryAction, compact, imageUrl, usdValue }: ListingCardProps): react_jsx_runtime.JSX.Element;
28
35
  declare function ListingCardSkeleton(): react_jsx_runtime.JSX.Element;
29
36
 
30
37
  export { ListingCard, type ListingCardProps, ListingCardSkeleton };
@@ -23,8 +23,15 @@ interface ListingCardProps {
23
23
  * Omit for the default gateway resolution; null renders the no-image fallback.
24
24
  */
25
25
  imageUrl?: string | null;
26
+ /**
27
+ * Pre-formatted USD equivalent of order.price (e.g. "$12.34"), computed by
28
+ * the host from its own live rate feed — this package has no price-feed
29
+ * access by design. Omit (or pass null) to render no USD line, e.g. when
30
+ * the host has no live rate for this order's currency.
31
+ */
32
+ usdValue?: string | null;
26
33
  }
27
- declare function ListingCard({ order: rawOrder, inCart, onBuy, onCart, overflowMenu, primaryAction, compact, imageUrl }: ListingCardProps): react_jsx_runtime.JSX.Element;
34
+ declare function ListingCard({ order: rawOrder, inCart, onBuy, onCart, overflowMenu, primaryAction, compact, imageUrl, usdValue }: ListingCardProps): react_jsx_runtime.JSX.Element;
28
35
  declare function ListingCardSkeleton(): react_jsx_runtime.JSX.Element;
29
36
 
30
37
  export { ListingCard, type ListingCardProps, ListingCardSkeleton };
@@ -11,7 +11,7 @@ import { ipfsToHttp } from "../utils/ipfs.js";
11
11
  import { formatDisplayPrice } from "../utils/format.js";
12
12
  import { timeAgo } from "../utils/time.js";
13
13
  import { isLivingRenderCollection } from "../data/living-render-collections.js";
14
- function ListingCard({ order: rawOrder, inCart = false, onBuy, onCart, overflowMenu, primaryAction, compact = false, imageUrl }) {
14
+ function ListingCard({ order: rawOrder, inCart = false, onBuy, onCart, overflowMenu, primaryAction, compact = false, imageUrl, usdValue }) {
15
15
  const order = rawOrder;
16
16
  const [imgError, setImgError] = useState(false);
17
17
  const isListing = order.offer.itemType === "ERC721" || order.offer.itemType === "ERC1155";
@@ -47,6 +47,7 @@ function ListingCard({ order: rawOrder, inCart = false, onBuy, onCart, overflowM
47
47
  order.price.currency && /* @__PURE__ */ jsx(CurrencyIcon, { symbol: order.price.currency, size: 11 }),
48
48
  formatDisplayPrice(order.price.formatted)
49
49
  ] }),
50
+ usdValue && /* @__PURE__ */ jsx("p", { className: "text-2xs text-muted-foreground/70", children: usdValue }),
50
51
  /* @__PURE__ */ jsx("p", { className: "text-2xs text-muted-foreground", children: timeAgo(order.createdAt) })
51
52
  ] })
52
53
  ] }) });
@@ -82,7 +83,8 @@ function ListingCard({ order: rawOrder, inCart = false, onBuy, onCart, overflowM
82
83
  order.price.currency && /* @__PURE__ */ jsx(CurrencyIcon, { symbol: order.price.currency, size: 18 }),
83
84
  formatDisplayPrice(order.price.formatted),
84
85
  /* @__PURE__ */ jsx("span", { className: "sr-only", children: order.price.currency })
85
- ] })
86
+ ] }),
87
+ usdValue && /* @__PURE__ */ jsx("p", { className: "text-2xs text-muted-foreground/60 mt-0.5", children: usdValue })
86
88
  ] }),
87
89
  /* @__PURE__ */ jsx("p", { className: "text-2xs text-muted-foreground/60 whitespace-nowrap shrink-0", children: timeAgo(order.createdAt) })
88
90
  ] }),
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/components/listing-card.tsx"],"sourcesContent":["\"use client\";\n\nimport { useState } from \"react\";\nimport Link from \"next/link\";\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 { ipfsToHttp } from \"../utils/ipfs.js\";\nimport { formatDisplayPrice } from \"../utils/format.js\";\nimport { timeAgo } from \"../utils/time.js\";\nimport { isLivingRenderCollection } from \"../data/living-render-collections.js\";\nimport type { ApiOrder, Chain } from \"@medialane/sdk\";\n\n/** ui's pinned SDK lags the apps — extend structurally for fields newer SDKs carry. */\ntype OrderWithAnimation = ApiOrder & { token: (ApiOrder[\"token\"] & { animationUrl?: string | null }) | null };\n\nexport interface ListingCardProps {\n order: ApiOrder;\n inCart?: boolean;\n onBuy?: (order: ApiOrder) => void;\n onCart?: (order: ApiOrder) => void;\n /** App passes a fully constructed <DropdownMenu> here */\n overflowMenu?: React.ReactNode;\n /**\n * Optional flex-1 primary control for listings, replacing the default\n * Buy/View button — lets a host inject its own action (e.g. an owner\n * \"Cancel\" with an app-specific, auth-coupled handler) while keeping this\n * card's layout. Ignored for offers (which always render \"View asset\").\n */\n primaryAction?: React.ReactNode;\n compact?: boolean;\n /**\n * Pre-resolved, browser-loadable image URL. When provided (including null),\n * overrides the card's internal ipfsToHttp resolution — apps that proxy or\n * resize images (e.g. a same-origin /api/ipfs?w= proxy) pass it here.\n * Omit for the default gateway resolution; null renders the no-image fallback.\n */\n imageUrl?: string | null;\n}\n\nexport function ListingCard({ order: rawOrder, inCart = false, onBuy, onCart, overflowMenu, primaryAction, compact = false, imageUrl }: ListingCardProps) {\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 ? ipfsToHttp(order.token.image) : null);\n // Not resolved through ipfsToHttp — animation_url is legitimately a data: URI for\n // fully on-chain-rendered collections (ipfsToHttp rejects data: by design).\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 = `/asset/${order.nftContract}/${order.nftTokenId}`;\n\n // Show the action bar for listings (Buy/View) and for offers (View asset),\n // whenever the host wired any action or an overflow menu.\n const showActionBar = !!(onBuy || onCart || overflowMenu || primaryAction);\n\n // ─── Compact variant ──────────────────────────────────────────────────────\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 flex items-center justify-center bg-gradient-to-br from-brand-purple/15 to-brand-blue/15\">\n <span className=\"text-xl font-mono text-muted-foreground\">#{order.nftTokenId}</span>\n </div>\n }\n />\n </div>\n <div className=\"p-2.5 space-y-0.5\">\n <p className=\"text-xs font-semibold truncate\">{name}</p>\n {order.price?.formatted && (\n <p className=\"text-2xs font-bold price-value inline-flex items-center gap-1\">\n {order.price.currency && <CurrencyIcon symbol={order.price.currency} size={11} />}\n {formatDisplayPrice(order.price.formatted)}\n </p>\n )}\n <p className=\"text-2xs text-muted-foreground\">{timeAgo(order.createdAt)}</p>\n </div>\n </Link>\n </MotionCard>\n );\n }\n\n // ─── Full variant ─────────────────────────────────────────────────────────\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 flex items-center justify-center bg-gradient-to-br from-brand-purple/15 to-brand-blue/15\">\n <span className=\"text-2xl font-mono text-muted-foreground\">#{order.nftTokenId}</span>\n </div>\n }\n />\n </div>\n\n <div className=\"p-3.5 space-y-3\">\n <div className=\"min-w-0\">\n <p className=\"font-semibold text-base truncate leading-snug\">{name}</p>\n <p className=\"text-2xs text-muted-foreground truncate leading-snug mt-0.5\">\n {order.token?.description ? order.token.description : `#${order.nftTokenId}`}\n </p>\n </div>\n\n {/* Price / Offer + age */}\n {order.price?.formatted && (\n <div className=\"flex items-end justify-between gap-2\">\n <div className=\"min-w-0\">\n <p className=\"text-2xs uppercase tracking-widest text-muted-foreground/55 leading-none\">\n {isListing ? \"Price\" : \"Offer\"}\n </p>\n <p className=\"text-lg font-bold price-value leading-none inline-flex items-center gap-1.5 mt-1.5\">\n {order.price.currency && <CurrencyIcon symbol={order.price.currency} size={18} />}\n {formatDisplayPrice(order.price.formatted)}\n <span className=\"sr-only\">{order.price.currency}</span>\n </p>\n </div>\n <p className=\"text-2xs text-muted-foreground/60 whitespace-nowrap shrink-0\">\n {timeAgo(order.createdAt)}\n </p>\n </div>\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 \"next/link\";\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 { ipfsToHttp } from \"../utils/ipfs.js\";\nimport { formatDisplayPrice } from \"../utils/format.js\";\nimport { timeAgo } from \"../utils/time.js\";\nimport { isLivingRenderCollection } from \"../data/living-render-collections.js\";\nimport type { ApiOrder, Chain } from \"@medialane/sdk\";\n\n/** ui's pinned SDK lags the apps — extend structurally for fields newer SDKs carry. */\ntype OrderWithAnimation = ApiOrder & { token: (ApiOrder[\"token\"] & { animationUrl?: string | null }) | null };\n\nexport interface ListingCardProps {\n order: ApiOrder;\n inCart?: boolean;\n onBuy?: (order: ApiOrder) => void;\n onCart?: (order: ApiOrder) => void;\n /** App passes a fully constructed <DropdownMenu> here */\n overflowMenu?: React.ReactNode;\n /**\n * Optional flex-1 primary control for listings, replacing the default\n * Buy/View button — lets a host inject its own action (e.g. an owner\n * \"Cancel\" with an app-specific, auth-coupled handler) while keeping this\n * card's layout. Ignored for offers (which always render \"View asset\").\n */\n primaryAction?: React.ReactNode;\n compact?: boolean;\n /**\n * Pre-resolved, browser-loadable image URL. When provided (including null),\n * overrides the card's internal ipfsToHttp resolution — apps that proxy or\n * resize images (e.g. a same-origin /api/ipfs?w= proxy) pass it here.\n * Omit for the default gateway resolution; null renders the no-image fallback.\n */\n imageUrl?: string | null;\n /**\n * Pre-formatted USD equivalent of order.price (e.g. \"$12.34\"), computed by\n * the host from its own live rate feed — this package has no price-feed\n * access by design. Omit (or pass null) to render no USD line, e.g. when\n * the host has no live rate for this order's currency.\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 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 ? ipfsToHttp(order.token.image) : null);\n // Not resolved through ipfsToHttp — animation_url is legitimately a data: URI for\n // fully on-chain-rendered collections (ipfsToHttp rejects data: by design).\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 = `/asset/${order.nftContract}/${order.nftTokenId}`;\n\n // Show the action bar for listings (Buy/View) and for offers (View asset),\n // whenever the host wired any action or an overflow menu.\n const showActionBar = !!(onBuy || onCart || overflowMenu || primaryAction);\n\n // ─── Compact variant ──────────────────────────────────────────────────────\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 flex items-center justify-center bg-gradient-to-br from-brand-purple/15 to-brand-blue/15\">\n <span className=\"text-xl font-mono text-muted-foreground\">#{order.nftTokenId}</span>\n </div>\n }\n />\n </div>\n <div className=\"p-2.5 space-y-0.5\">\n <p className=\"text-xs font-semibold truncate\">{name}</p>\n {order.price?.formatted && (\n <p className=\"text-2xs font-bold price-value inline-flex items-center gap-1\">\n {order.price.currency && <CurrencyIcon symbol={order.price.currency} size={11} />}\n {formatDisplayPrice(order.price.formatted)}\n </p>\n )}\n {usdValue && <p className=\"text-2xs text-muted-foreground/70\">{usdValue}</p>}\n <p className=\"text-2xs text-muted-foreground\">{timeAgo(order.createdAt)}</p>\n </div>\n </Link>\n </MotionCard>\n );\n }\n\n // ─── Full variant ─────────────────────────────────────────────────────────\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 flex items-center justify-center bg-gradient-to-br from-brand-purple/15 to-brand-blue/15\">\n <span className=\"text-2xl font-mono text-muted-foreground\">#{order.nftTokenId}</span>\n </div>\n }\n />\n </div>\n\n <div className=\"p-3.5 space-y-3\">\n <div className=\"min-w-0\">\n <p className=\"font-semibold text-base truncate leading-snug\">{name}</p>\n <p className=\"text-2xs text-muted-foreground truncate leading-snug mt-0.5\">\n {order.token?.description ? order.token.description : `#${order.nftTokenId}`}\n </p>\n </div>\n\n {/* Price / Offer + age */}\n {order.price?.formatted && (\n <div className=\"flex items-end justify-between gap-2\">\n <div className=\"min-w-0\">\n <p className=\"text-2xs uppercase tracking-widest text-muted-foreground/55 leading-none\">\n {isListing ? \"Price\" : \"Offer\"}\n </p>\n <p className=\"text-lg font-bold price-value leading-none inline-flex items-center gap-1.5 mt-1.5\">\n {order.price.currency && <CurrencyIcon symbol={order.price.currency} size={18} />}\n {formatDisplayPrice(order.price.formatted)}\n <span className=\"sr-only\">{order.price.currency}</span>\n </p>\n {usdValue && <p className=\"text-2xs text-muted-foreground/60 mt-0.5\">{usdValue}</p>}\n </div>\n <p className=\"text-2xs text-muted-foreground/60 whitespace-nowrap shrink-0\">\n {timeAgo(order.createdAt)}\n </p>\n </div>\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":[]}
@@ -0,0 +1,91 @@
1
+ "use strict";
2
+ "use client";
3
+ var __create = Object.create;
4
+ var __defProp = Object.defineProperty;
5
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
6
+ var __getOwnPropNames = Object.getOwnPropertyNames;
7
+ var __getProtoOf = Object.getPrototypeOf;
8
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
9
+ var __export = (target, all) => {
10
+ for (var name in all)
11
+ __defProp(target, name, { get: all[name], enumerable: true });
12
+ };
13
+ var __copyProps = (to, from, except, desc) => {
14
+ if (from && typeof from === "object" || typeof from === "function") {
15
+ for (let key of __getOwnPropNames(from))
16
+ if (!__hasOwnProp.call(to, key) && key !== except)
17
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
18
+ }
19
+ return to;
20
+ };
21
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
22
+ // If the importer is in node compatibility mode or this is not an ESM
23
+ // file that has been converted to a CommonJS file using a Babel-
24
+ // compatible transform (i.e. "__esModule" has not been set), then set
25
+ // "default" to the CommonJS "module.exports" for node compatibility.
26
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
27
+ mod
28
+ ));
29
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
30
+ var remixes_tab_exports = {};
31
+ __export(remixes_tab_exports, {
32
+ RemixesTab: () => RemixesTab
33
+ });
34
+ module.exports = __toCommonJS(remixes_tab_exports);
35
+ var import_jsx_runtime = require("react/jsx-runtime");
36
+ var import_link = __toESM(require("next/link"), 1);
37
+ var import_sdk = require("@medialane/sdk");
38
+ var import_lucide_react = require("lucide-react");
39
+ var import_use_remix_offers = require("../utils/use-remix-offers.js");
40
+ var import_skeleton = require("./skeleton.js");
41
+ function RemixesTab({ apiConfig, contractAddress, tokenId }) {
42
+ const { remixes, total, isLoading } = (0, import_use_remix_offers.useTokenRemixes)(apiConfig, contractAddress, tokenId);
43
+ if (isLoading) {
44
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "grid grid-cols-2 sm:grid-cols-3 gap-3", children: Array.from({ length: 3 }).map((_, i) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_skeleton.Skeleton, { className: "aspect-square rounded-xl" }, i)) });
45
+ }
46
+ if (remixes.length === 0) {
47
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex flex-col items-center gap-3 py-12 text-center", children: [
48
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "h-12 w-12 rounded-full bg-primary/10 flex items-center justify-center", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_lucide_react.GitBranch, { className: "h-5 w-5 text-primary" }) }),
49
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "text-sm font-medium", children: "No remixes yet" }),
50
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "text-xs text-muted-foreground", children: "Remix this asset to create the first derivative work." })
51
+ ] });
52
+ }
53
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "space-y-3", children: [
54
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("p", { className: "text-xs text-muted-foreground", children: [
55
+ total,
56
+ " remix",
57
+ total !== 1 ? "es" : "",
58
+ " of this asset"
59
+ ] }),
60
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "grid grid-cols-2 sm:grid-cols-3 gap-3", children: remixes.map(
61
+ (remix) => remix.remixContract && remix.remixTokenId ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
62
+ import_link.default,
63
+ {
64
+ href: (0, import_sdk.assetHref)("STARKNET", remix.remixContract, remix.remixTokenId),
65
+ className: "group relative block",
66
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "card-base p-3 space-y-1 hover:border-primary/40 transition-colors", children: [
67
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex items-center gap-1.5 text-xs text-muted-foreground", children: [
68
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_lucide_react.GitBranch, { className: "h-3 w-3 text-primary" }),
69
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "text-primary font-medium", children: "Remix" })
70
+ ] }),
71
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("p", { className: "text-xs tabular-nums truncate", children: [
72
+ "#",
73
+ remix.remixTokenId
74
+ ] }),
75
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "text-[10px] text-muted-foreground", children: remix.licenseType }),
76
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex gap-1 flex-wrap", children: [
77
+ remix.commercial && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "text-[9px] px-1.5 py-0.5 rounded-full bg-brand-blue/10 text-brand-blue border border-brand-blue/20", children: "Commercial" }),
78
+ remix.derivatives && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "text-[9px] px-1.5 py-0.5 rounded-full bg-primary/10 text-primary border border-primary/20", children: "Derivatives" })
79
+ ] })
80
+ ] })
81
+ },
82
+ remix.id
83
+ ) : null
84
+ ) })
85
+ ] });
86
+ }
87
+ // Annotate the CommonJS export names for ESM import in node:
88
+ 0 && (module.exports = {
89
+ RemixesTab
90
+ });
91
+ //# sourceMappingURL=remixes-tab.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/components/remixes-tab.tsx"],"sourcesContent":["\"use client\";\n\nimport Link from \"next/link\";\nimport { assetHref } from \"@medialane/sdk\";\nimport { GitBranch } from \"lucide-react\";\nimport { useTokenRemixes } from \"../utils/use-remix-offers.js\";\nimport { Skeleton } from \"./skeleton.js\";\nimport type { ApiFetchConfig } from \"../utils/api-fetch.js\";\n\nexport interface RemixesTabProps {\n apiConfig: ApiFetchConfig;\n contractAddress: string;\n tokenId: string;\n}\n\nexport function RemixesTab({ apiConfig, contractAddress, tokenId }: RemixesTabProps) {\n const { remixes, total, isLoading } = useTokenRemixes(apiConfig, contractAddress, tokenId);\n\n if (isLoading) {\n return (\n <div className=\"grid grid-cols-2 sm:grid-cols-3 gap-3\">\n {Array.from({ length: 3 }).map((_, i) => (\n <Skeleton key={i} className=\"aspect-square rounded-xl\" />\n ))}\n </div>\n );\n }\n\n if (remixes.length === 0) {\n return (\n <div className=\"flex flex-col items-center gap-3 py-12 text-center\">\n <div className=\"h-12 w-12 rounded-full bg-primary/10 flex items-center justify-center\">\n <GitBranch className=\"h-5 w-5 text-primary\" />\n </div>\n <p className=\"text-sm font-medium\">No remixes yet</p>\n <p className=\"text-xs text-muted-foreground\">Remix this asset to create the first derivative work.</p>\n </div>\n );\n }\n\n return (\n <div className=\"space-y-3\">\n <p className=\"text-xs text-muted-foreground\">{total} remix{total !== 1 ? \"es\" : \"\"} of this asset</p>\n <div className=\"grid grid-cols-2 sm:grid-cols-3 gap-3\">\n {remixes.map((remix) =>\n remix.remixContract && remix.remixTokenId ? (\n <Link\n key={remix.id}\n href={assetHref(\"STARKNET\", remix.remixContract, remix.remixTokenId)}\n className=\"group relative block\"\n >\n <div className=\"card-base p-3 space-y-1 hover:border-primary/40 transition-colors\">\n <div className=\"flex items-center gap-1.5 text-xs text-muted-foreground\">\n <GitBranch className=\"h-3 w-3 text-primary\" />\n <span className=\"text-primary font-medium\">Remix</span>\n </div>\n <p className=\"text-xs tabular-nums truncate\">#{remix.remixTokenId}</p>\n <p className=\"text-[10px] text-muted-foreground\">{remix.licenseType}</p>\n <div className=\"flex gap-1 flex-wrap\">\n {remix.commercial && (\n <span className=\"text-[9px] px-1.5 py-0.5 rounded-full bg-brand-blue/10 text-brand-blue border border-brand-blue/20\">Commercial</span>\n )}\n {remix.derivatives && (\n <span className=\"text-[9px] px-1.5 py-0.5 rounded-full bg-primary/10 text-primary border border-primary/20\">Derivatives</span>\n )}\n </div>\n </div>\n </Link>\n ) : null\n )}\n </div>\n </div>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAsBU;AApBV,kBAAiB;AACjB,iBAA0B;AAC1B,0BAA0B;AAC1B,8BAAgC;AAChC,sBAAyB;AASlB,SAAS,WAAW,EAAE,WAAW,iBAAiB,QAAQ,GAAoB;AACnF,QAAM,EAAE,SAAS,OAAO,UAAU,QAAI,yCAAgB,WAAW,iBAAiB,OAAO;AAEzF,MAAI,WAAW;AACb,WACE,4CAAC,SAAI,WAAU,yCACZ,gBAAM,KAAK,EAAE,QAAQ,EAAE,CAAC,EAAE,IAAI,CAAC,GAAG,MACjC,4CAAC,4BAAiB,WAAU,8BAAb,CAAwC,CACxD,GACH;AAAA,EAEJ;AAEA,MAAI,QAAQ,WAAW,GAAG;AACxB,WACE,6CAAC,SAAI,WAAU,sDACb;AAAA,kDAAC,SAAI,WAAU,yEACb,sDAAC,iCAAU,WAAU,wBAAuB,GAC9C;AAAA,MACA,4CAAC,OAAE,WAAU,uBAAsB,4BAAc;AAAA,MACjD,4CAAC,OAAE,WAAU,iCAAgC,mEAAqD;AAAA,OACpG;AAAA,EAEJ;AAEA,SACE,6CAAC,SAAI,WAAU,aACb;AAAA,iDAAC,OAAE,WAAU,iCAAiC;AAAA;AAAA,MAAM;AAAA,MAAO,UAAU,IAAI,OAAO;AAAA,MAAG;AAAA,OAAc;AAAA,IACjG,4CAAC,SAAI,WAAU,yCACZ,kBAAQ;AAAA,MAAI,CAAC,UACZ,MAAM,iBAAiB,MAAM,eAC3B;AAAA,QAAC,YAAAA;AAAA,QAAA;AAAA,UAEC,UAAM,sBAAU,YAAY,MAAM,eAAe,MAAM,YAAY;AAAA,UACnE,WAAU;AAAA,UAEV,uDAAC,SAAI,WAAU,qEACb;AAAA,yDAAC,SAAI,WAAU,2DACb;AAAA,0DAAC,iCAAU,WAAU,wBAAuB;AAAA,cAC5C,4CAAC,UAAK,WAAU,4BAA2B,mBAAK;AAAA,eAClD;AAAA,YACA,6CAAC,OAAE,WAAU,iCAAgC;AAAA;AAAA,cAAE,MAAM;AAAA,eAAa;AAAA,YAClE,4CAAC,OAAE,WAAU,qCAAqC,gBAAM,aAAY;AAAA,YACpE,6CAAC,SAAI,WAAU,wBACZ;AAAA,oBAAM,cACL,4CAAC,UAAK,WAAU,sGAAqG,wBAAU;AAAA,cAEhI,MAAM,eACL,4CAAC,UAAK,WAAU,6FAA4F,yBAAW;AAAA,eAE3H;AAAA,aACF;AAAA;AAAA,QAnBK,MAAM;AAAA,MAoBb,IACE;AAAA,IACN,GACF;AAAA,KACF;AAEJ;","names":["Link"]}
@@ -0,0 +1,11 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+ import { ApiFetchConfig } from '../utils/api-fetch.cjs';
3
+
4
+ interface RemixesTabProps {
5
+ apiConfig: ApiFetchConfig;
6
+ contractAddress: string;
7
+ tokenId: string;
8
+ }
9
+ declare function RemixesTab({ apiConfig, contractAddress, tokenId }: RemixesTabProps): react_jsx_runtime.JSX.Element;
10
+
11
+ export { RemixesTab, type RemixesTabProps };
@@ -0,0 +1,11 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+ import { ApiFetchConfig } from '../utils/api-fetch.js';
3
+
4
+ interface RemixesTabProps {
5
+ apiConfig: ApiFetchConfig;
6
+ contractAddress: string;
7
+ tokenId: string;
8
+ }
9
+ declare function RemixesTab({ apiConfig, contractAddress, tokenId }: RemixesTabProps): react_jsx_runtime.JSX.Element;
10
+
11
+ export { RemixesTab, type RemixesTabProps };
@@ -0,0 +1,57 @@
1
+ "use client";
2
+ import { jsx, jsxs } from "react/jsx-runtime";
3
+ import Link from "next/link";
4
+ import { assetHref } from "@medialane/sdk";
5
+ import { GitBranch } from "lucide-react";
6
+ import { useTokenRemixes } from "../utils/use-remix-offers.js";
7
+ import { Skeleton } from "./skeleton.js";
8
+ function RemixesTab({ apiConfig, contractAddress, tokenId }) {
9
+ const { remixes, total, isLoading } = useTokenRemixes(apiConfig, contractAddress, tokenId);
10
+ if (isLoading) {
11
+ return /* @__PURE__ */ jsx("div", { className: "grid grid-cols-2 sm:grid-cols-3 gap-3", children: Array.from({ length: 3 }).map((_, i) => /* @__PURE__ */ jsx(Skeleton, { className: "aspect-square rounded-xl" }, i)) });
12
+ }
13
+ if (remixes.length === 0) {
14
+ return /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center gap-3 py-12 text-center", children: [
15
+ /* @__PURE__ */ jsx("div", { className: "h-12 w-12 rounded-full bg-primary/10 flex items-center justify-center", children: /* @__PURE__ */ jsx(GitBranch, { className: "h-5 w-5 text-primary" }) }),
16
+ /* @__PURE__ */ jsx("p", { className: "text-sm font-medium", children: "No remixes yet" }),
17
+ /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: "Remix this asset to create the first derivative work." })
18
+ ] });
19
+ }
20
+ return /* @__PURE__ */ jsxs("div", { className: "space-y-3", children: [
21
+ /* @__PURE__ */ jsxs("p", { className: "text-xs text-muted-foreground", children: [
22
+ total,
23
+ " remix",
24
+ total !== 1 ? "es" : "",
25
+ " of this asset"
26
+ ] }),
27
+ /* @__PURE__ */ jsx("div", { className: "grid grid-cols-2 sm:grid-cols-3 gap-3", children: remixes.map(
28
+ (remix) => remix.remixContract && remix.remixTokenId ? /* @__PURE__ */ jsx(
29
+ Link,
30
+ {
31
+ href: assetHref("STARKNET", remix.remixContract, remix.remixTokenId),
32
+ className: "group relative block",
33
+ children: /* @__PURE__ */ jsxs("div", { className: "card-base p-3 space-y-1 hover:border-primary/40 transition-colors", children: [
34
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1.5 text-xs text-muted-foreground", children: [
35
+ /* @__PURE__ */ jsx(GitBranch, { className: "h-3 w-3 text-primary" }),
36
+ /* @__PURE__ */ jsx("span", { className: "text-primary font-medium", children: "Remix" })
37
+ ] }),
38
+ /* @__PURE__ */ jsxs("p", { className: "text-xs tabular-nums truncate", children: [
39
+ "#",
40
+ remix.remixTokenId
41
+ ] }),
42
+ /* @__PURE__ */ jsx("p", { className: "text-[10px] text-muted-foreground", children: remix.licenseType }),
43
+ /* @__PURE__ */ jsxs("div", { className: "flex gap-1 flex-wrap", children: [
44
+ remix.commercial && /* @__PURE__ */ jsx("span", { className: "text-[9px] px-1.5 py-0.5 rounded-full bg-brand-blue/10 text-brand-blue border border-brand-blue/20", children: "Commercial" }),
45
+ remix.derivatives && /* @__PURE__ */ jsx("span", { className: "text-[9px] px-1.5 py-0.5 rounded-full bg-primary/10 text-primary border border-primary/20", children: "Derivatives" })
46
+ ] })
47
+ ] })
48
+ },
49
+ remix.id
50
+ ) : null
51
+ ) })
52
+ ] });
53
+ }
54
+ export {
55
+ RemixesTab
56
+ };
57
+ //# sourceMappingURL=remixes-tab.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/components/remixes-tab.tsx"],"sourcesContent":["\"use client\";\n\nimport Link from \"next/link\";\nimport { assetHref } from \"@medialane/sdk\";\nimport { GitBranch } from \"lucide-react\";\nimport { useTokenRemixes } from \"../utils/use-remix-offers.js\";\nimport { Skeleton } from \"./skeleton.js\";\nimport type { ApiFetchConfig } from \"../utils/api-fetch.js\";\n\nexport interface RemixesTabProps {\n apiConfig: ApiFetchConfig;\n contractAddress: string;\n tokenId: string;\n}\n\nexport function RemixesTab({ apiConfig, contractAddress, tokenId }: RemixesTabProps) {\n const { remixes, total, isLoading } = useTokenRemixes(apiConfig, contractAddress, tokenId);\n\n if (isLoading) {\n return (\n <div className=\"grid grid-cols-2 sm:grid-cols-3 gap-3\">\n {Array.from({ length: 3 }).map((_, i) => (\n <Skeleton key={i} className=\"aspect-square rounded-xl\" />\n ))}\n </div>\n );\n }\n\n if (remixes.length === 0) {\n return (\n <div className=\"flex flex-col items-center gap-3 py-12 text-center\">\n <div className=\"h-12 w-12 rounded-full bg-primary/10 flex items-center justify-center\">\n <GitBranch className=\"h-5 w-5 text-primary\" />\n </div>\n <p className=\"text-sm font-medium\">No remixes yet</p>\n <p className=\"text-xs text-muted-foreground\">Remix this asset to create the first derivative work.</p>\n </div>\n );\n }\n\n return (\n <div className=\"space-y-3\">\n <p className=\"text-xs text-muted-foreground\">{total} remix{total !== 1 ? \"es\" : \"\"} of this asset</p>\n <div className=\"grid grid-cols-2 sm:grid-cols-3 gap-3\">\n {remixes.map((remix) =>\n remix.remixContract && remix.remixTokenId ? (\n <Link\n key={remix.id}\n href={assetHref(\"STARKNET\", remix.remixContract, remix.remixTokenId)}\n className=\"group relative block\"\n >\n <div className=\"card-base p-3 space-y-1 hover:border-primary/40 transition-colors\">\n <div className=\"flex items-center gap-1.5 text-xs text-muted-foreground\">\n <GitBranch className=\"h-3 w-3 text-primary\" />\n <span className=\"text-primary font-medium\">Remix</span>\n </div>\n <p className=\"text-xs tabular-nums truncate\">#{remix.remixTokenId}</p>\n <p className=\"text-[10px] text-muted-foreground\">{remix.licenseType}</p>\n <div className=\"flex gap-1 flex-wrap\">\n {remix.commercial && (\n <span className=\"text-[9px] px-1.5 py-0.5 rounded-full bg-brand-blue/10 text-brand-blue border border-brand-blue/20\">Commercial</span>\n )}\n {remix.derivatives && (\n <span className=\"text-[9px] px-1.5 py-0.5 rounded-full bg-primary/10 text-primary border border-primary/20\">Derivatives</span>\n )}\n </div>\n </div>\n </Link>\n ) : null\n )}\n </div>\n </div>\n );\n}\n"],"mappings":";AAsBU,cAQJ,YARI;AApBV,OAAO,UAAU;AACjB,SAAS,iBAAiB;AAC1B,SAAS,iBAAiB;AAC1B,SAAS,uBAAuB;AAChC,SAAS,gBAAgB;AASlB,SAAS,WAAW,EAAE,WAAW,iBAAiB,QAAQ,GAAoB;AACnF,QAAM,EAAE,SAAS,OAAO,UAAU,IAAI,gBAAgB,WAAW,iBAAiB,OAAO;AAEzF,MAAI,WAAW;AACb,WACE,oBAAC,SAAI,WAAU,yCACZ,gBAAM,KAAK,EAAE,QAAQ,EAAE,CAAC,EAAE,IAAI,CAAC,GAAG,MACjC,oBAAC,YAAiB,WAAU,8BAAb,CAAwC,CACxD,GACH;AAAA,EAEJ;AAEA,MAAI,QAAQ,WAAW,GAAG;AACxB,WACE,qBAAC,SAAI,WAAU,sDACb;AAAA,0BAAC,SAAI,WAAU,yEACb,8BAAC,aAAU,WAAU,wBAAuB,GAC9C;AAAA,MACA,oBAAC,OAAE,WAAU,uBAAsB,4BAAc;AAAA,MACjD,oBAAC,OAAE,WAAU,iCAAgC,mEAAqD;AAAA,OACpG;AAAA,EAEJ;AAEA,SACE,qBAAC,SAAI,WAAU,aACb;AAAA,yBAAC,OAAE,WAAU,iCAAiC;AAAA;AAAA,MAAM;AAAA,MAAO,UAAU,IAAI,OAAO;AAAA,MAAG;AAAA,OAAc;AAAA,IACjG,oBAAC,SAAI,WAAU,yCACZ,kBAAQ;AAAA,MAAI,CAAC,UACZ,MAAM,iBAAiB,MAAM,eAC3B;AAAA,QAAC;AAAA;AAAA,UAEC,MAAM,UAAU,YAAY,MAAM,eAAe,MAAM,YAAY;AAAA,UACnE,WAAU;AAAA,UAEV,+BAAC,SAAI,WAAU,qEACb;AAAA,iCAAC,SAAI,WAAU,2DACb;AAAA,kCAAC,aAAU,WAAU,wBAAuB;AAAA,cAC5C,oBAAC,UAAK,WAAU,4BAA2B,mBAAK;AAAA,eAClD;AAAA,YACA,qBAAC,OAAE,WAAU,iCAAgC;AAAA;AAAA,cAAE,MAAM;AAAA,eAAa;AAAA,YAClE,oBAAC,OAAE,WAAU,qCAAqC,gBAAM,aAAY;AAAA,YACpE,qBAAC,SAAI,WAAU,wBACZ;AAAA,oBAAM,cACL,oBAAC,UAAK,WAAU,sGAAqG,wBAAU;AAAA,cAEhI,MAAM,eACL,oBAAC,UAAK,WAAU,6FAA4F,yBAAW;AAAA,eAE3H;AAAA,aACF;AAAA;AAAA,QAnBK,MAAM;AAAA,MAoBb,IACE;AAAA,IACN,GACF;AAAA,KACF;AAEJ;","names":[]}
@@ -0,0 +1,17 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __copyProps = (to, from, except, desc) => {
7
+ if (from && typeof from === "object" || typeof from === "function") {
8
+ for (let key of __getOwnPropNames(from))
9
+ if (!__hasOwnProp.call(to, key) && key !== except)
10
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
11
+ }
12
+ return to;
13
+ };
14
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
15
+ var notification_exports = {};
16
+ module.exports = __toCommonJS(notification_exports);
17
+ //# sourceMappingURL=notification.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/data/notification.ts"],"sourcesContent":["export type NotificationType =\n | \"offer\"\n | \"offer_accepted\"\n | \"sale\"\n | \"listing\"\n | \"mint\"\n | \"transfer\"\n | \"asset_received\"\n | \"cancelled\"\n | \"announcement\";\n\n/** \"spotlight\" notifications open the full-attention panel on login; \"normal\" go to the feed only. */\nexport type NotificationPriority = \"normal\" | \"spotlight\";\n\nexport interface Notification {\n id: string;\n type: NotificationType;\n title: string;\n description: string;\n image: string | null;\n href: string;\n timestamp: string;\n isUnread: boolean;\n priority: NotificationPriority;\n /** True for positive outcomes that trigger confetti in the spotlight panel. */\n celebratory?: boolean;\n /** Extra structured data for rich spotlight rendering. */\n metadata?: {\n amount?: string;\n currency?: string;\n txHash?: string;\n assetName?: string;\n };\n}\n\nexport interface Announcement {\n id: string;\n title: string;\n body: string;\n image: string | null;\n href: string;\n created_at: string;\n pinned?: boolean;\n}\n"],"mappings":";;;;;;;;;;;;;;AAAA;AAAA;","names":[]}
@@ -0,0 +1,34 @@
1
+ type NotificationType = "offer" | "offer_accepted" | "sale" | "listing" | "mint" | "transfer" | "asset_received" | "cancelled" | "announcement";
2
+ /** "spotlight" notifications open the full-attention panel on login; "normal" go to the feed only. */
3
+ type NotificationPriority = "normal" | "spotlight";
4
+ interface Notification {
5
+ id: string;
6
+ type: NotificationType;
7
+ title: string;
8
+ description: string;
9
+ image: string | null;
10
+ href: string;
11
+ timestamp: string;
12
+ isUnread: boolean;
13
+ priority: NotificationPriority;
14
+ /** True for positive outcomes that trigger confetti in the spotlight panel. */
15
+ celebratory?: boolean;
16
+ /** Extra structured data for rich spotlight rendering. */
17
+ metadata?: {
18
+ amount?: string;
19
+ currency?: string;
20
+ txHash?: string;
21
+ assetName?: string;
22
+ };
23
+ }
24
+ interface Announcement {
25
+ id: string;
26
+ title: string;
27
+ body: string;
28
+ image: string | null;
29
+ href: string;
30
+ created_at: string;
31
+ pinned?: boolean;
32
+ }
33
+
34
+ export type { Announcement, Notification, NotificationPriority, NotificationType };
@@ -0,0 +1,34 @@
1
+ type NotificationType = "offer" | "offer_accepted" | "sale" | "listing" | "mint" | "transfer" | "asset_received" | "cancelled" | "announcement";
2
+ /** "spotlight" notifications open the full-attention panel on login; "normal" go to the feed only. */
3
+ type NotificationPriority = "normal" | "spotlight";
4
+ interface Notification {
5
+ id: string;
6
+ type: NotificationType;
7
+ title: string;
8
+ description: string;
9
+ image: string | null;
10
+ href: string;
11
+ timestamp: string;
12
+ isUnread: boolean;
13
+ priority: NotificationPriority;
14
+ /** True for positive outcomes that trigger confetti in the spotlight panel. */
15
+ celebratory?: boolean;
16
+ /** Extra structured data for rich spotlight rendering. */
17
+ metadata?: {
18
+ amount?: string;
19
+ currency?: string;
20
+ txHash?: string;
21
+ assetName?: string;
22
+ };
23
+ }
24
+ interface Announcement {
25
+ id: string;
26
+ title: string;
27
+ body: string;
28
+ image: string | null;
29
+ href: string;
30
+ created_at: string;
31
+ pinned?: boolean;
32
+ }
33
+
34
+ export type { Announcement, Notification, NotificationPriority, NotificationType };
@@ -0,0 +1 @@
1
+ //# sourceMappingURL=notification.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}