@medialane/ui 0.99.1 → 0.100.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.
- package/README.md +19 -0
- package/dist/components/action-button.cjs +1 -3
- package/dist/components/action-button.cjs.map +1 -1
- package/dist/components/action-button.js +1 -3
- package/dist/components/action-button.js.map +1 -1
- package/dist/components/activity-card.cjs +3 -3
- package/dist/components/activity-card.cjs.map +1 -1
- package/dist/components/activity-card.js +3 -3
- package/dist/components/activity-card.js.map +1 -1
- package/dist/components/activity-feed-shell.cjs +1 -1
- package/dist/components/activity-feed-shell.cjs.map +1 -1
- package/dist/components/activity-feed-shell.js +1 -1
- package/dist/components/activity-feed-shell.js.map +1 -1
- package/dist/components/activity-row.cjs +3 -3
- package/dist/components/activity-row.cjs.map +1 -1
- package/dist/components/activity-row.js +3 -3
- package/dist/components/activity-row.js.map +1 -1
- package/dist/components/activity-ticker.cjs +1 -1
- package/dist/components/activity-ticker.cjs.map +1 -1
- package/dist/components/activity-ticker.js +1 -1
- package/dist/components/activity-ticker.js.map +1 -1
- package/dist/components/asset-card.cjs +2 -2
- package/dist/components/asset-card.cjs.map +1 -1
- package/dist/components/asset-card.js +2 -2
- package/dist/components/asset-card.js.map +1 -1
- package/dist/components/asset-license-summary.cjs +2 -2
- package/dist/components/asset-license-summary.cjs.map +1 -1
- package/dist/components/asset-license-summary.js +2 -2
- package/dist/components/asset-license-summary.js.map +1 -1
- package/dist/components/asset-overview-content.cjs +2 -2
- package/dist/components/asset-overview-content.cjs.map +1 -1
- package/dist/components/asset-overview-content.js +2 -2
- package/dist/components/asset-overview-content.js.map +1 -1
- package/dist/components/asset-picker-cell.cjs +1 -1
- package/dist/components/asset-picker-cell.cjs.map +1 -1
- package/dist/components/asset-picker-cell.js +1 -1
- package/dist/components/asset-picker-cell.js.map +1 -1
- package/dist/components/asset-top-sections.cjs +1 -1
- package/dist/components/asset-top-sections.cjs.map +1 -1
- package/dist/components/asset-top-sections.js +1 -1
- package/dist/components/asset-top-sections.js.map +1 -1
- package/dist/components/asset-utility-icons.cjs +2 -2
- package/dist/components/asset-utility-icons.cjs.map +1 -1
- package/dist/components/asset-utility-icons.js +2 -2
- package/dist/components/asset-utility-icons.js.map +1 -1
- package/dist/components/claim-rail.cjs +1 -1
- package/dist/components/claim-rail.cjs.map +1 -1
- package/dist/components/claim-rail.js +1 -1
- package/dist/components/claim-rail.js.map +1 -1
- package/dist/components/coin-card.cjs +4 -4
- package/dist/components/coin-card.cjs.map +1 -1
- package/dist/components/coin-card.js +4 -4
- package/dist/components/coin-card.js.map +1 -1
- package/dist/components/coin-launch-preview.cjs +5 -5
- package/dist/components/coin-launch-preview.cjs.map +1 -1
- package/dist/components/coin-launch-preview.js +5 -5
- package/dist/components/coin-launch-preview.js.map +1 -1
- package/dist/components/coins-explorer.cjs +3 -3
- package/dist/components/coins-explorer.cjs.map +1 -1
- package/dist/components/coins-explorer.js +3 -3
- package/dist/components/coins-explorer.js.map +1 -1
- package/dist/components/collection-card.cjs +6 -6
- package/dist/components/collection-card.cjs.map +1 -1
- package/dist/components/collection-card.js +6 -6
- package/dist/components/collection-card.js.map +1 -1
- package/dist/components/collection-filters.cjs +5 -5
- package/dist/components/collection-filters.cjs.map +1 -1
- package/dist/components/collection-filters.js +5 -5
- package/dist/components/collection-filters.js.map +1 -1
- package/dist/components/community-rewards-section.cjs +2 -2
- package/dist/components/community-rewards-section.cjs.map +1 -1
- package/dist/components/community-rewards-section.js +2 -2
- package/dist/components/community-rewards-section.js.map +1 -1
- package/dist/components/discover-feed-section.cjs +1 -1
- package/dist/components/discover-feed-section.cjs.map +1 -1
- package/dist/components/discover-feed-section.js +1 -1
- package/dist/components/discover-feed-section.js.map +1 -1
- package/dist/components/featured-carousel.cjs +4 -4
- package/dist/components/featured-carousel.cjs.map +1 -1
- package/dist/components/featured-carousel.js +4 -4
- package/dist/components/featured-carousel.js.map +1 -1
- package/dist/components/gradient-button.cjs +1 -1
- package/dist/components/gradient-button.cjs.map +1 -1
- package/dist/components/gradient-button.js +1 -1
- package/dist/components/gradient-button.js.map +1 -1
- package/dist/components/hero-slider.cjs +2 -2
- package/dist/components/hero-slider.cjs.map +1 -1
- package/dist/components/hero-slider.js +2 -2
- package/dist/components/hero-slider.js.map +1 -1
- package/dist/components/launchpad-filter-bar.cjs +1 -1
- package/dist/components/launchpad-filter-bar.cjs.map +1 -1
- package/dist/components/launchpad-filter-bar.js +1 -1
- package/dist/components/launchpad-filter-bar.js.map +1 -1
- package/dist/components/launchpad-services.cjs +5 -5
- package/dist/components/launchpad-services.cjs.map +1 -1
- package/dist/components/launchpad-services.js +5 -5
- package/dist/components/launchpad-services.js.map +1 -1
- package/dist/components/launchpad-strip.cjs +4 -4
- package/dist/components/launchpad-strip.cjs.map +1 -1
- package/dist/components/launchpad-strip.js +4 -4
- package/dist/components/launchpad-strip.js.map +1 -1
- package/dist/components/listing-card.cjs +7 -7
- package/dist/components/listing-card.cjs.map +1 -1
- package/dist/components/listing-card.js +7 -7
- package/dist/components/listing-card.js.map +1 -1
- package/dist/components/medialane-collection-card.cjs +2 -2
- package/dist/components/medialane-collection-card.cjs.map +1 -1
- package/dist/components/medialane-collection-card.js +2 -2
- package/dist/components/medialane-collection-card.js.map +1 -1
- package/dist/components/nav-command-menu.cjs +5 -5
- package/dist/components/nav-command-menu.cjs.map +1 -1
- package/dist/components/nav-command-menu.js +5 -5
- package/dist/components/nav-command-menu.js.map +1 -1
- package/dist/components/nav-shell.cjs +2 -2
- package/dist/components/nav-shell.cjs.map +1 -1
- package/dist/components/nav-shell.js +2 -2
- package/dist/components/nav-shell.js.map +1 -1
- package/dist/components/portfolio-chip-filter.cjs +1 -1
- package/dist/components/portfolio-chip-filter.cjs.map +1 -1
- package/dist/components/portfolio-chip-filter.js +1 -1
- package/dist/components/portfolio-chip-filter.js.map +1 -1
- package/dist/components/portfolio-nav.cjs +2 -2
- package/dist/components/portfolio-nav.cjs.map +1 -1
- package/dist/components/portfolio-nav.js +2 -2
- package/dist/components/portfolio-nav.js.map +1 -1
- package/dist/components/rewards/journey-path.cjs +2 -2
- package/dist/components/rewards/journey-path.cjs.map +1 -1
- package/dist/components/rewards/journey-path.js +2 -2
- package/dist/components/rewards/journey-path.js.map +1 -1
- package/dist/components/rewards/level-badge.cjs +1 -1
- package/dist/components/rewards/level-badge.cjs.map +1 -1
- package/dist/components/rewards/level-badge.js +1 -1
- package/dist/components/rewards/level-badge.js.map +1 -1
- package/dist/components/rewards/level-ladder.cjs +2 -2
- package/dist/components/rewards/level-ladder.cjs.map +1 -1
- package/dist/components/rewards/level-ladder.js +2 -2
- package/dist/components/rewards/level-ladder.js.map +1 -1
- package/dist/components/scroll-section.cjs +1 -1
- package/dist/components/scroll-section.cjs.map +1 -1
- package/dist/components/scroll-section.js +1 -1
- package/dist/components/scroll-section.js.map +1 -1
- package/dist/components/share-button.cjs +1 -1
- package/dist/components/share-button.cjs.map +1 -1
- package/dist/components/share-button.js +1 -1
- package/dist/components/share-button.js.map +1 -1
- package/dist/components/stat-tile.cjs +3 -3
- package/dist/components/stat-tile.cjs.map +1 -1
- package/dist/components/stat-tile.js +3 -3
- package/dist/components/stat-tile.js.map +1 -1
- package/dist/components/token-card.cjs +4 -4
- package/dist/components/token-card.cjs.map +1 -1
- package/dist/components/token-card.js +4 -4
- package/dist/components/token-card.js.map +1 -1
- package/dist/data/brand.cjs +0 -14
- package/dist/data/brand.cjs.map +1 -1
- package/dist/data/brand.d.cts +0 -14
- package/dist/data/brand.d.ts +0 -14
- package/dist/data/brand.js +0 -14
- package/dist/data/brand.js.map +1 -1
- package/dist/medialane.css +4 -0
- package/dist/preset/tailwind.cjs +16 -8
- package/dist/preset/tailwind.cjs.map +1 -1
- package/dist/preset/tailwind.js +16 -8
- package/dist/preset/tailwind.js.map +1 -1
- package/package.json +6 -3
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/components/community-rewards-section.tsx"],"sourcesContent":["\"use client\";\n\nimport Link from \"next/link\";\nimport { ArrowRight, Zap, Palette, ShoppingBag, MessageSquare } from \"lucide-react\";\nimport { AddressDisplay } from \"./address-display.js\";\n\nexport interface CommunityRewardsEntry {\n address: string;\n totalXp: number;\n}\n\nexport interface CommunityRewardsSectionProps {\n entries: CommunityRewardsEntry[];\n isLoading?: boolean;\n rewardsHref: string;\n creatorHref: (address: string) => string;\n}\n\nconst EARN_ACTIONS = [\n { icon: Zap, label: \"Mint\" },\n { icon: Palette, label: \"Create\" },\n { icon: ShoppingBag, label: \"Trade\" },\n { icon: MessageSquare, label: \"Engage\" },\n];\n\nexport function CommunityRewardsSection({\n entries,\n isLoading = false,\n rewardsHref,\n creatorHref,\n}: CommunityRewardsSectionProps) {\n return (\n <section className=\"rounded-2xl bg-muted/50 dark:bg-card overflow-hidden grid lg:grid-cols-2 max-lg:divide-y lg:divide-x divide-brand-orange/20\">\n\n {/* Left — info panel */}\n <div className=\"px-7 py-8 sm:px-9 flex flex-col gap-6\">\n <div>\n {/* Creator's Fund eyebrow — shared with CreatorAirdropBanner */}\n <span className=\"text-xs font-bold uppercase tracking-widest text-brand-orange\">\n Creator's Fund\n </span>\n <h2 className=\"mt-4 text-2xl font-bold tracking-tight leading-tight\">\n Community Rewards\n </h2>\n <p className=\"mt-1.5 text-sm text-muted-foreground leading-relaxed\">\n Earn XP. Share the Creator's Fund.\n </p>\n </div>\n\n {/* Recently active — live rail sourced from the same entries as the\n member cards on the right, no rank/podium framing. */}\n {!isLoading && entries.length > 0 && (\n <div className=\"border-y border-brand-orange/20 py-3\">\n <p className=\"text-
|
|
1
|
+
{"version":3,"sources":["../../src/components/community-rewards-section.tsx"],"sourcesContent":["\"use client\";\n\nimport Link from \"next/link\";\nimport { ArrowRight, Zap, Palette, ShoppingBag, MessageSquare } from \"lucide-react\";\nimport { AddressDisplay } from \"./address-display.js\";\n\nexport interface CommunityRewardsEntry {\n address: string;\n totalXp: number;\n}\n\nexport interface CommunityRewardsSectionProps {\n entries: CommunityRewardsEntry[];\n isLoading?: boolean;\n rewardsHref: string;\n creatorHref: (address: string) => string;\n}\n\nconst EARN_ACTIONS = [\n { icon: Zap, label: \"Mint\" },\n { icon: Palette, label: \"Create\" },\n { icon: ShoppingBag, label: \"Trade\" },\n { icon: MessageSquare, label: \"Engage\" },\n];\n\nexport function CommunityRewardsSection({\n entries,\n isLoading = false,\n rewardsHref,\n creatorHref,\n}: CommunityRewardsSectionProps) {\n return (\n <section className=\"rounded-2xl bg-muted/50 dark:bg-card overflow-hidden grid lg:grid-cols-2 max-lg:divide-y lg:divide-x divide-brand-orange/20\">\n\n {/* Left — info panel */}\n <div className=\"px-7 py-8 sm:px-9 flex flex-col gap-6\">\n <div>\n {/* Creator's Fund eyebrow — shared with CreatorAirdropBanner */}\n <span className=\"text-xs font-bold uppercase tracking-widest text-brand-orange\">\n Creator's Fund\n </span>\n <h2 className=\"mt-4 text-2xl font-bold tracking-tight leading-tight\">\n Community Rewards\n </h2>\n <p className=\"mt-1.5 text-sm text-muted-foreground leading-relaxed\">\n Earn XP. Share the Creator's Fund.\n </p>\n </div>\n\n {/* Recently active — live rail sourced from the same entries as the\n member cards on the right, no rank/podium framing. */}\n {!isLoading && entries.length > 0 && (\n <div className=\"border-y border-brand-orange/20 py-3\">\n <p className=\"text-2xs font-bold uppercase tracking-widest text-muted-foreground mb-2\">\n Recently active\n </p>\n <div className=\"flex -space-x-2\">\n {entries.slice(0, 5).map((entry) => (\n <span\n key={entry.address}\n title={`${entry.totalXp.toLocaleString()} XP`}\n className=\"flex h-7 w-7 items-center justify-center rounded-full border-2 border-background bg-brand-orange/15 text-[9px] font-bold text-brand-orange\"\n >\n {entry.address.slice(2, 4).toUpperCase()}\n </span>\n ))}\n </div>\n </div>\n )}\n\n {/* Earn actions — plain text row */}\n <div className=\"flex flex-wrap gap-x-5 gap-y-2\">\n {EARN_ACTIONS.map(({ icon: Icon, label }) => (\n <span\n key={label}\n className=\"flex items-center gap-1.5 text-xs font-medium text-muted-foreground\"\n >\n <Icon className=\"h-3 w-3 text-brand-orange/70\" />\n {label}\n </span>\n ))}\n </div>\n\n <Link\n href={rewardsHref}\n className=\"mt-auto inline-flex items-center justify-center gap-2 rounded-xl bg-brand-orange px-6 py-3 text-sm font-semibold text-white transition-all hover:brightness-110 active:scale-[0.98]\"\n >\n Start earning XP\n <ArrowRight className=\"h-3.5 w-3.5\" />\n </Link>\n </div>\n\n {/* Right — member cards */}\n <div className=\"p-5 sm:p-6\">\n {!isLoading && entries.length > 0 ? (\n <div className=\"grid grid-cols-2 gap-3 h-full\">\n {entries.slice(0, 4).map((entry) => (\n <Link\n key={entry.address}\n href={creatorHref(entry.address)}\n className=\"group relative rounded-xl bg-card dark:bg-muted/30 hover:bg-card/70 dark:hover:bg-muted/50 overflow-hidden transition-colors flex flex-col justify-between p-4\"\n >\n <div className=\"absolute inset-x-0 top-0 h-0.5 bg-brand-orange/40\" />\n\n {/* XP — hero number */}\n <div className=\"space-y-0.5 pt-1\">\n <p className=\"text-2xl font-black tabular-nums leading-none\">\n {entry.totalXp.toLocaleString()}\n </p>\n <p className=\"text-2xs text-muted-foreground\">XP earned</p>\n </div>\n\n <AddressDisplay\n address={entry.address}\n chars={4}\n showCopy={false}\n className=\"text-xs font-medium text-muted-foreground group-hover:text-foreground transition-colors\"\n />\n </Link>\n ))}\n </div>\n ) : (\n <div className=\"grid grid-cols-2 gap-3 h-full\">\n {Array.from({ length: 4 }).map((_, i) => (\n <div key={i} className=\"min-h-[100px] rounded-xl bg-card dark:bg-muted/30 animate-pulse\" />\n ))}\n </div>\n )}\n </div>\n </section>\n );\n}\n"],"mappings":";AAoCQ,SAEE,KAFF;AAlCR,OAAO,UAAU;AACjB,SAAS,YAAY,KAAK,SAAS,aAAa,qBAAqB;AACrE,SAAS,sBAAsB;AAc/B,MAAM,eAAe;AAAA,EACnB,EAAE,MAAM,KAAK,OAAO,OAAO;AAAA,EAC3B,EAAE,MAAM,SAAS,OAAO,SAAS;AAAA,EACjC,EAAE,MAAM,aAAa,OAAO,QAAQ;AAAA,EACpC,EAAE,MAAM,eAAe,OAAO,SAAS;AACzC;AAEO,SAAS,wBAAwB;AAAA,EACtC;AAAA,EACA,YAAY;AAAA,EACZ;AAAA,EACA;AACF,GAAiC;AAC/B,SACE,qBAAC,aAAQ,WAAU,+HAGjB;AAAA,yBAAC,SAAI,WAAU,yCACb;AAAA,2BAAC,SAEC;AAAA,4BAAC,UAAK,WAAU,iEAAgE,4BAEhF;AAAA,QACA,oBAAC,QAAG,WAAU,wDAAuD,+BAErE;AAAA,QACA,oBAAC,OAAE,WAAU,wDAAuD,gDAEpE;AAAA,SACF;AAAA,MAIC,CAAC,aAAa,QAAQ,SAAS,KAC9B,qBAAC,SAAI,WAAU,wCACb;AAAA,4BAAC,OAAE,WAAU,2EAA0E,6BAEvF;AAAA,QACA,oBAAC,SAAI,WAAU,mBACZ,kBAAQ,MAAM,GAAG,CAAC,EAAE,IAAI,CAAC,UACxB;AAAA,UAAC;AAAA;AAAA,YAEC,OAAO,GAAG,MAAM,QAAQ,eAAe,CAAC;AAAA,YACxC,WAAU;AAAA,YAET,gBAAM,QAAQ,MAAM,GAAG,CAAC,EAAE,YAAY;AAAA;AAAA,UAJlC,MAAM;AAAA,QAKb,CACD,GACH;AAAA,SACF;AAAA,MAIF,oBAAC,SAAI,WAAU,kCACZ,uBAAa,IAAI,CAAC,EAAE,MAAM,MAAM,MAAM,MACrC;AAAA,QAAC;AAAA;AAAA,UAEC,WAAU;AAAA,UAEV;AAAA,gCAAC,QAAK,WAAU,gCAA+B;AAAA,YAC9C;AAAA;AAAA;AAAA,QAJI;AAAA,MAKP,CACD,GACH;AAAA,MAEA;AAAA,QAAC;AAAA;AAAA,UACC,MAAM;AAAA,UACN,WAAU;AAAA,UACX;AAAA;AAAA,YAEC,oBAAC,cAAW,WAAU,eAAc;AAAA;AAAA;AAAA,MACtC;AAAA,OACF;AAAA,IAGA,oBAAC,SAAI,WAAU,cACZ,WAAC,aAAa,QAAQ,SAAS,IAC9B,oBAAC,SAAI,WAAU,iCACZ,kBAAQ,MAAM,GAAG,CAAC,EAAE,IAAI,CAAC,UACxB;AAAA,MAAC;AAAA;AAAA,QAEC,MAAM,YAAY,MAAM,OAAO;AAAA,QAC/B,WAAU;AAAA,QAEV;AAAA,8BAAC,SAAI,WAAU,qDAAoD;AAAA,UAGnE,qBAAC,SAAI,WAAU,oBACb;AAAA,gCAAC,OAAE,WAAU,iDACV,gBAAM,QAAQ,eAAe,GAChC;AAAA,YACA,oBAAC,OAAE,WAAU,kCAAiC,uBAAS;AAAA,aACzD;AAAA,UAEA;AAAA,YAAC;AAAA;AAAA,cACC,SAAS,MAAM;AAAA,cACf,OAAO;AAAA,cACP,UAAU;AAAA,cACV,WAAU;AAAA;AAAA,UACZ;AAAA;AAAA;AAAA,MAnBK,MAAM;AAAA,IAoBb,CACD,GACH,IAEA,oBAAC,SAAI,WAAU,iCACZ,gBAAM,KAAK,EAAE,QAAQ,EAAE,CAAC,EAAE,IAAI,CAAC,GAAG,MACjC,oBAAC,SAAY,WAAU,qEAAb,CAA+E,CAC1F,GACH,GAEJ;AAAA,KACF;AAEJ;","names":[]}
|
|
@@ -45,7 +45,7 @@ function StripShell({
|
|
|
45
45
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: (0, import_cn.cn)("h-7 w-7 rounded-lg flex items-center justify-center", iconBg), children: icon }),
|
|
46
46
|
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
|
|
47
47
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("h2", { className: "text-lg sm:text-xl font-semibold leading-none", children: title }),
|
|
48
|
-
subtitle && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "text-
|
|
48
|
+
subtitle && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "text-2xs text-muted-foreground mt-1", children: subtitle })
|
|
49
49
|
] })
|
|
50
50
|
] }),
|
|
51
51
|
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/components/discover-feed-section.tsx"],"sourcesContent":["\"use client\";\n\nimport { Tag, Activity, RefreshCw, ArrowRight } from \"lucide-react\";\nimport { FadeIn } from \"./motion-primitives.js\";\nimport { ListingCard, ListingCardSkeleton } from \"./listing-card.js\";\nimport { ActivityCard, ActivityCardSkeleton } from \"./activity-card.js\";\nimport { timeAgo } from \"../utils/time.js\";\nimport { cn } from \"../utils/cn.js\";\nimport type { ApiOrder, ApiActivity } from \"@medialane/sdk\";\n\nexport interface DiscoverFeedSectionProps {\n orders: ApiOrder[];\n isLoading: boolean;\n activities: ApiActivity[];\n activitiesLoading: boolean;\n lastUpdated: string;\n getAssetHref?: (contract: string, tokenId: string) => string;\n getActorHref?: (address: string) => string;\n explorerUrl?: string;\n marketplaceHref?: string;\n activitiesHref?: string;\n /** Optional buy hook — when provided, listing cards show their Buy action */\n onBuyOrder?: (order: ApiOrder) => void;\n /** Hide the buy action for orders the viewer owns */\n isOwnOrder?: (order: ApiOrder) => boolean;\n}\n\n// ── Shared strip shell: icon-badge header + snap-scroll row ─────────────────\nfunction StripShell({\n icon,\n iconBg,\n title,\n subtitle,\n href,\n linkLabel,\n children,\n}: {\n icon: React.ReactNode;\n iconBg: string;\n title: string;\n subtitle?: React.ReactNode;\n href: string;\n linkLabel: string;\n children: React.ReactNode;\n}) {\n return (\n <section className=\"space-y-4\">\n <div className=\"flex items-center justify-between\">\n <div className=\"flex items-center gap-2.5\">\n <div className={cn(\"h-7 w-7 rounded-lg flex items-center justify-center\", iconBg)}>\n {icon}\n </div>\n <div>\n <h2 className=\"text-lg sm:text-xl font-semibold leading-none\">{title}</h2>\n {subtitle && <p className=\"text-
|
|
1
|
+
{"version":3,"sources":["../../src/components/discover-feed-section.tsx"],"sourcesContent":["\"use client\";\n\nimport { Tag, Activity, RefreshCw, ArrowRight } from \"lucide-react\";\nimport { FadeIn } from \"./motion-primitives.js\";\nimport { ListingCard, ListingCardSkeleton } from \"./listing-card.js\";\nimport { ActivityCard, ActivityCardSkeleton } from \"./activity-card.js\";\nimport { timeAgo } from \"../utils/time.js\";\nimport { cn } from \"../utils/cn.js\";\nimport type { ApiOrder, ApiActivity } from \"@medialane/sdk\";\n\nexport interface DiscoverFeedSectionProps {\n orders: ApiOrder[];\n isLoading: boolean;\n activities: ApiActivity[];\n activitiesLoading: boolean;\n lastUpdated: string;\n getAssetHref?: (contract: string, tokenId: string) => string;\n getActorHref?: (address: string) => string;\n explorerUrl?: string;\n marketplaceHref?: string;\n activitiesHref?: string;\n /** Optional buy hook — when provided, listing cards show their Buy action */\n onBuyOrder?: (order: ApiOrder) => void;\n /** Hide the buy action for orders the viewer owns */\n isOwnOrder?: (order: ApiOrder) => boolean;\n}\n\n// ── Shared strip shell: icon-badge header + snap-scroll row ─────────────────\nfunction StripShell({\n icon,\n iconBg,\n title,\n subtitle,\n href,\n linkLabel,\n children,\n}: {\n icon: React.ReactNode;\n iconBg: string;\n title: string;\n subtitle?: React.ReactNode;\n href: string;\n linkLabel: string;\n children: React.ReactNode;\n}) {\n return (\n <section className=\"space-y-4\">\n <div className=\"flex items-center justify-between\">\n <div className=\"flex items-center gap-2.5\">\n <div className={cn(\"h-7 w-7 rounded-lg flex items-center justify-center\", iconBg)}>\n {icon}\n </div>\n <div>\n <h2 className=\"text-lg sm:text-xl font-semibold leading-none\">{title}</h2>\n {subtitle && <p className=\"text-2xs text-muted-foreground mt-1\">{subtitle}</p>}\n </div>\n </div>\n <a\n href={href}\n className=\"inline-flex items-center gap-1 text-sm text-muted-foreground hover:text-foreground px-3 py-1.5 rounded-md hover:bg-accent transition-colors\"\n >\n {linkLabel} <ArrowRight className=\"h-3.5 w-3.5\" />\n </a>\n </div>\n\n <div className=\"w-full overflow-x-auto scrollbar-hide\">\n <div className=\"flex gap-5 sm:gap-6 snap-x snap-mandatory pb-2\" style={{ width: \"max-content\" }}>\n {children}\n </div>\n </div>\n </section>\n );\n}\n\nexport interface DiscoverActivityStripProps {\n orders: ApiOrder[];\n isLoading: boolean;\n marketplaceHref?: string;\n onBuyOrder?: (order: ApiOrder) => void;\n isOwnOrder?: (order: ApiOrder) => boolean;\n}\n\n/** Markets — recent listings carousel. Standalone (2026-07-05) so a page can\n * place other sections (e.g. Browse by Type) between this and Community. */\nexport function DiscoverActivityStrip({\n orders,\n isLoading,\n marketplaceHref = \"/marketplace\",\n onBuyOrder,\n isOwnOrder,\n}: DiscoverActivityStripProps) {\n return (\n <FadeIn>\n <StripShell\n icon={<Tag className=\"h-3.5 w-3.5 text-white\" />}\n iconBg=\"bg-gradient-to-br from-rose-500 to-pink-600 shadow-md shadow-rose-500/20\"\n title=\"Activity\"\n href={marketplaceHref}\n linkLabel=\"View all\"\n >\n {isLoading\n ? Array.from({ length: 8 }).map((_, i) => (\n <div key={i} className=\"w-72 snap-start shrink-0\">\n <ListingCardSkeleton />\n </div>\n ))\n : orders.length === 0\n ? (\n <p className=\"text-sm text-muted-foreground py-4\">No active listings yet.</p>\n )\n : orders.map((order) => {\n const own = isOwnOrder?.(order) ?? false;\n return (\n <div key={order.orderHash} className=\"w-72 snap-start shrink-0\">\n <ListingCard\n order={order}\n onBuy={onBuyOrder && !own ? () => onBuyOrder(order) : undefined}\n />\n </div>\n );\n })}\n </StripShell>\n </FadeIn>\n );\n}\n\n/**\n * @deprecated Kept for apps still on the combined layout (e.g. medialane-io,\n * pending its own migration). New pages should use `DiscoverActivityStrip`\n * directly and build their own Community section — see medialane-starknet's\n * discover community-section.tsx for the 2-column activities+leaderboard\n * replacement of the old carousel below.\n */\nexport function DiscoverFeedSection({\n orders,\n isLoading,\n activities,\n activitiesLoading,\n lastUpdated,\n getAssetHref = (c, t) => `/asset/${c}/${t}`,\n marketplaceHref = \"/marketplace\",\n activitiesHref = \"/activities\",\n onBuyOrder,\n isOwnOrder,\n}: DiscoverFeedSectionProps) {\n return (\n <div className=\"space-y-14 sm:space-y-20\">\n <DiscoverActivityStrip\n orders={orders}\n isLoading={isLoading}\n marketplaceHref={marketplaceHref}\n onBuyOrder={onBuyOrder}\n isOwnOrder={isOwnOrder}\n />\n\n {/* Community — recent on-chain activity carousel */}\n <FadeIn delay={0.08}>\n <StripShell\n icon={<Activity className=\"h-3.5 w-3.5 text-white\" />}\n iconBg=\"bg-gradient-to-br from-indigo-500 to-blue-600 shadow-md shadow-indigo-500/20\"\n title=\"Community\"\n subtitle={\n <span className=\"flex items-center gap-1\">\n <RefreshCw className=\"h-2.5 w-2.5\" />\n Updated {timeAgo(lastUpdated)}\n </span>\n }\n href={activitiesHref}\n linkLabel=\"Activities\"\n >\n {activitiesLoading\n ? Array.from({ length: 8 }).map((_, i) => (\n <div key={i} className=\"w-56 sm:w-64 snap-start shrink-0\">\n <ActivityCardSkeleton />\n </div>\n ))\n : activities.length === 0\n ? (\n <p className=\"text-sm text-muted-foreground py-4\">\n No activity yet. Be the first to trade on Medialane!\n </p>\n )\n : activities.map((act, i) => {\n const key = act.txHash\n ? `${act.txHash}-${act.type}-${act.nftTokenId ?? \"\"}`\n : `activity-${i}`;\n return (\n <div key={key} className=\"w-56 sm:w-64 snap-start shrink-0\">\n <ActivityCard activity={act} getAssetHref={getAssetHref} />\n </div>\n );\n })}\n </StripShell>\n </FadeIn>\n </div>\n );\n}\n"],"mappings":"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","names":[]}
|
|
@@ -21,7 +21,7 @@ function StripShell({
|
|
|
21
21
|
/* @__PURE__ */ jsx("div", { className: cn("h-7 w-7 rounded-lg flex items-center justify-center", iconBg), children: icon }),
|
|
22
22
|
/* @__PURE__ */ jsxs("div", { children: [
|
|
23
23
|
/* @__PURE__ */ jsx("h2", { className: "text-lg sm:text-xl font-semibold leading-none", children: title }),
|
|
24
|
-
subtitle && /* @__PURE__ */ jsx("p", { className: "text-
|
|
24
|
+
subtitle && /* @__PURE__ */ jsx("p", { className: "text-2xs text-muted-foreground mt-1", children: subtitle })
|
|
25
25
|
] })
|
|
26
26
|
] }),
|
|
27
27
|
/* @__PURE__ */ jsxs(
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/components/discover-feed-section.tsx"],"sourcesContent":["\"use client\";\n\nimport { Tag, Activity, RefreshCw, ArrowRight } from \"lucide-react\";\nimport { FadeIn } from \"./motion-primitives.js\";\nimport { ListingCard, ListingCardSkeleton } from \"./listing-card.js\";\nimport { ActivityCard, ActivityCardSkeleton } from \"./activity-card.js\";\nimport { timeAgo } from \"../utils/time.js\";\nimport { cn } from \"../utils/cn.js\";\nimport type { ApiOrder, ApiActivity } from \"@medialane/sdk\";\n\nexport interface DiscoverFeedSectionProps {\n orders: ApiOrder[];\n isLoading: boolean;\n activities: ApiActivity[];\n activitiesLoading: boolean;\n lastUpdated: string;\n getAssetHref?: (contract: string, tokenId: string) => string;\n getActorHref?: (address: string) => string;\n explorerUrl?: string;\n marketplaceHref?: string;\n activitiesHref?: string;\n /** Optional buy hook — when provided, listing cards show their Buy action */\n onBuyOrder?: (order: ApiOrder) => void;\n /** Hide the buy action for orders the viewer owns */\n isOwnOrder?: (order: ApiOrder) => boolean;\n}\n\n// ── Shared strip shell: icon-badge header + snap-scroll row ─────────────────\nfunction StripShell({\n icon,\n iconBg,\n title,\n subtitle,\n href,\n linkLabel,\n children,\n}: {\n icon: React.ReactNode;\n iconBg: string;\n title: string;\n subtitle?: React.ReactNode;\n href: string;\n linkLabel: string;\n children: React.ReactNode;\n}) {\n return (\n <section className=\"space-y-4\">\n <div className=\"flex items-center justify-between\">\n <div className=\"flex items-center gap-2.5\">\n <div className={cn(\"h-7 w-7 rounded-lg flex items-center justify-center\", iconBg)}>\n {icon}\n </div>\n <div>\n <h2 className=\"text-lg sm:text-xl font-semibold leading-none\">{title}</h2>\n {subtitle && <p className=\"text-
|
|
1
|
+
{"version":3,"sources":["../../src/components/discover-feed-section.tsx"],"sourcesContent":["\"use client\";\n\nimport { Tag, Activity, RefreshCw, ArrowRight } from \"lucide-react\";\nimport { FadeIn } from \"./motion-primitives.js\";\nimport { ListingCard, ListingCardSkeleton } from \"./listing-card.js\";\nimport { ActivityCard, ActivityCardSkeleton } from \"./activity-card.js\";\nimport { timeAgo } from \"../utils/time.js\";\nimport { cn } from \"../utils/cn.js\";\nimport type { ApiOrder, ApiActivity } from \"@medialane/sdk\";\n\nexport interface DiscoverFeedSectionProps {\n orders: ApiOrder[];\n isLoading: boolean;\n activities: ApiActivity[];\n activitiesLoading: boolean;\n lastUpdated: string;\n getAssetHref?: (contract: string, tokenId: string) => string;\n getActorHref?: (address: string) => string;\n explorerUrl?: string;\n marketplaceHref?: string;\n activitiesHref?: string;\n /** Optional buy hook — when provided, listing cards show their Buy action */\n onBuyOrder?: (order: ApiOrder) => void;\n /** Hide the buy action for orders the viewer owns */\n isOwnOrder?: (order: ApiOrder) => boolean;\n}\n\n// ── Shared strip shell: icon-badge header + snap-scroll row ─────────────────\nfunction StripShell({\n icon,\n iconBg,\n title,\n subtitle,\n href,\n linkLabel,\n children,\n}: {\n icon: React.ReactNode;\n iconBg: string;\n title: string;\n subtitle?: React.ReactNode;\n href: string;\n linkLabel: string;\n children: React.ReactNode;\n}) {\n return (\n <section className=\"space-y-4\">\n <div className=\"flex items-center justify-between\">\n <div className=\"flex items-center gap-2.5\">\n <div className={cn(\"h-7 w-7 rounded-lg flex items-center justify-center\", iconBg)}>\n {icon}\n </div>\n <div>\n <h2 className=\"text-lg sm:text-xl font-semibold leading-none\">{title}</h2>\n {subtitle && <p className=\"text-2xs text-muted-foreground mt-1\">{subtitle}</p>}\n </div>\n </div>\n <a\n href={href}\n className=\"inline-flex items-center gap-1 text-sm text-muted-foreground hover:text-foreground px-3 py-1.5 rounded-md hover:bg-accent transition-colors\"\n >\n {linkLabel} <ArrowRight className=\"h-3.5 w-3.5\" />\n </a>\n </div>\n\n <div className=\"w-full overflow-x-auto scrollbar-hide\">\n <div className=\"flex gap-5 sm:gap-6 snap-x snap-mandatory pb-2\" style={{ width: \"max-content\" }}>\n {children}\n </div>\n </div>\n </section>\n );\n}\n\nexport interface DiscoverActivityStripProps {\n orders: ApiOrder[];\n isLoading: boolean;\n marketplaceHref?: string;\n onBuyOrder?: (order: ApiOrder) => void;\n isOwnOrder?: (order: ApiOrder) => boolean;\n}\n\n/** Markets — recent listings carousel. Standalone (2026-07-05) so a page can\n * place other sections (e.g. Browse by Type) between this and Community. */\nexport function DiscoverActivityStrip({\n orders,\n isLoading,\n marketplaceHref = \"/marketplace\",\n onBuyOrder,\n isOwnOrder,\n}: DiscoverActivityStripProps) {\n return (\n <FadeIn>\n <StripShell\n icon={<Tag className=\"h-3.5 w-3.5 text-white\" />}\n iconBg=\"bg-gradient-to-br from-rose-500 to-pink-600 shadow-md shadow-rose-500/20\"\n title=\"Activity\"\n href={marketplaceHref}\n linkLabel=\"View all\"\n >\n {isLoading\n ? Array.from({ length: 8 }).map((_, i) => (\n <div key={i} className=\"w-72 snap-start shrink-0\">\n <ListingCardSkeleton />\n </div>\n ))\n : orders.length === 0\n ? (\n <p className=\"text-sm text-muted-foreground py-4\">No active listings yet.</p>\n )\n : orders.map((order) => {\n const own = isOwnOrder?.(order) ?? false;\n return (\n <div key={order.orderHash} className=\"w-72 snap-start shrink-0\">\n <ListingCard\n order={order}\n onBuy={onBuyOrder && !own ? () => onBuyOrder(order) : undefined}\n />\n </div>\n );\n })}\n </StripShell>\n </FadeIn>\n );\n}\n\n/**\n * @deprecated Kept for apps still on the combined layout (e.g. medialane-io,\n * pending its own migration). New pages should use `DiscoverActivityStrip`\n * directly and build their own Community section — see medialane-starknet's\n * discover community-section.tsx for the 2-column activities+leaderboard\n * replacement of the old carousel below.\n */\nexport function DiscoverFeedSection({\n orders,\n isLoading,\n activities,\n activitiesLoading,\n lastUpdated,\n getAssetHref = (c, t) => `/asset/${c}/${t}`,\n marketplaceHref = \"/marketplace\",\n activitiesHref = \"/activities\",\n onBuyOrder,\n isOwnOrder,\n}: DiscoverFeedSectionProps) {\n return (\n <div className=\"space-y-14 sm:space-y-20\">\n <DiscoverActivityStrip\n orders={orders}\n isLoading={isLoading}\n marketplaceHref={marketplaceHref}\n onBuyOrder={onBuyOrder}\n isOwnOrder={isOwnOrder}\n />\n\n {/* Community — recent on-chain activity carousel */}\n <FadeIn delay={0.08}>\n <StripShell\n icon={<Activity className=\"h-3.5 w-3.5 text-white\" />}\n iconBg=\"bg-gradient-to-br from-indigo-500 to-blue-600 shadow-md shadow-indigo-500/20\"\n title=\"Community\"\n subtitle={\n <span className=\"flex items-center gap-1\">\n <RefreshCw className=\"h-2.5 w-2.5\" />\n Updated {timeAgo(lastUpdated)}\n </span>\n }\n href={activitiesHref}\n linkLabel=\"Activities\"\n >\n {activitiesLoading\n ? Array.from({ length: 8 }).map((_, i) => (\n <div key={i} className=\"w-56 sm:w-64 snap-start shrink-0\">\n <ActivityCardSkeleton />\n </div>\n ))\n : activities.length === 0\n ? (\n <p className=\"text-sm text-muted-foreground py-4\">\n No activity yet. Be the first to trade on Medialane!\n </p>\n )\n : activities.map((act, i) => {\n const key = act.txHash\n ? `${act.txHash}-${act.type}-${act.nftTokenId ?? \"\"}`\n : `activity-${i}`;\n return (\n <div key={key} className=\"w-56 sm:w-64 snap-start shrink-0\">\n <ActivityCard activity={act} getAssetHref={getAssetHref} />\n </div>\n );\n })}\n </StripShell>\n </FadeIn>\n </div>\n );\n}\n"],"mappings":"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","names":[]}
|
|
@@ -144,18 +144,18 @@ function FeaturedCarousel({
|
|
|
144
144
|
"button",
|
|
145
145
|
{
|
|
146
146
|
onClick: prev,
|
|
147
|
-
className: "absolute left-3 top-1/2 -translate-y-1/2 h-
|
|
147
|
+
className: "absolute left-3 top-1/2 -translate-y-1/2 min-h-11 min-w-11 rounded-full flex items-center justify-center z-10",
|
|
148
148
|
"aria-label": "Previous",
|
|
149
|
-
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_lucide_react.ChevronLeft, { className: "h-4 w-4" })
|
|
149
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "flex h-9 w-9 items-center justify-center rounded-full bg-black/40 border border-white/10 backdrop-blur-sm text-white hover:bg-black/60 transition-colors", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_lucide_react.ChevronLeft, { className: "h-4 w-4" }) })
|
|
150
150
|
}
|
|
151
151
|
),
|
|
152
152
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
153
153
|
"button",
|
|
154
154
|
{
|
|
155
155
|
onClick: next,
|
|
156
|
-
className: "absolute right-3 top-1/2 -translate-y-1/2 h-
|
|
156
|
+
className: "absolute right-3 top-1/2 -translate-y-1/2 min-h-11 min-w-11 rounded-full flex items-center justify-center z-10",
|
|
157
157
|
"aria-label": "Next",
|
|
158
|
-
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_lucide_react.ChevronRight, { className: "h-4 w-4" })
|
|
158
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "flex h-9 w-9 items-center justify-center rounded-full bg-black/40 border border-white/10 backdrop-blur-sm text-white hover:bg-black/60 transition-colors", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_lucide_react.ChevronRight, { className: "h-4 w-4" }) })
|
|
159
159
|
}
|
|
160
160
|
)
|
|
161
161
|
] }),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/components/featured-carousel.tsx"],"sourcesContent":["\"use client\";\n\nimport { useState, useEffect, useCallback } from \"react\";\nimport { motion, AnimatePresence } from \"framer-motion\";\nimport { ChevronLeft, ChevronRight, ArrowRight, Sparkles } from \"lucide-react\";\nimport { FadeIn, EASE_OUT } from \"./motion-primitives.js\";\nimport { ipfsToHttp } from \"../utils/ipfs.js\";\nimport { formatDisplayPrice } from \"../utils/format.js\";\nimport type { ApiCollection } from \"@medialane/sdk\";\n\nexport interface FeaturedCarouselProps {\n collections: ApiCollection[];\n isLoading: boolean;\n getHref: (collection: ApiCollection) => string;\n allCollectionsHref?: string;\n}\n\nfunction Slide({ collection, href }: { collection: ApiCollection; href: string }) {\n const name = collection.name ?? \"Featured Collection\";\n const image = collection.image ? ipfsToHttp(collection.image) : null;\n\n return (\n <div className=\"relative w-full h-full\">\n {image ? (\n <img\n src={image}\n alt={name}\n loading=\"lazy\"\n className=\"absolute inset-0 w-full h-full object-cover\"\n />\n ) : (\n <div className=\"absolute inset-0 bg-gradient-to-br from-brand-purple/30 via-brand-blue/20 to-brand-navy/40\" />\n )}\n <div className=\"absolute inset-0 bg-gradient-to-t from-black/85 via-black/30 to-black/10\" />\n <div className=\"absolute inset-0 bg-gradient-to-r from-black/50 via-transparent to-transparent\" />\n <div className=\"absolute bottom-0 left-0 right-0 p-5 sm:p-8 lg:p-10\">\n <h2 className=\"text-2xl sm:text-3xl lg:text-4xl font-black text-white leading-tight mb-3 max-w-lg\">\n {name}\n </h2>\n <div className=\"flex items-center gap-4 mb-5\">\n {collection.totalSupply != null && (\n <span className=\"text-sm text-white/70\">\n <span className=\"font-bold text-white\">{collection.totalSupply}</span> items\n </span>\n )}\n {collection.floorPrice && (\n <span className=\"text-sm text-white/70\">\n Floor{\" \"}\n <span className=\"font-bold text-brand-orange\">\n {formatDisplayPrice(collection.floorPrice)}\n </span>\n </span>\n )}\n </div>\n <a\n href={href}\n className=\"inline-flex items-center gap-2 text-sm font-semibold px-4 py-2 rounded-lg bg-white/15 hover:bg-white/25 text-white border border-white/20 backdrop-blur-sm transition-colors\"\n >\n View Collection <ArrowRight className=\"h-3.5 w-3.5\" />\n </a>\n </div>\n </div>\n );\n}\n\nexport function FeaturedCarouselSkeleton() {\n return (\n <FadeIn>\n <section className=\"space-y-4\">\n <div className=\"flex items-center gap-2 mt-0.5\">\n <Sparkles className=\"h-4 w-4 text-brand-purple\" />\n <h2 className=\"text-xl font-bold\">Featured Collections</h2>\n </div>\n <div className=\"-mx-4 sm:-mx-6 lg:-mx-8 aspect-[16/7] sm:aspect-[21/9] bg-muted animate-pulse\" />\n </section>\n </FadeIn>\n );\n}\n\nexport function FeaturedCarousel({\n collections,\n isLoading,\n getHref,\n allCollectionsHref = \"/collections\",\n}: FeaturedCarouselProps) {\n const [active, setActive] = useState(0);\n const [paused, setPaused] = useState(false);\n const total = collections.length;\n\n const next = useCallback(() => setActive((p) => (p + 1) % total), [total]);\n const prev = useCallback(() => setActive((p) => (p - 1 + total) % total), [total]);\n\n useEffect(() => { setActive(0); }, [total]);\n\n useEffect(() => {\n if (paused || total <= 1) return;\n const id = setInterval(next, 6000);\n return () => clearInterval(id);\n }, [next, paused, total]);\n\n if (isLoading) return <FeaturedCarouselSkeleton />;\n if (total === 0) return null;\n\n return (\n <FadeIn>\n <section className=\"space-y-4\">\n <div className=\"flex items-center justify-between\">\n <div className=\"flex items-center gap-2 mt-0.5\">\n <Sparkles className=\"h-4 w-4 text-brand-purple\" />\n <h2 className=\"text-xl font-bold\">Featured Collections</h2>\n </div>\n <a\n href={allCollectionsHref}\n className=\"inline-flex items-center gap-1 text-sm text-muted-foreground hover:text-foreground px-3 py-1.5 rounded-md hover:bg-accent transition-colors\"\n >\n View all <ArrowRight className=\"h-3.5 w-3.5\" />\n </a>\n </div>\n\n <div\n className=\"-mx-4 sm:-mx-6 lg:-mx-8 relative overflow-hidden aspect-[16/7] sm:aspect-[21/9] bg-muted\"\n onMouseEnter={() => setPaused(true)}\n onMouseLeave={() => setPaused(false)}\n >\n <AnimatePresence mode=\"wait\">\n <motion.div\n key={active}\n className=\"absolute inset-0\"\n initial={{ opacity: 0, x: 40 }}\n animate={{ opacity: 1, x: 0 }}\n exit={{ opacity: 0, x: -40 }}\n transition={{ duration: 0.45, ease: EASE_OUT }}\n >\n <Slide collection={collections[active]} href={getHref(collections[active])} />\n </motion.div>\n </AnimatePresence>\n\n {total > 1 && (\n <>\n <button\n onClick={prev}\n className=\"absolute left-3 top-1/2 -translate-y-1/2 h-9 w-9 rounded-full bg-black/40 border border-white/10 backdrop-blur-sm flex items-center justify-center text-white hover:bg-black/60 transition-colors z-10\"\n aria-label=\"Previous\"\n >\n <ChevronLeft className=\"h-4 w-4\" />\n </button>\n <button\n onClick={next}\n className=\"absolute right-3 top-1/2 -translate-y-1/2 h-9 w-9 rounded-full bg-black/40 border border-white/10 backdrop-blur-sm flex items-center justify-center text-white hover:bg-black/60 transition-colors z-10\"\n aria-label=\"Next\"\n >\n <ChevronRight className=\"h-4 w-4\" />\n </button>\n </>\n )}\n\n {total > 1 && (\n <div className=\"absolute bottom-4 left-1/2 -translate-x-1/2 flex items-center gap-1.5 z-10\">\n {collections.map((_, i) => (\n <button\n key={i}\n onClick={() => setActive(i)}\n className={`h-1.5 rounded-full transition-all duration-300 ${\n i === active ? \"w-6 bg-white\" : \"w-1.5 bg-white/40 hover:bg-white/70\"\n }`}\n aria-label={`Go to slide ${i + 1}`}\n />\n ))}\n </div>\n )}\n </div>\n </section>\n </FadeIn>\n );\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../src/components/featured-carousel.tsx"],"sourcesContent":["\"use client\";\n\nimport { useState, useEffect, useCallback } from \"react\";\nimport { motion, AnimatePresence } from \"framer-motion\";\nimport { ChevronLeft, ChevronRight, ArrowRight, Sparkles } from \"lucide-react\";\nimport { FadeIn, EASE_OUT } from \"./motion-primitives.js\";\nimport { ipfsToHttp } from \"../utils/ipfs.js\";\nimport { formatDisplayPrice } from \"../utils/format.js\";\nimport type { ApiCollection } from \"@medialane/sdk\";\n\nexport interface FeaturedCarouselProps {\n collections: ApiCollection[];\n isLoading: boolean;\n getHref: (collection: ApiCollection) => string;\n allCollectionsHref?: string;\n}\n\nfunction Slide({ collection, href }: { collection: ApiCollection; href: string }) {\n const name = collection.name ?? \"Featured Collection\";\n const image = collection.image ? ipfsToHttp(collection.image) : null;\n\n return (\n <div className=\"relative w-full h-full\">\n {image ? (\n <img\n src={image}\n alt={name}\n loading=\"lazy\"\n className=\"absolute inset-0 w-full h-full object-cover\"\n />\n ) : (\n <div className=\"absolute inset-0 bg-gradient-to-br from-brand-purple/30 via-brand-blue/20 to-brand-navy/40\" />\n )}\n <div className=\"absolute inset-0 bg-gradient-to-t from-black/85 via-black/30 to-black/10\" />\n <div className=\"absolute inset-0 bg-gradient-to-r from-black/50 via-transparent to-transparent\" />\n <div className=\"absolute bottom-0 left-0 right-0 p-5 sm:p-8 lg:p-10\">\n <h2 className=\"text-2xl sm:text-3xl lg:text-4xl font-black text-white leading-tight mb-3 max-w-lg\">\n {name}\n </h2>\n <div className=\"flex items-center gap-4 mb-5\">\n {collection.totalSupply != null && (\n <span className=\"text-sm text-white/70\">\n <span className=\"font-bold text-white\">{collection.totalSupply}</span> items\n </span>\n )}\n {collection.floorPrice && (\n <span className=\"text-sm text-white/70\">\n Floor{\" \"}\n <span className=\"font-bold text-brand-orange\">\n {formatDisplayPrice(collection.floorPrice)}\n </span>\n </span>\n )}\n </div>\n <a\n href={href}\n className=\"inline-flex items-center gap-2 text-sm font-semibold px-4 py-2 rounded-lg bg-white/15 hover:bg-white/25 text-white border border-white/20 backdrop-blur-sm transition-colors\"\n >\n View Collection <ArrowRight className=\"h-3.5 w-3.5\" />\n </a>\n </div>\n </div>\n );\n}\n\nexport function FeaturedCarouselSkeleton() {\n return (\n <FadeIn>\n <section className=\"space-y-4\">\n <div className=\"flex items-center gap-2 mt-0.5\">\n <Sparkles className=\"h-4 w-4 text-brand-purple\" />\n <h2 className=\"text-xl font-bold\">Featured Collections</h2>\n </div>\n <div className=\"-mx-4 sm:-mx-6 lg:-mx-8 aspect-[16/7] sm:aspect-[21/9] bg-muted animate-pulse\" />\n </section>\n </FadeIn>\n );\n}\n\nexport function FeaturedCarousel({\n collections,\n isLoading,\n getHref,\n allCollectionsHref = \"/collections\",\n}: FeaturedCarouselProps) {\n const [active, setActive] = useState(0);\n const [paused, setPaused] = useState(false);\n const total = collections.length;\n\n const next = useCallback(() => setActive((p) => (p + 1) % total), [total]);\n const prev = useCallback(() => setActive((p) => (p - 1 + total) % total), [total]);\n\n useEffect(() => { setActive(0); }, [total]);\n\n useEffect(() => {\n if (paused || total <= 1) return;\n const id = setInterval(next, 6000);\n return () => clearInterval(id);\n }, [next, paused, total]);\n\n if (isLoading) return <FeaturedCarouselSkeleton />;\n if (total === 0) return null;\n\n return (\n <FadeIn>\n <section className=\"space-y-4\">\n <div className=\"flex items-center justify-between\">\n <div className=\"flex items-center gap-2 mt-0.5\">\n <Sparkles className=\"h-4 w-4 text-brand-purple\" />\n <h2 className=\"text-xl font-bold\">Featured Collections</h2>\n </div>\n <a\n href={allCollectionsHref}\n className=\"inline-flex items-center gap-1 text-sm text-muted-foreground hover:text-foreground px-3 py-1.5 rounded-md hover:bg-accent transition-colors\"\n >\n View all <ArrowRight className=\"h-3.5 w-3.5\" />\n </a>\n </div>\n\n <div\n className=\"-mx-4 sm:-mx-6 lg:-mx-8 relative overflow-hidden aspect-[16/7] sm:aspect-[21/9] bg-muted\"\n onMouseEnter={() => setPaused(true)}\n onMouseLeave={() => setPaused(false)}\n >\n <AnimatePresence mode=\"wait\">\n <motion.div\n key={active}\n className=\"absolute inset-0\"\n initial={{ opacity: 0, x: 40 }}\n animate={{ opacity: 1, x: 0 }}\n exit={{ opacity: 0, x: -40 }}\n transition={{ duration: 0.45, ease: EASE_OUT }}\n >\n <Slide collection={collections[active]} href={getHref(collections[active])} />\n </motion.div>\n </AnimatePresence>\n\n {total > 1 && (\n <>\n <button\n onClick={prev}\n className=\"absolute left-3 top-1/2 -translate-y-1/2 min-h-11 min-w-11 rounded-full flex items-center justify-center z-10\"\n aria-label=\"Previous\"\n >\n <span className=\"flex h-9 w-9 items-center justify-center rounded-full bg-black/40 border border-white/10 backdrop-blur-sm text-white hover:bg-black/60 transition-colors\">\n <ChevronLeft className=\"h-4 w-4\" />\n </span>\n </button>\n <button\n onClick={next}\n className=\"absolute right-3 top-1/2 -translate-y-1/2 min-h-11 min-w-11 rounded-full flex items-center justify-center z-10\"\n aria-label=\"Next\"\n >\n <span className=\"flex h-9 w-9 items-center justify-center rounded-full bg-black/40 border border-white/10 backdrop-blur-sm text-white hover:bg-black/60 transition-colors\">\n <ChevronRight className=\"h-4 w-4\" />\n </span>\n </button>\n </>\n )}\n\n {total > 1 && (\n <div className=\"absolute bottom-4 left-1/2 -translate-x-1/2 flex items-center gap-1.5 z-10\">\n {collections.map((_, i) => (\n <button\n key={i}\n onClick={() => setActive(i)}\n className={`h-1.5 rounded-full transition-all duration-300 ${\n i === active ? \"w-6 bg-white\" : \"w-1.5 bg-white/40 hover:bg-white/70\"\n }`}\n aria-label={`Go to slide ${i + 1}`}\n />\n ))}\n </div>\n )}\n </div>\n </section>\n </FadeIn>\n );\n}\n"],"mappings":"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","names":[]}
|
|
@@ -120,18 +120,18 @@ function FeaturedCarousel({
|
|
|
120
120
|
"button",
|
|
121
121
|
{
|
|
122
122
|
onClick: prev,
|
|
123
|
-
className: "absolute left-3 top-1/2 -translate-y-1/2 h-
|
|
123
|
+
className: "absolute left-3 top-1/2 -translate-y-1/2 min-h-11 min-w-11 rounded-full flex items-center justify-center z-10",
|
|
124
124
|
"aria-label": "Previous",
|
|
125
|
-
children: /* @__PURE__ */ jsx(ChevronLeft, { className: "h-4 w-4" })
|
|
125
|
+
children: /* @__PURE__ */ jsx("span", { className: "flex h-9 w-9 items-center justify-center rounded-full bg-black/40 border border-white/10 backdrop-blur-sm text-white hover:bg-black/60 transition-colors", children: /* @__PURE__ */ jsx(ChevronLeft, { className: "h-4 w-4" }) })
|
|
126
126
|
}
|
|
127
127
|
),
|
|
128
128
|
/* @__PURE__ */ jsx(
|
|
129
129
|
"button",
|
|
130
130
|
{
|
|
131
131
|
onClick: next,
|
|
132
|
-
className: "absolute right-3 top-1/2 -translate-y-1/2 h-
|
|
132
|
+
className: "absolute right-3 top-1/2 -translate-y-1/2 min-h-11 min-w-11 rounded-full flex items-center justify-center z-10",
|
|
133
133
|
"aria-label": "Next",
|
|
134
|
-
children: /* @__PURE__ */ jsx(ChevronRight, { className: "h-4 w-4" })
|
|
134
|
+
children: /* @__PURE__ */ jsx("span", { className: "flex h-9 w-9 items-center justify-center rounded-full bg-black/40 border border-white/10 backdrop-blur-sm text-white hover:bg-black/60 transition-colors", children: /* @__PURE__ */ jsx(ChevronRight, { className: "h-4 w-4" }) })
|
|
135
135
|
}
|
|
136
136
|
)
|
|
137
137
|
] }),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/components/featured-carousel.tsx"],"sourcesContent":["\"use client\";\n\nimport { useState, useEffect, useCallback } from \"react\";\nimport { motion, AnimatePresence } from \"framer-motion\";\nimport { ChevronLeft, ChevronRight, ArrowRight, Sparkles } from \"lucide-react\";\nimport { FadeIn, EASE_OUT } from \"./motion-primitives.js\";\nimport { ipfsToHttp } from \"../utils/ipfs.js\";\nimport { formatDisplayPrice } from \"../utils/format.js\";\nimport type { ApiCollection } from \"@medialane/sdk\";\n\nexport interface FeaturedCarouselProps {\n collections: ApiCollection[];\n isLoading: boolean;\n getHref: (collection: ApiCollection) => string;\n allCollectionsHref?: string;\n}\n\nfunction Slide({ collection, href }: { collection: ApiCollection; href: string }) {\n const name = collection.name ?? \"Featured Collection\";\n const image = collection.image ? ipfsToHttp(collection.image) : null;\n\n return (\n <div className=\"relative w-full h-full\">\n {image ? (\n <img\n src={image}\n alt={name}\n loading=\"lazy\"\n className=\"absolute inset-0 w-full h-full object-cover\"\n />\n ) : (\n <div className=\"absolute inset-0 bg-gradient-to-br from-brand-purple/30 via-brand-blue/20 to-brand-navy/40\" />\n )}\n <div className=\"absolute inset-0 bg-gradient-to-t from-black/85 via-black/30 to-black/10\" />\n <div className=\"absolute inset-0 bg-gradient-to-r from-black/50 via-transparent to-transparent\" />\n <div className=\"absolute bottom-0 left-0 right-0 p-5 sm:p-8 lg:p-10\">\n <h2 className=\"text-2xl sm:text-3xl lg:text-4xl font-black text-white leading-tight mb-3 max-w-lg\">\n {name}\n </h2>\n <div className=\"flex items-center gap-4 mb-5\">\n {collection.totalSupply != null && (\n <span className=\"text-sm text-white/70\">\n <span className=\"font-bold text-white\">{collection.totalSupply}</span> items\n </span>\n )}\n {collection.floorPrice && (\n <span className=\"text-sm text-white/70\">\n Floor{\" \"}\n <span className=\"font-bold text-brand-orange\">\n {formatDisplayPrice(collection.floorPrice)}\n </span>\n </span>\n )}\n </div>\n <a\n href={href}\n className=\"inline-flex items-center gap-2 text-sm font-semibold px-4 py-2 rounded-lg bg-white/15 hover:bg-white/25 text-white border border-white/20 backdrop-blur-sm transition-colors\"\n >\n View Collection <ArrowRight className=\"h-3.5 w-3.5\" />\n </a>\n </div>\n </div>\n );\n}\n\nexport function FeaturedCarouselSkeleton() {\n return (\n <FadeIn>\n <section className=\"space-y-4\">\n <div className=\"flex items-center gap-2 mt-0.5\">\n <Sparkles className=\"h-4 w-4 text-brand-purple\" />\n <h2 className=\"text-xl font-bold\">Featured Collections</h2>\n </div>\n <div className=\"-mx-4 sm:-mx-6 lg:-mx-8 aspect-[16/7] sm:aspect-[21/9] bg-muted animate-pulse\" />\n </section>\n </FadeIn>\n );\n}\n\nexport function FeaturedCarousel({\n collections,\n isLoading,\n getHref,\n allCollectionsHref = \"/collections\",\n}: FeaturedCarouselProps) {\n const [active, setActive] = useState(0);\n const [paused, setPaused] = useState(false);\n const total = collections.length;\n\n const next = useCallback(() => setActive((p) => (p + 1) % total), [total]);\n const prev = useCallback(() => setActive((p) => (p - 1 + total) % total), [total]);\n\n useEffect(() => { setActive(0); }, [total]);\n\n useEffect(() => {\n if (paused || total <= 1) return;\n const id = setInterval(next, 6000);\n return () => clearInterval(id);\n }, [next, paused, total]);\n\n if (isLoading) return <FeaturedCarouselSkeleton />;\n if (total === 0) return null;\n\n return (\n <FadeIn>\n <section className=\"space-y-4\">\n <div className=\"flex items-center justify-between\">\n <div className=\"flex items-center gap-2 mt-0.5\">\n <Sparkles className=\"h-4 w-4 text-brand-purple\" />\n <h2 className=\"text-xl font-bold\">Featured Collections</h2>\n </div>\n <a\n href={allCollectionsHref}\n className=\"inline-flex items-center gap-1 text-sm text-muted-foreground hover:text-foreground px-3 py-1.5 rounded-md hover:bg-accent transition-colors\"\n >\n View all <ArrowRight className=\"h-3.5 w-3.5\" />\n </a>\n </div>\n\n <div\n className=\"-mx-4 sm:-mx-6 lg:-mx-8 relative overflow-hidden aspect-[16/7] sm:aspect-[21/9] bg-muted\"\n onMouseEnter={() => setPaused(true)}\n onMouseLeave={() => setPaused(false)}\n >\n <AnimatePresence mode=\"wait\">\n <motion.div\n key={active}\n className=\"absolute inset-0\"\n initial={{ opacity: 0, x: 40 }}\n animate={{ opacity: 1, x: 0 }}\n exit={{ opacity: 0, x: -40 }}\n transition={{ duration: 0.45, ease: EASE_OUT }}\n >\n <Slide collection={collections[active]} href={getHref(collections[active])} />\n </motion.div>\n </AnimatePresence>\n\n {total > 1 && (\n <>\n <button\n onClick={prev}\n className=\"absolute left-3 top-1/2 -translate-y-1/2 h-9 w-9 rounded-full bg-black/40 border border-white/10 backdrop-blur-sm flex items-center justify-center text-white hover:bg-black/60 transition-colors z-10\"\n aria-label=\"Previous\"\n >\n <ChevronLeft className=\"h-4 w-4\" />\n </button>\n <button\n onClick={next}\n className=\"absolute right-3 top-1/2 -translate-y-1/2 h-9 w-9 rounded-full bg-black/40 border border-white/10 backdrop-blur-sm flex items-center justify-center text-white hover:bg-black/60 transition-colors z-10\"\n aria-label=\"Next\"\n >\n <ChevronRight className=\"h-4 w-4\" />\n </button>\n </>\n )}\n\n {total > 1 && (\n <div className=\"absolute bottom-4 left-1/2 -translate-x-1/2 flex items-center gap-1.5 z-10\">\n {collections.map((_, i) => (\n <button\n key={i}\n onClick={() => setActive(i)}\n className={`h-1.5 rounded-full transition-all duration-300 ${\n i === active ? \"w-6 bg-white\" : \"w-1.5 bg-white/40 hover:bg-white/70\"\n }`}\n aria-label={`Go to slide ${i + 1}`}\n />\n ))}\n </div>\n )}\n </div>\n </section>\n </FadeIn>\n );\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../src/components/featured-carousel.tsx"],"sourcesContent":["\"use client\";\n\nimport { useState, useEffect, useCallback } from \"react\";\nimport { motion, AnimatePresence } from \"framer-motion\";\nimport { ChevronLeft, ChevronRight, ArrowRight, Sparkles } from \"lucide-react\";\nimport { FadeIn, EASE_OUT } from \"./motion-primitives.js\";\nimport { ipfsToHttp } from \"../utils/ipfs.js\";\nimport { formatDisplayPrice } from \"../utils/format.js\";\nimport type { ApiCollection } from \"@medialane/sdk\";\n\nexport interface FeaturedCarouselProps {\n collections: ApiCollection[];\n isLoading: boolean;\n getHref: (collection: ApiCollection) => string;\n allCollectionsHref?: string;\n}\n\nfunction Slide({ collection, href }: { collection: ApiCollection; href: string }) {\n const name = collection.name ?? \"Featured Collection\";\n const image = collection.image ? ipfsToHttp(collection.image) : null;\n\n return (\n <div className=\"relative w-full h-full\">\n {image ? (\n <img\n src={image}\n alt={name}\n loading=\"lazy\"\n className=\"absolute inset-0 w-full h-full object-cover\"\n />\n ) : (\n <div className=\"absolute inset-0 bg-gradient-to-br from-brand-purple/30 via-brand-blue/20 to-brand-navy/40\" />\n )}\n <div className=\"absolute inset-0 bg-gradient-to-t from-black/85 via-black/30 to-black/10\" />\n <div className=\"absolute inset-0 bg-gradient-to-r from-black/50 via-transparent to-transparent\" />\n <div className=\"absolute bottom-0 left-0 right-0 p-5 sm:p-8 lg:p-10\">\n <h2 className=\"text-2xl sm:text-3xl lg:text-4xl font-black text-white leading-tight mb-3 max-w-lg\">\n {name}\n </h2>\n <div className=\"flex items-center gap-4 mb-5\">\n {collection.totalSupply != null && (\n <span className=\"text-sm text-white/70\">\n <span className=\"font-bold text-white\">{collection.totalSupply}</span> items\n </span>\n )}\n {collection.floorPrice && (\n <span className=\"text-sm text-white/70\">\n Floor{\" \"}\n <span className=\"font-bold text-brand-orange\">\n {formatDisplayPrice(collection.floorPrice)}\n </span>\n </span>\n )}\n </div>\n <a\n href={href}\n className=\"inline-flex items-center gap-2 text-sm font-semibold px-4 py-2 rounded-lg bg-white/15 hover:bg-white/25 text-white border border-white/20 backdrop-blur-sm transition-colors\"\n >\n View Collection <ArrowRight className=\"h-3.5 w-3.5\" />\n </a>\n </div>\n </div>\n );\n}\n\nexport function FeaturedCarouselSkeleton() {\n return (\n <FadeIn>\n <section className=\"space-y-4\">\n <div className=\"flex items-center gap-2 mt-0.5\">\n <Sparkles className=\"h-4 w-4 text-brand-purple\" />\n <h2 className=\"text-xl font-bold\">Featured Collections</h2>\n </div>\n <div className=\"-mx-4 sm:-mx-6 lg:-mx-8 aspect-[16/7] sm:aspect-[21/9] bg-muted animate-pulse\" />\n </section>\n </FadeIn>\n );\n}\n\nexport function FeaturedCarousel({\n collections,\n isLoading,\n getHref,\n allCollectionsHref = \"/collections\",\n}: FeaturedCarouselProps) {\n const [active, setActive] = useState(0);\n const [paused, setPaused] = useState(false);\n const total = collections.length;\n\n const next = useCallback(() => setActive((p) => (p + 1) % total), [total]);\n const prev = useCallback(() => setActive((p) => (p - 1 + total) % total), [total]);\n\n useEffect(() => { setActive(0); }, [total]);\n\n useEffect(() => {\n if (paused || total <= 1) return;\n const id = setInterval(next, 6000);\n return () => clearInterval(id);\n }, [next, paused, total]);\n\n if (isLoading) return <FeaturedCarouselSkeleton />;\n if (total === 0) return null;\n\n return (\n <FadeIn>\n <section className=\"space-y-4\">\n <div className=\"flex items-center justify-between\">\n <div className=\"flex items-center gap-2 mt-0.5\">\n <Sparkles className=\"h-4 w-4 text-brand-purple\" />\n <h2 className=\"text-xl font-bold\">Featured Collections</h2>\n </div>\n <a\n href={allCollectionsHref}\n className=\"inline-flex items-center gap-1 text-sm text-muted-foreground hover:text-foreground px-3 py-1.5 rounded-md hover:bg-accent transition-colors\"\n >\n View all <ArrowRight className=\"h-3.5 w-3.5\" />\n </a>\n </div>\n\n <div\n className=\"-mx-4 sm:-mx-6 lg:-mx-8 relative overflow-hidden aspect-[16/7] sm:aspect-[21/9] bg-muted\"\n onMouseEnter={() => setPaused(true)}\n onMouseLeave={() => setPaused(false)}\n >\n <AnimatePresence mode=\"wait\">\n <motion.div\n key={active}\n className=\"absolute inset-0\"\n initial={{ opacity: 0, x: 40 }}\n animate={{ opacity: 1, x: 0 }}\n exit={{ opacity: 0, x: -40 }}\n transition={{ duration: 0.45, ease: EASE_OUT }}\n >\n <Slide collection={collections[active]} href={getHref(collections[active])} />\n </motion.div>\n </AnimatePresence>\n\n {total > 1 && (\n <>\n <button\n onClick={prev}\n className=\"absolute left-3 top-1/2 -translate-y-1/2 min-h-11 min-w-11 rounded-full flex items-center justify-center z-10\"\n aria-label=\"Previous\"\n >\n <span className=\"flex h-9 w-9 items-center justify-center rounded-full bg-black/40 border border-white/10 backdrop-blur-sm text-white hover:bg-black/60 transition-colors\">\n <ChevronLeft className=\"h-4 w-4\" />\n </span>\n </button>\n <button\n onClick={next}\n className=\"absolute right-3 top-1/2 -translate-y-1/2 min-h-11 min-w-11 rounded-full flex items-center justify-center z-10\"\n aria-label=\"Next\"\n >\n <span className=\"flex h-9 w-9 items-center justify-center rounded-full bg-black/40 border border-white/10 backdrop-blur-sm text-white hover:bg-black/60 transition-colors\">\n <ChevronRight className=\"h-4 w-4\" />\n </span>\n </button>\n </>\n )}\n\n {total > 1 && (\n <div className=\"absolute bottom-4 left-1/2 -translate-x-1/2 flex items-center gap-1.5 z-10\">\n {collections.map((_, i) => (\n <button\n key={i}\n onClick={() => setActive(i)}\n className={`h-1.5 rounded-full transition-all duration-300 ${\n i === active ? \"w-6 bg-white\" : \"w-1.5 bg-white/40 hover:bg-white/70\"\n }`}\n aria-label={`Go to slide ${i + 1}`}\n />\n ))}\n </div>\n )}\n </div>\n </section>\n </FadeIn>\n );\n}\n"],"mappings":"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","names":[]}
|
|
@@ -25,7 +25,7 @@ module.exports = __toCommonJS(gradient_button_exports);
|
|
|
25
25
|
var import_jsx_runtime = require("react/jsx-runtime");
|
|
26
26
|
var import_cn = require("../utils/cn.js");
|
|
27
27
|
function GradientButton({ big, children, className, wrapperClassName, ...rest }) {
|
|
28
|
-
const sizeClass = big ? "h-[54px] px-5 rounded-[15px] text-
|
|
28
|
+
const sizeClass = big ? "h-[54px] px-5 rounded-[15px] text-base" : "h-12 px-5 rounded-[11px] text-sm";
|
|
29
29
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: (0, import_cn.cn)("btn-border-animated w-full p-[1px]", big ? "rounded-2xl" : "rounded-xl", wrapperClassName), children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
30
30
|
"button",
|
|
31
31
|
{
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/components/gradient-button.tsx"],"sourcesContent":["\"use client\";\n\nimport { cn } from \"../utils/cn.js\";\n\nexport interface GradientButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {\n /** Tall 54px variant for hero contexts (onboarding, primary page CTAs). */\n big?: boolean;\n /** Outer wrapper class (the `.btn-border-animated` ring) — rarely needed. */\n wrapperClassName?: string;\n}\n\n/**\n * The full-spectrum animated-gradient CTA — `.btn-border-animated` as a 1px\n * wrapper around a transparent-fill button, so the ring reads as a solid,\n * continuously shifting brand-gradient fill (blue → purple → rose → orange).\n * This is the primary-action treatment used across medialane.io/starknet.medialane.io\n * (e.g. \"Connect wallet\", \"Buy now\") — previously hand-copied per call site;\n * this component is the single source.\n *\n * For the secondary animated-border treatment (transparent fill, only the\n * per-action gradient border and colored icon/text), use `ActionButton` instead.\n */\nexport function GradientButton({ big, children, className, wrapperClassName, ...rest }: GradientButtonProps) {\n const sizeClass = big\n ? \"h-[54px] px-5 rounded-[15px] text-
|
|
1
|
+
{"version":3,"sources":["../../src/components/gradient-button.tsx"],"sourcesContent":["\"use client\";\n\nimport { cn } from \"../utils/cn.js\";\n\nexport interface GradientButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {\n /** Tall 54px variant for hero contexts (onboarding, primary page CTAs). */\n big?: boolean;\n /** Outer wrapper class (the `.btn-border-animated` ring) — rarely needed. */\n wrapperClassName?: string;\n}\n\n/**\n * The full-spectrum animated-gradient CTA — `.btn-border-animated` as a 1px\n * wrapper around a transparent-fill button, so the ring reads as a solid,\n * continuously shifting brand-gradient fill (blue → purple → rose → orange).\n * This is the primary-action treatment used across medialane.io/starknet.medialane.io\n * (e.g. \"Connect wallet\", \"Buy now\") — previously hand-copied per call site;\n * this component is the single source.\n *\n * For the secondary animated-border treatment (transparent fill, only the\n * per-action gradient border and colored icon/text), use `ActionButton` instead.\n */\nexport function GradientButton({ big, children, className, wrapperClassName, ...rest }: GradientButtonProps) {\n const sizeClass = big\n ? \"h-[54px] px-5 rounded-[15px] text-base\"\n : \"h-12 px-5 rounded-[11px] text-sm\";\n\n return (\n <div className={cn(\"btn-border-animated w-full p-[1px]\", big ? \"rounded-2xl\" : \"rounded-xl\", wrapperClassName)}>\n <button\n {...rest}\n className={cn(\n \"inline-flex w-full items-center justify-center gap-2 whitespace-nowrap font-semibold text-white\",\n \"border-none bg-transparent transition-all hover:brightness-110 active:scale-[0.98]\",\n sizeClass,\n className,\n )}\n >\n {children}\n </button>\n </div>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AA6BM;AA3BN,gBAAmB;AAoBZ,SAAS,eAAe,EAAE,KAAK,UAAU,WAAW,kBAAkB,GAAG,KAAK,GAAwB;AAC3G,QAAM,YAAY,MACd,2CACA;AAEJ,SACE,4CAAC,SAAI,eAAW,cAAG,sCAAsC,MAAM,gBAAgB,cAAc,gBAAgB,GAC3G;AAAA,IAAC;AAAA;AAAA,MACE,GAAG;AAAA,MACJ,eAAW;AAAA,QACT;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAAA,MAEC;AAAA;AAAA,EACH,GACF;AAEJ;","names":[]}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import { jsx } from "react/jsx-runtime";
|
|
3
3
|
import { cn } from "../utils/cn.js";
|
|
4
4
|
function GradientButton({ big, children, className, wrapperClassName, ...rest }) {
|
|
5
|
-
const sizeClass = big ? "h-[54px] px-5 rounded-[15px] text-
|
|
5
|
+
const sizeClass = big ? "h-[54px] px-5 rounded-[15px] text-base" : "h-12 px-5 rounded-[11px] text-sm";
|
|
6
6
|
return /* @__PURE__ */ jsx("div", { className: cn("btn-border-animated w-full p-[1px]", big ? "rounded-2xl" : "rounded-xl", wrapperClassName), children: /* @__PURE__ */ jsx(
|
|
7
7
|
"button",
|
|
8
8
|
{
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/components/gradient-button.tsx"],"sourcesContent":["\"use client\";\n\nimport { cn } from \"../utils/cn.js\";\n\nexport interface GradientButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {\n /** Tall 54px variant for hero contexts (onboarding, primary page CTAs). */\n big?: boolean;\n /** Outer wrapper class (the `.btn-border-animated` ring) — rarely needed. */\n wrapperClassName?: string;\n}\n\n/**\n * The full-spectrum animated-gradient CTA — `.btn-border-animated` as a 1px\n * wrapper around a transparent-fill button, so the ring reads as a solid,\n * continuously shifting brand-gradient fill (blue → purple → rose → orange).\n * This is the primary-action treatment used across medialane.io/starknet.medialane.io\n * (e.g. \"Connect wallet\", \"Buy now\") — previously hand-copied per call site;\n * this component is the single source.\n *\n * For the secondary animated-border treatment (transparent fill, only the\n * per-action gradient border and colored icon/text), use `ActionButton` instead.\n */\nexport function GradientButton({ big, children, className, wrapperClassName, ...rest }: GradientButtonProps) {\n const sizeClass = big\n ? \"h-[54px] px-5 rounded-[15px] text-
|
|
1
|
+
{"version":3,"sources":["../../src/components/gradient-button.tsx"],"sourcesContent":["\"use client\";\n\nimport { cn } from \"../utils/cn.js\";\n\nexport interface GradientButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {\n /** Tall 54px variant for hero contexts (onboarding, primary page CTAs). */\n big?: boolean;\n /** Outer wrapper class (the `.btn-border-animated` ring) — rarely needed. */\n wrapperClassName?: string;\n}\n\n/**\n * The full-spectrum animated-gradient CTA — `.btn-border-animated` as a 1px\n * wrapper around a transparent-fill button, so the ring reads as a solid,\n * continuously shifting brand-gradient fill (blue → purple → rose → orange).\n * This is the primary-action treatment used across medialane.io/starknet.medialane.io\n * (e.g. \"Connect wallet\", \"Buy now\") — previously hand-copied per call site;\n * this component is the single source.\n *\n * For the secondary animated-border treatment (transparent fill, only the\n * per-action gradient border and colored icon/text), use `ActionButton` instead.\n */\nexport function GradientButton({ big, children, className, wrapperClassName, ...rest }: GradientButtonProps) {\n const sizeClass = big\n ? \"h-[54px] px-5 rounded-[15px] text-base\"\n : \"h-12 px-5 rounded-[11px] text-sm\";\n\n return (\n <div className={cn(\"btn-border-animated w-full p-[1px]\", big ? \"rounded-2xl\" : \"rounded-xl\", wrapperClassName)}>\n <button\n {...rest}\n className={cn(\n \"inline-flex w-full items-center justify-center gap-2 whitespace-nowrap font-semibold text-white\",\n \"border-none bg-transparent transition-all hover:brightness-110 active:scale-[0.98]\",\n sizeClass,\n className,\n )}\n >\n {children}\n </button>\n </div>\n );\n}\n"],"mappings":";AA6BM;AA3BN,SAAS,UAAU;AAoBZ,SAAS,eAAe,EAAE,KAAK,UAAU,WAAW,kBAAkB,GAAG,KAAK,GAAwB;AAC3G,QAAM,YAAY,MACd,2CACA;AAEJ,SACE,oBAAC,SAAI,WAAW,GAAG,sCAAsC,MAAM,gBAAgB,cAAc,gBAAgB,GAC3G;AAAA,IAAC;AAAA;AAAA,MACE,GAAG;AAAA,MACJ,WAAW;AAAA,QACT;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAAA,MAEC;AAAA;AAAA,EACH,GACF;AAEJ;","names":[]}
|
|
@@ -102,8 +102,8 @@ function HeroSlider({ collections, isLoading, getHref, placeholderHrefs = {} })
|
|
|
102
102
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("section", { className: "relative w-full h-[78vw] min-h-[420px] max-h-[768px] sm:h-[72vh] sm:max-h-[816px] overflow-hidden bg-muted", children: count === 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(HeroPlaceholder, { hrefs }) : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
|
|
103
103
|
collections.map((col, i) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(HeroSlide, { collection: col, active: i === current, getHref }, col.contractAddress)),
|
|
104
104
|
count > 1 && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
|
|
105
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { onClick: prev, "aria-label": "Previous slide", className: "absolute left-3 top-1/2 -translate-y-1/2 z-10 h-9 w-9 rounded-full bg-white/10 backdrop-blur-md hover:bg-white/20
|
|
106
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { onClick: next, "aria-label": "Next slide", className: "absolute right-3 top-1/2 -translate-y-1/2 z-10 h-9 w-9 rounded-full bg-white/10 backdrop-blur-md hover:bg-white/20
|
|
105
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { onClick: prev, "aria-label": "Previous slide", className: "absolute left-3 top-1/2 -translate-y-1/2 z-10 min-h-11 min-w-11 rounded-full flex items-center justify-center transition-colors", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "flex h-9 w-9 items-center justify-center rounded-full bg-white/10 backdrop-blur-md hover:bg-white/20", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_lucide_react.ChevronLeft, { className: "h-5 w-5 text-white" }) }) }),
|
|
106
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { onClick: next, "aria-label": "Next slide", className: "absolute right-3 top-1/2 -translate-y-1/2 z-10 min-h-11 min-w-11 rounded-full flex items-center justify-center transition-colors", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "flex h-9 w-9 items-center justify-center rounded-full bg-white/10 backdrop-blur-md hover:bg-white/20", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_lucide_react.ChevronRight, { className: "h-5 w-5 text-white" }) }) }),
|
|
107
107
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "absolute bottom-4 left-1/2 -translate-x-1/2 flex gap-1.5 z-10", children: collections.map((_, i) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { onClick: () => setCurrent(i), "aria-label": `Go to slide ${i + 1}`, className: (0, import_cn.cn)("h-1.5 rounded-full transition-all", i === current ? "w-6 bg-white" : "w-1.5 bg-white/40") }, i)) })
|
|
108
108
|
] })
|
|
109
109
|
] }) });
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/components/hero-slider.tsx"],"sourcesContent":["\"use client\";\n\nimport { useState, useEffect, useCallback } from \"react\";\nimport Link from \"next/link\";\nimport Image from \"next/image\";\nimport { ChevronLeft, ChevronRight } from \"lucide-react\";\nimport { cn } from \"../utils/cn.js\";\nimport { ipfsToHttp } from \"../utils/ipfs.js\";\nimport type { ApiCollection } from \"@medialane/sdk\";\n\nexport interface HeroSliderProps {\n collections: ApiCollection[];\n isLoading: boolean;\n getHref: (collection: ApiCollection) => string;\n /** Optional placeholder CTA hrefs — defaults to \"/marketplace\" and \"/create/asset\" */\n placeholderHrefs?: { markets?: string; create?: string };\n}\n\nfunction formatFloorPrice(price: string | null | undefined): string {\n if (!price) return \"\";\n const n = parseFloat(price);\n if (isNaN(n)) return price;\n if (n >= 1000) return `${(n / 1000).toFixed(1)}K`;\n if (n >= 1) return n.toFixed(2);\n return n.toPrecision(3);\n}\n\nfunction HeroPlaceholder({ hrefs }: { hrefs: Required<HeroSliderProps>[\"placeholderHrefs\"] }) {\n return (\n <div className=\"absolute inset-0 bg-gradient-to-br from-brand-purple/30 via-brand-blue/20 to-brand-navy/50 flex flex-col items-center justify-center gap-4 text-center px-6 overflow-hidden\">\n\n <h2 className=\"text-4xl sm:text-6xl font-black gradient-text relative z-10\">Medialane</h2>\n <p className=\"text-muted-foreground text-lg relative z-10 max-w-md\">\n New monetization revenues for creative works\n </p>\n <div className=\"flex gap-3 relative z-10\">\n <Link href={hrefs.markets!} className=\"inline-flex items-center justify-center rounded-[11px] bg-brand-blue px-4 py-2 text-sm font-semibold text-white hover:brightness-110 active:scale-[0.98] transition-all\">\n Markets\n </Link>\n <Link href={hrefs.create!} className=\"inline-flex items-center justify-center rounded-[11px] border border-white/20 bg-background/20 backdrop-blur-sm px-4 py-2 text-sm font-semibold text-white hover:bg-white/10 transition-all\">\n Create\n </Link>\n </div>\n </div>\n );\n}\n\nfunction HeroSlide({ collection, active, getHref }: { collection: ApiCollection; active: boolean; getHref: (col: ApiCollection) => string }) {\n const imageUrl = collection.image ? ipfsToHttp(collection.image) : null;\n const name = collection.name ?? \"Collection\";\n const floor = collection.floorPrice;\n const supply = collection.totalSupply;\n\n return (\n <div className={cn(\"absolute inset-0 transition-opacity duration-700\", active ? \"opacity-100\" : \"opacity-0 pointer-events-none\")}>\n {imageUrl ? (\n <div className=\"absolute inset-0 overflow-hidden\">\n <div className=\"animate-kenburns absolute inset-0\">\n <Image src={imageUrl} alt={name} fill className=\"object-cover\" priority={active} unoptimized />\n </div>\n </div>\n ) : (\n <div className=\"absolute inset-0 bg-gradient-to-br from-brand-purple/40 via-brand-blue/20 to-brand-navy/60\" />\n )}\n <div className=\"absolute bottom-0 left-0 right-0 p-6 sm:p-10 flex flex-col gap-3\">\n <Link href={getHref(collection)} className=\"hover:opacity-90 transition-opacity\">\n <h2 className=\"text-4xl lg:text-5xl font-semibold text-white leading-tight\">{name}</h2>\n </Link>\n <div className=\"flex items-center gap-4 text-sm text-white/70\">\n {supply != null && <span>{supply.toLocaleString()} items</span>}\n {floor && <span className=\"text-white font-semibold\">Floor {formatFloorPrice(floor)}</span>}\n </div>\n </div>\n </div>\n );\n}\n\nexport function HeroSliderSkeleton() {\n return <section className=\"relative w-full h-[78vw] min-h-[420px] max-h-[768px] sm:h-[72vh] sm:max-h-[816px] bg-muted animate-pulse\" />;\n}\n\nexport function HeroSlider({ collections, isLoading, getHref, placeholderHrefs = {} }: HeroSliderProps) {\n const hrefs = { markets: \"/marketplace\", create: \"/create/asset\", ...placeholderHrefs };\n const [current, setCurrent] = useState(0);\n const count = collections.length;\n\n const next = useCallback(() => { if (count > 1) setCurrent((c) => (c + 1) % count); }, [count]);\n const prev = useCallback(() => { if (count > 1) setCurrent((c) => (c - 1 + count) % count); }, [count]);\n\n useEffect(() => {\n if (count <= 1) return;\n const id = setInterval(next, 7000);\n return () => clearInterval(id);\n }, [count, next]);\n\n if (isLoading) return <HeroSliderSkeleton />;\n\n return (\n <section className=\"relative w-full h-[78vw] min-h-[420px] max-h-[768px] sm:h-[72vh] sm:max-h-[816px] overflow-hidden bg-muted\">\n {count === 0 ? (\n <HeroPlaceholder hrefs={hrefs} />\n ) : (\n <>\n {collections.map((col, i) => (\n <HeroSlide key={col.contractAddress} collection={col} active={i === current} getHref={getHref} />\n ))}\n {count > 1 && (\n <>\n <button onClick={prev} aria-label=\"Previous slide\" className=\"absolute left-3 top-1/2 -translate-y-1/2 z-10 h-9 w-9 rounded-full bg-white/10 backdrop-blur-md hover:bg-white/20
|
|
1
|
+
{"version":3,"sources":["../../src/components/hero-slider.tsx"],"sourcesContent":["\"use client\";\n\nimport { useState, useEffect, useCallback } from \"react\";\nimport Link from \"next/link\";\nimport Image from \"next/image\";\nimport { ChevronLeft, ChevronRight } from \"lucide-react\";\nimport { cn } from \"../utils/cn.js\";\nimport { ipfsToHttp } from \"../utils/ipfs.js\";\nimport type { ApiCollection } from \"@medialane/sdk\";\n\nexport interface HeroSliderProps {\n collections: ApiCollection[];\n isLoading: boolean;\n getHref: (collection: ApiCollection) => string;\n /** Optional placeholder CTA hrefs — defaults to \"/marketplace\" and \"/create/asset\" */\n placeholderHrefs?: { markets?: string; create?: string };\n}\n\nfunction formatFloorPrice(price: string | null | undefined): string {\n if (!price) return \"\";\n const n = parseFloat(price);\n if (isNaN(n)) return price;\n if (n >= 1000) return `${(n / 1000).toFixed(1)}K`;\n if (n >= 1) return n.toFixed(2);\n return n.toPrecision(3);\n}\n\nfunction HeroPlaceholder({ hrefs }: { hrefs: Required<HeroSliderProps>[\"placeholderHrefs\"] }) {\n return (\n <div className=\"absolute inset-0 bg-gradient-to-br from-brand-purple/30 via-brand-blue/20 to-brand-navy/50 flex flex-col items-center justify-center gap-4 text-center px-6 overflow-hidden\">\n\n <h2 className=\"text-4xl sm:text-6xl font-black gradient-text relative z-10\">Medialane</h2>\n <p className=\"text-muted-foreground text-lg relative z-10 max-w-md\">\n New monetization revenues for creative works\n </p>\n <div className=\"flex gap-3 relative z-10\">\n <Link href={hrefs.markets!} className=\"inline-flex items-center justify-center rounded-[11px] bg-brand-blue px-4 py-2 text-sm font-semibold text-white hover:brightness-110 active:scale-[0.98] transition-all\">\n Markets\n </Link>\n <Link href={hrefs.create!} className=\"inline-flex items-center justify-center rounded-[11px] border border-white/20 bg-background/20 backdrop-blur-sm px-4 py-2 text-sm font-semibold text-white hover:bg-white/10 transition-all\">\n Create\n </Link>\n </div>\n </div>\n );\n}\n\nfunction HeroSlide({ collection, active, getHref }: { collection: ApiCollection; active: boolean; getHref: (col: ApiCollection) => string }) {\n const imageUrl = collection.image ? ipfsToHttp(collection.image) : null;\n const name = collection.name ?? \"Collection\";\n const floor = collection.floorPrice;\n const supply = collection.totalSupply;\n\n return (\n <div className={cn(\"absolute inset-0 transition-opacity duration-700\", active ? \"opacity-100\" : \"opacity-0 pointer-events-none\")}>\n {imageUrl ? (\n <div className=\"absolute inset-0 overflow-hidden\">\n <div className=\"animate-kenburns absolute inset-0\">\n <Image src={imageUrl} alt={name} fill className=\"object-cover\" priority={active} unoptimized />\n </div>\n </div>\n ) : (\n <div className=\"absolute inset-0 bg-gradient-to-br from-brand-purple/40 via-brand-blue/20 to-brand-navy/60\" />\n )}\n <div className=\"absolute bottom-0 left-0 right-0 p-6 sm:p-10 flex flex-col gap-3\">\n <Link href={getHref(collection)} className=\"hover:opacity-90 transition-opacity\">\n <h2 className=\"text-4xl lg:text-5xl font-semibold text-white leading-tight\">{name}</h2>\n </Link>\n <div className=\"flex items-center gap-4 text-sm text-white/70\">\n {supply != null && <span>{supply.toLocaleString()} items</span>}\n {floor && <span className=\"text-white font-semibold\">Floor {formatFloorPrice(floor)}</span>}\n </div>\n </div>\n </div>\n );\n}\n\nexport function HeroSliderSkeleton() {\n return <section className=\"relative w-full h-[78vw] min-h-[420px] max-h-[768px] sm:h-[72vh] sm:max-h-[816px] bg-muted animate-pulse\" />;\n}\n\nexport function HeroSlider({ collections, isLoading, getHref, placeholderHrefs = {} }: HeroSliderProps) {\n const hrefs = { markets: \"/marketplace\", create: \"/create/asset\", ...placeholderHrefs };\n const [current, setCurrent] = useState(0);\n const count = collections.length;\n\n const next = useCallback(() => { if (count > 1) setCurrent((c) => (c + 1) % count); }, [count]);\n const prev = useCallback(() => { if (count > 1) setCurrent((c) => (c - 1 + count) % count); }, [count]);\n\n useEffect(() => {\n if (count <= 1) return;\n const id = setInterval(next, 7000);\n return () => clearInterval(id);\n }, [count, next]);\n\n if (isLoading) return <HeroSliderSkeleton />;\n\n return (\n <section className=\"relative w-full h-[78vw] min-h-[420px] max-h-[768px] sm:h-[72vh] sm:max-h-[816px] overflow-hidden bg-muted\">\n {count === 0 ? (\n <HeroPlaceholder hrefs={hrefs} />\n ) : (\n <>\n {collections.map((col, i) => (\n <HeroSlide key={col.contractAddress} collection={col} active={i === current} getHref={getHref} />\n ))}\n {count > 1 && (\n <>\n <button onClick={prev} aria-label=\"Previous slide\" className=\"absolute left-3 top-1/2 -translate-y-1/2 z-10 min-h-11 min-w-11 rounded-full flex items-center justify-center transition-colors\">\n <span className=\"flex h-9 w-9 items-center justify-center rounded-full bg-white/10 backdrop-blur-md hover:bg-white/20\">\n <ChevronLeft className=\"h-5 w-5 text-white\" />\n </span>\n </button>\n <button onClick={next} aria-label=\"Next slide\" className=\"absolute right-3 top-1/2 -translate-y-1/2 z-10 min-h-11 min-w-11 rounded-full flex items-center justify-center transition-colors\">\n <span className=\"flex h-9 w-9 items-center justify-center rounded-full bg-white/10 backdrop-blur-md hover:bg-white/20\">\n <ChevronRight className=\"h-5 w-5 text-white\" />\n </span>\n </button>\n <div className=\"absolute bottom-4 left-1/2 -translate-x-1/2 flex gap-1.5 z-10\">\n {collections.map((_, i) => (\n <button key={i} onClick={() => setCurrent(i)} aria-label={`Go to slide ${i + 1}`} className={cn(\"h-1.5 rounded-full transition-all\", i === current ? \"w-6 bg-white\" : \"w-1.5 bg-white/40\")} />\n ))}\n </div>\n </>\n )}\n </>\n )}\n </section>\n );\n}\n"],"mappings":"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","names":["Link","Image"]}
|
|
@@ -68,8 +68,8 @@ function HeroSlider({ collections, isLoading, getHref, placeholderHrefs = {} })
|
|
|
68
68
|
return /* @__PURE__ */ jsx("section", { className: "relative w-full h-[78vw] min-h-[420px] max-h-[768px] sm:h-[72vh] sm:max-h-[816px] overflow-hidden bg-muted", children: count === 0 ? /* @__PURE__ */ jsx(HeroPlaceholder, { hrefs }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
69
69
|
collections.map((col, i) => /* @__PURE__ */ jsx(HeroSlide, { collection: col, active: i === current, getHref }, col.contractAddress)),
|
|
70
70
|
count > 1 && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
71
|
-
/* @__PURE__ */ jsx("button", { onClick: prev, "aria-label": "Previous slide", className: "absolute left-3 top-1/2 -translate-y-1/2 z-10 h-9 w-9 rounded-full bg-white/10 backdrop-blur-md hover:bg-white/20
|
|
72
|
-
/* @__PURE__ */ jsx("button", { onClick: next, "aria-label": "Next slide", className: "absolute right-3 top-1/2 -translate-y-1/2 z-10 h-9 w-9 rounded-full bg-white/10 backdrop-blur-md hover:bg-white/20
|
|
71
|
+
/* @__PURE__ */ jsx("button", { onClick: prev, "aria-label": "Previous slide", className: "absolute left-3 top-1/2 -translate-y-1/2 z-10 min-h-11 min-w-11 rounded-full flex items-center justify-center transition-colors", children: /* @__PURE__ */ jsx("span", { className: "flex h-9 w-9 items-center justify-center rounded-full bg-white/10 backdrop-blur-md hover:bg-white/20", children: /* @__PURE__ */ jsx(ChevronLeft, { className: "h-5 w-5 text-white" }) }) }),
|
|
72
|
+
/* @__PURE__ */ jsx("button", { onClick: next, "aria-label": "Next slide", className: "absolute right-3 top-1/2 -translate-y-1/2 z-10 min-h-11 min-w-11 rounded-full flex items-center justify-center transition-colors", children: /* @__PURE__ */ jsx("span", { className: "flex h-9 w-9 items-center justify-center rounded-full bg-white/10 backdrop-blur-md hover:bg-white/20", children: /* @__PURE__ */ jsx(ChevronRight, { className: "h-5 w-5 text-white" }) }) }),
|
|
73
73
|
/* @__PURE__ */ jsx("div", { className: "absolute bottom-4 left-1/2 -translate-x-1/2 flex gap-1.5 z-10", children: collections.map((_, i) => /* @__PURE__ */ jsx("button", { onClick: () => setCurrent(i), "aria-label": `Go to slide ${i + 1}`, className: cn("h-1.5 rounded-full transition-all", i === current ? "w-6 bg-white" : "w-1.5 bg-white/40") }, i)) })
|
|
74
74
|
] })
|
|
75
75
|
] }) });
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/components/hero-slider.tsx"],"sourcesContent":["\"use client\";\n\nimport { useState, useEffect, useCallback } from \"react\";\nimport Link from \"next/link\";\nimport Image from \"next/image\";\nimport { ChevronLeft, ChevronRight } from \"lucide-react\";\nimport { cn } from \"../utils/cn.js\";\nimport { ipfsToHttp } from \"../utils/ipfs.js\";\nimport type { ApiCollection } from \"@medialane/sdk\";\n\nexport interface HeroSliderProps {\n collections: ApiCollection[];\n isLoading: boolean;\n getHref: (collection: ApiCollection) => string;\n /** Optional placeholder CTA hrefs — defaults to \"/marketplace\" and \"/create/asset\" */\n placeholderHrefs?: { markets?: string; create?: string };\n}\n\nfunction formatFloorPrice(price: string | null | undefined): string {\n if (!price) return \"\";\n const n = parseFloat(price);\n if (isNaN(n)) return price;\n if (n >= 1000) return `${(n / 1000).toFixed(1)}K`;\n if (n >= 1) return n.toFixed(2);\n return n.toPrecision(3);\n}\n\nfunction HeroPlaceholder({ hrefs }: { hrefs: Required<HeroSliderProps>[\"placeholderHrefs\"] }) {\n return (\n <div className=\"absolute inset-0 bg-gradient-to-br from-brand-purple/30 via-brand-blue/20 to-brand-navy/50 flex flex-col items-center justify-center gap-4 text-center px-6 overflow-hidden\">\n\n <h2 className=\"text-4xl sm:text-6xl font-black gradient-text relative z-10\">Medialane</h2>\n <p className=\"text-muted-foreground text-lg relative z-10 max-w-md\">\n New monetization revenues for creative works\n </p>\n <div className=\"flex gap-3 relative z-10\">\n <Link href={hrefs.markets!} className=\"inline-flex items-center justify-center rounded-[11px] bg-brand-blue px-4 py-2 text-sm font-semibold text-white hover:brightness-110 active:scale-[0.98] transition-all\">\n Markets\n </Link>\n <Link href={hrefs.create!} className=\"inline-flex items-center justify-center rounded-[11px] border border-white/20 bg-background/20 backdrop-blur-sm px-4 py-2 text-sm font-semibold text-white hover:bg-white/10 transition-all\">\n Create\n </Link>\n </div>\n </div>\n );\n}\n\nfunction HeroSlide({ collection, active, getHref }: { collection: ApiCollection; active: boolean; getHref: (col: ApiCollection) => string }) {\n const imageUrl = collection.image ? ipfsToHttp(collection.image) : null;\n const name = collection.name ?? \"Collection\";\n const floor = collection.floorPrice;\n const supply = collection.totalSupply;\n\n return (\n <div className={cn(\"absolute inset-0 transition-opacity duration-700\", active ? \"opacity-100\" : \"opacity-0 pointer-events-none\")}>\n {imageUrl ? (\n <div className=\"absolute inset-0 overflow-hidden\">\n <div className=\"animate-kenburns absolute inset-0\">\n <Image src={imageUrl} alt={name} fill className=\"object-cover\" priority={active} unoptimized />\n </div>\n </div>\n ) : (\n <div className=\"absolute inset-0 bg-gradient-to-br from-brand-purple/40 via-brand-blue/20 to-brand-navy/60\" />\n )}\n <div className=\"absolute bottom-0 left-0 right-0 p-6 sm:p-10 flex flex-col gap-3\">\n <Link href={getHref(collection)} className=\"hover:opacity-90 transition-opacity\">\n <h2 className=\"text-4xl lg:text-5xl font-semibold text-white leading-tight\">{name}</h2>\n </Link>\n <div className=\"flex items-center gap-4 text-sm text-white/70\">\n {supply != null && <span>{supply.toLocaleString()} items</span>}\n {floor && <span className=\"text-white font-semibold\">Floor {formatFloorPrice(floor)}</span>}\n </div>\n </div>\n </div>\n );\n}\n\nexport function HeroSliderSkeleton() {\n return <section className=\"relative w-full h-[78vw] min-h-[420px] max-h-[768px] sm:h-[72vh] sm:max-h-[816px] bg-muted animate-pulse\" />;\n}\n\nexport function HeroSlider({ collections, isLoading, getHref, placeholderHrefs = {} }: HeroSliderProps) {\n const hrefs = { markets: \"/marketplace\", create: \"/create/asset\", ...placeholderHrefs };\n const [current, setCurrent] = useState(0);\n const count = collections.length;\n\n const next = useCallback(() => { if (count > 1) setCurrent((c) => (c + 1) % count); }, [count]);\n const prev = useCallback(() => { if (count > 1) setCurrent((c) => (c - 1 + count) % count); }, [count]);\n\n useEffect(() => {\n if (count <= 1) return;\n const id = setInterval(next, 7000);\n return () => clearInterval(id);\n }, [count, next]);\n\n if (isLoading) return <HeroSliderSkeleton />;\n\n return (\n <section className=\"relative w-full h-[78vw] min-h-[420px] max-h-[768px] sm:h-[72vh] sm:max-h-[816px] overflow-hidden bg-muted\">\n {count === 0 ? (\n <HeroPlaceholder hrefs={hrefs} />\n ) : (\n <>\n {collections.map((col, i) => (\n <HeroSlide key={col.contractAddress} collection={col} active={i === current} getHref={getHref} />\n ))}\n {count > 1 && (\n <>\n <button onClick={prev} aria-label=\"Previous slide\" className=\"absolute left-3 top-1/2 -translate-y-1/2 z-10 h-9 w-9 rounded-full bg-white/10 backdrop-blur-md hover:bg-white/20
|
|
1
|
+
{"version":3,"sources":["../../src/components/hero-slider.tsx"],"sourcesContent":["\"use client\";\n\nimport { useState, useEffect, useCallback } from \"react\";\nimport Link from \"next/link\";\nimport Image from \"next/image\";\nimport { ChevronLeft, ChevronRight } from \"lucide-react\";\nimport { cn } from \"../utils/cn.js\";\nimport { ipfsToHttp } from \"../utils/ipfs.js\";\nimport type { ApiCollection } from \"@medialane/sdk\";\n\nexport interface HeroSliderProps {\n collections: ApiCollection[];\n isLoading: boolean;\n getHref: (collection: ApiCollection) => string;\n /** Optional placeholder CTA hrefs — defaults to \"/marketplace\" and \"/create/asset\" */\n placeholderHrefs?: { markets?: string; create?: string };\n}\n\nfunction formatFloorPrice(price: string | null | undefined): string {\n if (!price) return \"\";\n const n = parseFloat(price);\n if (isNaN(n)) return price;\n if (n >= 1000) return `${(n / 1000).toFixed(1)}K`;\n if (n >= 1) return n.toFixed(2);\n return n.toPrecision(3);\n}\n\nfunction HeroPlaceholder({ hrefs }: { hrefs: Required<HeroSliderProps>[\"placeholderHrefs\"] }) {\n return (\n <div className=\"absolute inset-0 bg-gradient-to-br from-brand-purple/30 via-brand-blue/20 to-brand-navy/50 flex flex-col items-center justify-center gap-4 text-center px-6 overflow-hidden\">\n\n <h2 className=\"text-4xl sm:text-6xl font-black gradient-text relative z-10\">Medialane</h2>\n <p className=\"text-muted-foreground text-lg relative z-10 max-w-md\">\n New monetization revenues for creative works\n </p>\n <div className=\"flex gap-3 relative z-10\">\n <Link href={hrefs.markets!} className=\"inline-flex items-center justify-center rounded-[11px] bg-brand-blue px-4 py-2 text-sm font-semibold text-white hover:brightness-110 active:scale-[0.98] transition-all\">\n Markets\n </Link>\n <Link href={hrefs.create!} className=\"inline-flex items-center justify-center rounded-[11px] border border-white/20 bg-background/20 backdrop-blur-sm px-4 py-2 text-sm font-semibold text-white hover:bg-white/10 transition-all\">\n Create\n </Link>\n </div>\n </div>\n );\n}\n\nfunction HeroSlide({ collection, active, getHref }: { collection: ApiCollection; active: boolean; getHref: (col: ApiCollection) => string }) {\n const imageUrl = collection.image ? ipfsToHttp(collection.image) : null;\n const name = collection.name ?? \"Collection\";\n const floor = collection.floorPrice;\n const supply = collection.totalSupply;\n\n return (\n <div className={cn(\"absolute inset-0 transition-opacity duration-700\", active ? \"opacity-100\" : \"opacity-0 pointer-events-none\")}>\n {imageUrl ? (\n <div className=\"absolute inset-0 overflow-hidden\">\n <div className=\"animate-kenburns absolute inset-0\">\n <Image src={imageUrl} alt={name} fill className=\"object-cover\" priority={active} unoptimized />\n </div>\n </div>\n ) : (\n <div className=\"absolute inset-0 bg-gradient-to-br from-brand-purple/40 via-brand-blue/20 to-brand-navy/60\" />\n )}\n <div className=\"absolute bottom-0 left-0 right-0 p-6 sm:p-10 flex flex-col gap-3\">\n <Link href={getHref(collection)} className=\"hover:opacity-90 transition-opacity\">\n <h2 className=\"text-4xl lg:text-5xl font-semibold text-white leading-tight\">{name}</h2>\n </Link>\n <div className=\"flex items-center gap-4 text-sm text-white/70\">\n {supply != null && <span>{supply.toLocaleString()} items</span>}\n {floor && <span className=\"text-white font-semibold\">Floor {formatFloorPrice(floor)}</span>}\n </div>\n </div>\n </div>\n );\n}\n\nexport function HeroSliderSkeleton() {\n return <section className=\"relative w-full h-[78vw] min-h-[420px] max-h-[768px] sm:h-[72vh] sm:max-h-[816px] bg-muted animate-pulse\" />;\n}\n\nexport function HeroSlider({ collections, isLoading, getHref, placeholderHrefs = {} }: HeroSliderProps) {\n const hrefs = { markets: \"/marketplace\", create: \"/create/asset\", ...placeholderHrefs };\n const [current, setCurrent] = useState(0);\n const count = collections.length;\n\n const next = useCallback(() => { if (count > 1) setCurrent((c) => (c + 1) % count); }, [count]);\n const prev = useCallback(() => { if (count > 1) setCurrent((c) => (c - 1 + count) % count); }, [count]);\n\n useEffect(() => {\n if (count <= 1) return;\n const id = setInterval(next, 7000);\n return () => clearInterval(id);\n }, [count, next]);\n\n if (isLoading) return <HeroSliderSkeleton />;\n\n return (\n <section className=\"relative w-full h-[78vw] min-h-[420px] max-h-[768px] sm:h-[72vh] sm:max-h-[816px] overflow-hidden bg-muted\">\n {count === 0 ? (\n <HeroPlaceholder hrefs={hrefs} />\n ) : (\n <>\n {collections.map((col, i) => (\n <HeroSlide key={col.contractAddress} collection={col} active={i === current} getHref={getHref} />\n ))}\n {count > 1 && (\n <>\n <button onClick={prev} aria-label=\"Previous slide\" className=\"absolute left-3 top-1/2 -translate-y-1/2 z-10 min-h-11 min-w-11 rounded-full flex items-center justify-center transition-colors\">\n <span className=\"flex h-9 w-9 items-center justify-center rounded-full bg-white/10 backdrop-blur-md hover:bg-white/20\">\n <ChevronLeft className=\"h-5 w-5 text-white\" />\n </span>\n </button>\n <button onClick={next} aria-label=\"Next slide\" className=\"absolute right-3 top-1/2 -translate-y-1/2 z-10 min-h-11 min-w-11 rounded-full flex items-center justify-center transition-colors\">\n <span className=\"flex h-9 w-9 items-center justify-center rounded-full bg-white/10 backdrop-blur-md hover:bg-white/20\">\n <ChevronRight className=\"h-5 w-5 text-white\" />\n </span>\n </button>\n <div className=\"absolute bottom-4 left-1/2 -translate-x-1/2 flex gap-1.5 z-10\">\n {collections.map((_, i) => (\n <button key={i} onClick={() => setCurrent(i)} aria-label={`Go to slide ${i + 1}`} className={cn(\"h-1.5 rounded-full transition-all\", i === current ? \"w-6 bg-white\" : \"w-1.5 bg-white/40\")} />\n ))}\n </div>\n </>\n )}\n </>\n )}\n </section>\n );\n}\n"],"mappings":"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","names":[]}
|
|
@@ -73,7 +73,7 @@ function LaunchpadFilterBar({
|
|
|
73
73
|
className: "absolute right-2.5 top-1/2 -translate-y-1/2 h-5 w-5 flex items-center justify-center rounded-full text-muted-foreground hover:text-foreground transition-colors",
|
|
74
74
|
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_lucide_react.X, { className: "h-3.5 w-3.5" })
|
|
75
75
|
}
|
|
76
|
-
) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("kbd", { className: "absolute right-3 top-1/2 -translate-y-1/2 hidden sm:flex h-5 w-5 items-center justify-center rounded border border-border bg-muted/40 text-
|
|
76
|
+
) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("kbd", { className: "absolute right-3 top-1/2 -translate-y-1/2 hidden sm:flex h-5 w-5 items-center justify-center rounded border border-border bg-muted/40 text-2xs font-medium text-muted-foreground pointer-events-none", children: "/" })
|
|
77
77
|
] }),
|
|
78
78
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "flex flex-wrap gap-2 flex-1", children: groups.map((group) => {
|
|
79
79
|
const active = activeGroups.has(group.key);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/components/launchpad-filter-bar.tsx"],"sourcesContent":["\"use client\";\n\nimport { useEffect, useRef } from \"react\";\nimport { Search, X } from \"lucide-react\";\nimport { cn } from \"../utils/cn.js\";\nimport { serviceMatchesQuery } from \"./launchpad-services.js\";\nimport {\n LAUNCHPAD_SERVICE_DEFINITIONS,\n type ServiceGroup,\n type ServiceGroupDefinition,\n} from \"../data/launchpad-services.js\";\n\nexport interface LaunchpadFilterBarProps {\n query: string;\n onQueryChange: (value: string) => void;\n groups: ServiceGroupDefinition[];\n activeGroups: Set<ServiceGroup>;\n onToggleGroup: (key: ServiceGroup) => void;\n /** Accepted for API stability; the bar no longer renders a total count. */\n resultCount?: number;\n}\n\n/**\n * Search + group-filter bar above the launchpad grid. Fully controlled — all\n * state (query, active groups) lives in the caller so the grid below reacts\n * to the same state. Each pill shows the live count of services it matches\n * under the current search query. Pressing \"/\" anywhere on the page focuses\n * the search input.\n */\nexport function LaunchpadFilterBar({\n query,\n onQueryChange,\n groups,\n activeGroups,\n onToggleGroup,\n}: LaunchpadFilterBarProps) {\n const inputRef = useRef<HTMLInputElement>(null);\n\n useEffect(() => {\n const onKeyDown = (e: KeyboardEvent) => {\n if (e.key !== \"/\" || e.metaKey || e.ctrlKey || e.altKey) return;\n const target = e.target as HTMLElement | null;\n if (target && (target.tagName === \"INPUT\" || target.tagName === \"TEXTAREA\" || target.isContentEditable)) return;\n e.preventDefault();\n inputRef.current?.focus();\n };\n window.addEventListener(\"keydown\", onKeyDown);\n return () => window.removeEventListener(\"keydown\", onKeyDown);\n }, []);\n\n const countFor = (key: ServiceGroup) =>\n LAUNCHPAD_SERVICE_DEFINITIONS.filter(\n (d) => d.group === key && d.status === \"live\" && serviceMatchesQuery(d, query),\n ).length;\n\n return (\n <div className=\"flex flex-col sm:flex-row gap-3 sm:items-center\">\n <div className=\"relative flex-1 sm:max-w-xs\">\n <Search className=\"absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-muted-foreground pointer-events-none\" />\n <input\n ref={inputRef}\n type=\"text\"\n value={query}\n onChange={(e) => onQueryChange(e.target.value)}\n placeholder=\"Search services\"\n className=\"w-full h-10 rounded-full border border-border bg-card pl-9 pr-9 text-sm placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-brand-purple/40 focus:border-brand-purple/40\"\n />\n {query ? (\n <button\n type=\"button\"\n onClick={() => onQueryChange(\"\")}\n aria-label=\"Clear search\"\n className=\"absolute right-2.5 top-1/2 -translate-y-1/2 h-5 w-5 flex items-center justify-center rounded-full text-muted-foreground hover:text-foreground transition-colors\"\n >\n <X className=\"h-3.5 w-3.5\" />\n </button>\n ) : (\n <kbd className=\"absolute right-3 top-1/2 -translate-y-1/2 hidden sm:flex h-5 w-5 items-center justify-center rounded border border-border bg-muted/40 text-
|
|
1
|
+
{"version":3,"sources":["../../src/components/launchpad-filter-bar.tsx"],"sourcesContent":["\"use client\";\n\nimport { useEffect, useRef } from \"react\";\nimport { Search, X } from \"lucide-react\";\nimport { cn } from \"../utils/cn.js\";\nimport { serviceMatchesQuery } from \"./launchpad-services.js\";\nimport {\n LAUNCHPAD_SERVICE_DEFINITIONS,\n type ServiceGroup,\n type ServiceGroupDefinition,\n} from \"../data/launchpad-services.js\";\n\nexport interface LaunchpadFilterBarProps {\n query: string;\n onQueryChange: (value: string) => void;\n groups: ServiceGroupDefinition[];\n activeGroups: Set<ServiceGroup>;\n onToggleGroup: (key: ServiceGroup) => void;\n /** Accepted for API stability; the bar no longer renders a total count. */\n resultCount?: number;\n}\n\n/**\n * Search + group-filter bar above the launchpad grid. Fully controlled — all\n * state (query, active groups) lives in the caller so the grid below reacts\n * to the same state. Each pill shows the live count of services it matches\n * under the current search query. Pressing \"/\" anywhere on the page focuses\n * the search input.\n */\nexport function LaunchpadFilterBar({\n query,\n onQueryChange,\n groups,\n activeGroups,\n onToggleGroup,\n}: LaunchpadFilterBarProps) {\n const inputRef = useRef<HTMLInputElement>(null);\n\n useEffect(() => {\n const onKeyDown = (e: KeyboardEvent) => {\n if (e.key !== \"/\" || e.metaKey || e.ctrlKey || e.altKey) return;\n const target = e.target as HTMLElement | null;\n if (target && (target.tagName === \"INPUT\" || target.tagName === \"TEXTAREA\" || target.isContentEditable)) return;\n e.preventDefault();\n inputRef.current?.focus();\n };\n window.addEventListener(\"keydown\", onKeyDown);\n return () => window.removeEventListener(\"keydown\", onKeyDown);\n }, []);\n\n const countFor = (key: ServiceGroup) =>\n LAUNCHPAD_SERVICE_DEFINITIONS.filter(\n (d) => d.group === key && d.status === \"live\" && serviceMatchesQuery(d, query),\n ).length;\n\n return (\n <div className=\"flex flex-col sm:flex-row gap-3 sm:items-center\">\n <div className=\"relative flex-1 sm:max-w-xs\">\n <Search className=\"absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-muted-foreground pointer-events-none\" />\n <input\n ref={inputRef}\n type=\"text\"\n value={query}\n onChange={(e) => onQueryChange(e.target.value)}\n placeholder=\"Search services\"\n className=\"w-full h-10 rounded-full border border-border bg-card pl-9 pr-9 text-sm placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-brand-purple/40 focus:border-brand-purple/40\"\n />\n {query ? (\n <button\n type=\"button\"\n onClick={() => onQueryChange(\"\")}\n aria-label=\"Clear search\"\n className=\"absolute right-2.5 top-1/2 -translate-y-1/2 h-5 w-5 flex items-center justify-center rounded-full text-muted-foreground hover:text-foreground transition-colors\"\n >\n <X className=\"h-3.5 w-3.5\" />\n </button>\n ) : (\n <kbd className=\"absolute right-3 top-1/2 -translate-y-1/2 hidden sm:flex h-5 w-5 items-center justify-center rounded border border-border bg-muted/40 text-2xs font-medium text-muted-foreground pointer-events-none\">\n /\n </kbd>\n )}\n </div>\n\n <div className=\"flex flex-wrap gap-2 flex-1\">\n {groups.map((group) => {\n const active = activeGroups.has(group.key);\n const count = countFor(group.key);\n return (\n <button\n key={group.key}\n type=\"button\"\n onClick={() => onToggleGroup(group.key)}\n aria-pressed={active}\n className={cn(\n \"h-8 px-3.5 rounded-full text-xs font-semibold transition-colors border inline-flex items-center gap-1.5\",\n active\n ? \"bg-gradient-to-r from-brand-purple to-brand-blue text-white border-transparent shadow-sm\"\n : \"bg-card text-muted-foreground border-border active:bg-muted/60 sm:hover:bg-muted/40\",\n )}\n >\n {group.title}\n <span className={cn(\"tabular-nums\", active ? \"text-white/70\" : \"text-muted-foreground/60\")}>\n {count}\n </span>\n </button>\n );\n })}\n </div>\n\n </div>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAyDM;AAvDN,mBAAkC;AAClC,0BAA0B;AAC1B,gBAAmB;AACnB,gCAAoC;AACpC,IAAAA,6BAIO;AAmBA,SAAS,mBAAmB;AAAA,EACjC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAA4B;AAC1B,QAAM,eAAW,qBAAyB,IAAI;AAE9C,8BAAU,MAAM;AACd,UAAM,YAAY,CAAC,MAAqB;AACtC,UAAI,EAAE,QAAQ,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,OAAQ;AACzD,YAAM,SAAS,EAAE;AACjB,UAAI,WAAW,OAAO,YAAY,WAAW,OAAO,YAAY,cAAc,OAAO,mBAAoB;AACzG,QAAE,eAAe;AACjB,eAAS,SAAS,MAAM;AAAA,IAC1B;AACA,WAAO,iBAAiB,WAAW,SAAS;AAC5C,WAAO,MAAM,OAAO,oBAAoB,WAAW,SAAS;AAAA,EAC9D,GAAG,CAAC,CAAC;AAEL,QAAM,WAAW,CAAC,QAChB,yDAA8B;AAAA,IAC5B,CAAC,MAAM,EAAE,UAAU,OAAO,EAAE,WAAW,cAAU,+CAAoB,GAAG,KAAK;AAAA,EAC/E,EAAE;AAEJ,SACE,6CAAC,SAAI,WAAU,mDACb;AAAA,iDAAC,SAAI,WAAU,+BACb;AAAA,kDAAC,8BAAO,WAAU,8FAA6F;AAAA,MAC/G;AAAA,QAAC;AAAA;AAAA,UACC,KAAK;AAAA,UACL,MAAK;AAAA,UACL,OAAO;AAAA,UACP,UAAU,CAAC,MAAM,cAAc,EAAE,OAAO,KAAK;AAAA,UAC7C,aAAY;AAAA,UACZ,WAAU;AAAA;AAAA,MACZ;AAAA,MACC,QACC;AAAA,QAAC;AAAA;AAAA,UACC,MAAK;AAAA,UACL,SAAS,MAAM,cAAc,EAAE;AAAA,UAC/B,cAAW;AAAA,UACX,WAAU;AAAA,UAEV,sDAAC,yBAAE,WAAU,eAAc;AAAA;AAAA,MAC7B,IAEA,4CAAC,SAAI,WAAU,wMAAuM,eAEtN;AAAA,OAEJ;AAAA,IAEA,4CAAC,SAAI,WAAU,+BACZ,iBAAO,IAAI,CAAC,UAAU;AACrB,YAAM,SAAS,aAAa,IAAI,MAAM,GAAG;AACzC,YAAM,QAAQ,SAAS,MAAM,GAAG;AAChC,aACE;AAAA,QAAC;AAAA;AAAA,UAEC,MAAK;AAAA,UACL,SAAS,MAAM,cAAc,MAAM,GAAG;AAAA,UACtC,gBAAc;AAAA,UACd,eAAW;AAAA,YACT;AAAA,YACA,SACI,6FACA;AAAA,UACN;AAAA,UAEC;AAAA,kBAAM;AAAA,YACP,4CAAC,UAAK,eAAW,cAAG,gBAAgB,SAAS,kBAAkB,0BAA0B,GACtF,iBACH;AAAA;AAAA;AAAA,QAdK,MAAM;AAAA,MAeb;AAAA,IAEJ,CAAC,GACH;AAAA,KAEF;AAEJ;","names":["import_launchpad_services"]}
|
|
@@ -52,7 +52,7 @@ function LaunchpadFilterBar({
|
|
|
52
52
|
className: "absolute right-2.5 top-1/2 -translate-y-1/2 h-5 w-5 flex items-center justify-center rounded-full text-muted-foreground hover:text-foreground transition-colors",
|
|
53
53
|
children: /* @__PURE__ */ jsx(X, { className: "h-3.5 w-3.5" })
|
|
54
54
|
}
|
|
55
|
-
) : /* @__PURE__ */ jsx("kbd", { className: "absolute right-3 top-1/2 -translate-y-1/2 hidden sm:flex h-5 w-5 items-center justify-center rounded border border-border bg-muted/40 text-
|
|
55
|
+
) : /* @__PURE__ */ jsx("kbd", { className: "absolute right-3 top-1/2 -translate-y-1/2 hidden sm:flex h-5 w-5 items-center justify-center rounded border border-border bg-muted/40 text-2xs font-medium text-muted-foreground pointer-events-none", children: "/" })
|
|
56
56
|
] }),
|
|
57
57
|
/* @__PURE__ */ jsx("div", { className: "flex flex-wrap gap-2 flex-1", children: groups.map((group) => {
|
|
58
58
|
const active = activeGroups.has(group.key);
|