@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
|
@@ -38,7 +38,7 @@ function ClaimRail({ included, steps, trustLead, trust, trustIcon: TrustIcon = i
|
|
|
38
38
|
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("section", { className: "p-5", children: [
|
|
39
39
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "section-label", children: "How it works" }),
|
|
40
40
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("ol", { className: "mt-4 space-y-3", children: steps.map((label, i) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("li", { className: "flex items-center gap-3", children: [
|
|
41
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "h-8 w-8 rounded-lg bg-brand-purple/10 text-brand-purple flex items-center justify-center shrink-0 text-
|
|
41
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "h-8 w-8 rounded-lg bg-brand-purple/10 text-brand-purple flex items-center justify-center shrink-0 text-sm font-bold tabular-nums", children: i + 1 }),
|
|
42
42
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "text-sm text-foreground/90", children: label })
|
|
43
43
|
] }, label)) })
|
|
44
44
|
] }),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/components/claim-rail.tsx"],"sourcesContent":["import { ShieldCheck, type LucideIcon } from \"lucide-react\";\n\nexport interface ClaimRailProps {\n /** \"What's included\" benefits. Omit to hide that panel (e.g. when a live\n * preview is the rail's first panel instead). */\n included?: { icon: LucideIcon; title: string; desc: string }[];\n /** \"How it works\" ordered steps. */\n steps: string[];\n /** Trust/assurance note: bold lead + the rest of the sentence. */\n trustLead: string;\n trust: string;\n /** Trust glyph — defaults to a shield. */\n trustIcon?: LucideIcon;\n}\n\n/** Right-rail for launchpad/claim pages — one quiet borderless sectioned card\n * (What's included · How it works · trust). The brand spectrum appears only\n * as tints: blue benefit chips → purple step numerals → orange trust glyph.\n * Pure presentation; the consuming app passes the per-surface content. */\nexport function ClaimRail({ included, steps, trustLead, trust, trustIcon: TrustIcon = ShieldCheck }: ClaimRailProps) {\n return (\n <div className=\"rounded-2xl bg-muted/50 dark:bg-card divide-y divide-border/60 overflow-hidden\">\n {included && included.length > 0 && (\n <section className=\"p-5\">\n <p className=\"section-label\">What's included</p>\n <ul className=\"mt-4 space-y-4\">\n {included.map(({ icon: Icon, title, desc }) => (\n <li key={title} className=\"flex items-start gap-3\">\n <div className=\"h-8 w-8 rounded-lg bg-brand-blue/10 flex items-center justify-center shrink-0\">\n <Icon className=\"h-4 w-4 text-brand-blue\" />\n </div>\n <div className=\"min-w-0\">\n <p className=\"text-sm font-semibold text-foreground\">{title}</p>\n <p className=\"text-sm text-muted-foreground leading-relaxed mt-0.5\">{desc}</p>\n </div>\n </li>\n ))}\n </ul>\n </section>\n )}\n\n <section className=\"p-5\">\n <p className=\"section-label\">How it works</p>\n <ol className=\"mt-4 space-y-3\">\n {steps.map((label, i) => (\n <li key={label} className=\"flex items-center gap-3\">\n <span className=\"h-8 w-8 rounded-lg bg-brand-purple/10 text-brand-purple flex items-center justify-center shrink-0 text-
|
|
1
|
+
{"version":3,"sources":["../../src/components/claim-rail.tsx"],"sourcesContent":["import { ShieldCheck, type LucideIcon } from \"lucide-react\";\n\nexport interface ClaimRailProps {\n /** \"What's included\" benefits. Omit to hide that panel (e.g. when a live\n * preview is the rail's first panel instead). */\n included?: { icon: LucideIcon; title: string; desc: string }[];\n /** \"How it works\" ordered steps. */\n steps: string[];\n /** Trust/assurance note: bold lead + the rest of the sentence. */\n trustLead: string;\n trust: string;\n /** Trust glyph — defaults to a shield. */\n trustIcon?: LucideIcon;\n}\n\n/** Right-rail for launchpad/claim pages — one quiet borderless sectioned card\n * (What's included · How it works · trust). The brand spectrum appears only\n * as tints: blue benefit chips → purple step numerals → orange trust glyph.\n * Pure presentation; the consuming app passes the per-surface content. */\nexport function ClaimRail({ included, steps, trustLead, trust, trustIcon: TrustIcon = ShieldCheck }: ClaimRailProps) {\n return (\n <div className=\"rounded-2xl bg-muted/50 dark:bg-card divide-y divide-border/60 overflow-hidden\">\n {included && included.length > 0 && (\n <section className=\"p-5\">\n <p className=\"section-label\">What's included</p>\n <ul className=\"mt-4 space-y-4\">\n {included.map(({ icon: Icon, title, desc }) => (\n <li key={title} className=\"flex items-start gap-3\">\n <div className=\"h-8 w-8 rounded-lg bg-brand-blue/10 flex items-center justify-center shrink-0\">\n <Icon className=\"h-4 w-4 text-brand-blue\" />\n </div>\n <div className=\"min-w-0\">\n <p className=\"text-sm font-semibold text-foreground\">{title}</p>\n <p className=\"text-sm text-muted-foreground leading-relaxed mt-0.5\">{desc}</p>\n </div>\n </li>\n ))}\n </ul>\n </section>\n )}\n\n <section className=\"p-5\">\n <p className=\"section-label\">How it works</p>\n <ol className=\"mt-4 space-y-3\">\n {steps.map((label, i) => (\n <li key={label} className=\"flex items-center gap-3\">\n <span className=\"h-8 w-8 rounded-lg bg-brand-purple/10 text-brand-purple flex items-center justify-center shrink-0 text-sm font-bold tabular-nums\">\n {i + 1}\n </span>\n <span className=\"text-sm text-foreground/90\">{label}</span>\n </li>\n ))}\n </ol>\n </section>\n\n <section className=\"p-5 bg-muted/40 flex items-start gap-3\">\n <TrustIcon className=\"h-5 w-5 text-brand-orange shrink-0 mt-0.5\" />\n <p className=\"text-sm text-muted-foreground leading-relaxed\">\n <span className=\"font-semibold text-foreground\">{trustLead}</span> {trust}\n </p>\n </section>\n </div>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAwBU;AAxBV,0BAA6C;AAmBtC,SAAS,UAAU,EAAE,UAAU,OAAO,WAAW,OAAO,WAAW,YAAY,gCAAY,GAAmB;AACnH,SACE,6CAAC,SAAI,WAAU,kFACZ;AAAA,gBAAY,SAAS,SAAS,KAC7B,6CAAC,aAAQ,WAAU,OACjB;AAAA,kDAAC,OAAE,WAAU,iBAAgB,6BAAoB;AAAA,MACjD,4CAAC,QAAG,WAAU,kBACX,mBAAS,IAAI,CAAC,EAAE,MAAM,MAAM,OAAO,KAAK,MACvC,6CAAC,QAAe,WAAU,0BACxB;AAAA,oDAAC,SAAI,WAAU,iFACb,sDAAC,QAAK,WAAU,2BAA0B,GAC5C;AAAA,QACA,6CAAC,SAAI,WAAU,WACb;AAAA,sDAAC,OAAE,WAAU,yCAAyC,iBAAM;AAAA,UAC5D,4CAAC,OAAE,WAAU,wDAAwD,gBAAK;AAAA,WAC5E;AAAA,WAPO,KAQT,CACD,GACH;AAAA,OACF;AAAA,IAGF,6CAAC,aAAQ,WAAU,OACjB;AAAA,kDAAC,OAAE,WAAU,iBAAgB,0BAAY;AAAA,MACzC,4CAAC,QAAG,WAAU,kBACX,gBAAM,IAAI,CAAC,OAAO,MACjB,6CAAC,QAAe,WAAU,2BACxB;AAAA,oDAAC,UAAK,WAAU,oIACb,cAAI,GACP;AAAA,QACA,4CAAC,UAAK,WAAU,8BAA8B,iBAAM;AAAA,WAJ7C,KAKT,CACD,GACH;AAAA,OACF;AAAA,IAEA,6CAAC,aAAQ,WAAU,0CACjB;AAAA,kDAAC,aAAU,WAAU,6CAA4C;AAAA,MACjE,6CAAC,OAAE,WAAU,iDACX;AAAA,oDAAC,UAAK,WAAU,iCAAiC,qBAAU;AAAA,QAAO;AAAA,QAAE;AAAA,SACtE;AAAA,OACF;AAAA,KACF;AAEJ;","names":[]}
|
|
@@ -15,7 +15,7 @@ function ClaimRail({ included, steps, trustLead, trust, trustIcon: TrustIcon = S
|
|
|
15
15
|
/* @__PURE__ */ jsxs("section", { className: "p-5", children: [
|
|
16
16
|
/* @__PURE__ */ jsx("p", { className: "section-label", children: "How it works" }),
|
|
17
17
|
/* @__PURE__ */ jsx("ol", { className: "mt-4 space-y-3", children: steps.map((label, i) => /* @__PURE__ */ jsxs("li", { className: "flex items-center gap-3", children: [
|
|
18
|
-
/* @__PURE__ */ jsx("span", { className: "h-8 w-8 rounded-lg bg-brand-purple/10 text-brand-purple flex items-center justify-center shrink-0 text-
|
|
18
|
+
/* @__PURE__ */ jsx("span", { className: "h-8 w-8 rounded-lg bg-brand-purple/10 text-brand-purple flex items-center justify-center shrink-0 text-sm font-bold tabular-nums", children: i + 1 }),
|
|
19
19
|
/* @__PURE__ */ jsx("span", { className: "text-sm text-foreground/90", children: label })
|
|
20
20
|
] }, label)) })
|
|
21
21
|
] }),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/components/claim-rail.tsx"],"sourcesContent":["import { ShieldCheck, type LucideIcon } from \"lucide-react\";\n\nexport interface ClaimRailProps {\n /** \"What's included\" benefits. Omit to hide that panel (e.g. when a live\n * preview is the rail's first panel instead). */\n included?: { icon: LucideIcon; title: string; desc: string }[];\n /** \"How it works\" ordered steps. */\n steps: string[];\n /** Trust/assurance note: bold lead + the rest of the sentence. */\n trustLead: string;\n trust: string;\n /** Trust glyph — defaults to a shield. */\n trustIcon?: LucideIcon;\n}\n\n/** Right-rail for launchpad/claim pages — one quiet borderless sectioned card\n * (What's included · How it works · trust). The brand spectrum appears only\n * as tints: blue benefit chips → purple step numerals → orange trust glyph.\n * Pure presentation; the consuming app passes the per-surface content. */\nexport function ClaimRail({ included, steps, trustLead, trust, trustIcon: TrustIcon = ShieldCheck }: ClaimRailProps) {\n return (\n <div className=\"rounded-2xl bg-muted/50 dark:bg-card divide-y divide-border/60 overflow-hidden\">\n {included && included.length > 0 && (\n <section className=\"p-5\">\n <p className=\"section-label\">What's included</p>\n <ul className=\"mt-4 space-y-4\">\n {included.map(({ icon: Icon, title, desc }) => (\n <li key={title} className=\"flex items-start gap-3\">\n <div className=\"h-8 w-8 rounded-lg bg-brand-blue/10 flex items-center justify-center shrink-0\">\n <Icon className=\"h-4 w-4 text-brand-blue\" />\n </div>\n <div className=\"min-w-0\">\n <p className=\"text-sm font-semibold text-foreground\">{title}</p>\n <p className=\"text-sm text-muted-foreground leading-relaxed mt-0.5\">{desc}</p>\n </div>\n </li>\n ))}\n </ul>\n </section>\n )}\n\n <section className=\"p-5\">\n <p className=\"section-label\">How it works</p>\n <ol className=\"mt-4 space-y-3\">\n {steps.map((label, i) => (\n <li key={label} className=\"flex items-center gap-3\">\n <span className=\"h-8 w-8 rounded-lg bg-brand-purple/10 text-brand-purple flex items-center justify-center shrink-0 text-
|
|
1
|
+
{"version":3,"sources":["../../src/components/claim-rail.tsx"],"sourcesContent":["import { ShieldCheck, type LucideIcon } from \"lucide-react\";\n\nexport interface ClaimRailProps {\n /** \"What's included\" benefits. Omit to hide that panel (e.g. when a live\n * preview is the rail's first panel instead). */\n included?: { icon: LucideIcon; title: string; desc: string }[];\n /** \"How it works\" ordered steps. */\n steps: string[];\n /** Trust/assurance note: bold lead + the rest of the sentence. */\n trustLead: string;\n trust: string;\n /** Trust glyph — defaults to a shield. */\n trustIcon?: LucideIcon;\n}\n\n/** Right-rail for launchpad/claim pages — one quiet borderless sectioned card\n * (What's included · How it works · trust). The brand spectrum appears only\n * as tints: blue benefit chips → purple step numerals → orange trust glyph.\n * Pure presentation; the consuming app passes the per-surface content. */\nexport function ClaimRail({ included, steps, trustLead, trust, trustIcon: TrustIcon = ShieldCheck }: ClaimRailProps) {\n return (\n <div className=\"rounded-2xl bg-muted/50 dark:bg-card divide-y divide-border/60 overflow-hidden\">\n {included && included.length > 0 && (\n <section className=\"p-5\">\n <p className=\"section-label\">What's included</p>\n <ul className=\"mt-4 space-y-4\">\n {included.map(({ icon: Icon, title, desc }) => (\n <li key={title} className=\"flex items-start gap-3\">\n <div className=\"h-8 w-8 rounded-lg bg-brand-blue/10 flex items-center justify-center shrink-0\">\n <Icon className=\"h-4 w-4 text-brand-blue\" />\n </div>\n <div className=\"min-w-0\">\n <p className=\"text-sm font-semibold text-foreground\">{title}</p>\n <p className=\"text-sm text-muted-foreground leading-relaxed mt-0.5\">{desc}</p>\n </div>\n </li>\n ))}\n </ul>\n </section>\n )}\n\n <section className=\"p-5\">\n <p className=\"section-label\">How it works</p>\n <ol className=\"mt-4 space-y-3\">\n {steps.map((label, i) => (\n <li key={label} className=\"flex items-center gap-3\">\n <span className=\"h-8 w-8 rounded-lg bg-brand-purple/10 text-brand-purple flex items-center justify-center shrink-0 text-sm font-bold tabular-nums\">\n {i + 1}\n </span>\n <span className=\"text-sm text-foreground/90\">{label}</span>\n </li>\n ))}\n </ol>\n </section>\n\n <section className=\"p-5 bg-muted/40 flex items-start gap-3\">\n <TrustIcon className=\"h-5 w-5 text-brand-orange shrink-0 mt-0.5\" />\n <p className=\"text-sm text-muted-foreground leading-relaxed\">\n <span className=\"font-semibold text-foreground\">{trustLead}</span> {trust}\n </p>\n </section>\n </div>\n );\n}\n"],"mappings":"AAwBU,cAOM,YAPN;AAxBV,SAAS,mBAAoC;AAmBtC,SAAS,UAAU,EAAE,UAAU,OAAO,WAAW,OAAO,WAAW,YAAY,YAAY,GAAmB;AACnH,SACE,qBAAC,SAAI,WAAU,kFACZ;AAAA,gBAAY,SAAS,SAAS,KAC7B,qBAAC,aAAQ,WAAU,OACjB;AAAA,0BAAC,OAAE,WAAU,iBAAgB,6BAAoB;AAAA,MACjD,oBAAC,QAAG,WAAU,kBACX,mBAAS,IAAI,CAAC,EAAE,MAAM,MAAM,OAAO,KAAK,MACvC,qBAAC,QAAe,WAAU,0BACxB;AAAA,4BAAC,SAAI,WAAU,iFACb,8BAAC,QAAK,WAAU,2BAA0B,GAC5C;AAAA,QACA,qBAAC,SAAI,WAAU,WACb;AAAA,8BAAC,OAAE,WAAU,yCAAyC,iBAAM;AAAA,UAC5D,oBAAC,OAAE,WAAU,wDAAwD,gBAAK;AAAA,WAC5E;AAAA,WAPO,KAQT,CACD,GACH;AAAA,OACF;AAAA,IAGF,qBAAC,aAAQ,WAAU,OACjB;AAAA,0BAAC,OAAE,WAAU,iBAAgB,0BAAY;AAAA,MACzC,oBAAC,QAAG,WAAU,kBACX,gBAAM,IAAI,CAAC,OAAO,MACjB,qBAAC,QAAe,WAAU,2BACxB;AAAA,4BAAC,UAAK,WAAU,oIACb,cAAI,GACP;AAAA,QACA,oBAAC,UAAK,WAAU,8BAA8B,iBAAM;AAAA,WAJ7C,KAKT,CACD,GACH;AAAA,OACF;AAAA,IAEA,qBAAC,aAAQ,WAAU,0CACjB;AAAA,0BAAC,aAAU,WAAU,6CAA4C;AAAA,MACjE,qBAAC,OAAE,WAAU,iDACX;AAAA,4BAAC,UAAK,WAAU,iCAAiC,qBAAU;AAAA,QAAO;AAAA,QAAE;AAAA,SACtE;AAAA,OACF;AAAA,KACF;AAEJ;","names":[]}
|
|
@@ -86,7 +86,7 @@ function CoinCard({ collection, usePrice, href }) {
|
|
|
86
86
|
unoptimized: true
|
|
87
87
|
}
|
|
88
88
|
) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: (0, import_cn.cn)("absolute inset-0 flex items-center justify-center bg-gradient-to-br", KIND_FALLBACK[m.kind]), children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "select-none text-5xl font-black tracking-tighter text-white/90", children: m.initials }) }),
|
|
89
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: (0, import_cn.cn)("absolute left-2 top-2 rounded-full px-2 py-0.5 text-
|
|
89
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: (0, import_cn.cn)("absolute left-2 top-2 rounded-full px-2 py-0.5 text-2xs font-semibold backdrop-blur-md", KIND_OVER_IMAGE[m.kind]), children: kindLabel(m.kind) })
|
|
90
90
|
] }),
|
|
91
91
|
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex flex-col gap-2 p-3", children: [
|
|
92
92
|
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "min-w-0", children: [
|
|
@@ -94,7 +94,7 @@ function CoinCard({ collection, usePrice, href }) {
|
|
|
94
94
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "truncate text-sm text-muted-foreground", children: collection.symbol ?? "\u2014" })
|
|
95
95
|
] }),
|
|
96
96
|
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex items-baseline justify-between gap-2", children: [
|
|
97
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "text-
|
|
97
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "text-2xs uppercase tracking-wide text-muted-foreground", children: "Price" }),
|
|
98
98
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(PriceValue, { price: m.price, isLoading: m.isLoading })
|
|
99
99
|
] })
|
|
100
100
|
] })
|
|
@@ -103,12 +103,12 @@ function CoinCard({ collection, usePrice, href }) {
|
|
|
103
103
|
function CoinRow({ collection, usePrice, href }) {
|
|
104
104
|
const m = useTileModel({ collection, usePrice });
|
|
105
105
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_link.default, { href, className: "flex items-center gap-3 rounded-lg border border-border/60 bg-card px-3 py-2.5 transition-colors hover:border-primary/40 active:scale-[0.99]", children: [
|
|
106
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "relative h-9 w-9 shrink-0 overflow-hidden rounded-full bg-muted", children: m.logo ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_image.default, { src: m.logo, alt: "", fill: true, sizes: "36px", className: "object-cover", unoptimized: true }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: (0, import_cn.cn)("flex h-full w-full items-center justify-center bg-gradient-to-br text-
|
|
106
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "relative h-9 w-9 shrink-0 overflow-hidden rounded-full bg-muted", children: m.logo ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_image.default, { src: m.logo, alt: "", fill: true, sizes: "36px", className: "object-cover", unoptimized: true }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: (0, import_cn.cn)("flex h-full w-full items-center justify-center bg-gradient-to-br text-2xs font-bold text-white", KIND_FALLBACK[m.kind]), children: m.initials }) }),
|
|
107
107
|
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "min-w-0 flex-1", children: [
|
|
108
108
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "truncate text-sm font-semibold", children: collection.name ?? "Untitled coin" }),
|
|
109
109
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "block truncate text-xs text-muted-foreground", children: collection.symbol ?? "\u2014" })
|
|
110
110
|
] }),
|
|
111
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: (0, import_cn.cn)("hidden shrink-0 rounded-full border px-2 py-0.5 text-
|
|
111
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: (0, import_cn.cn)("hidden shrink-0 rounded-full border px-2 py-0.5 text-2xs font-medium sm:inline-block", KIND_TAG[m.kind]), children: kindLabel(m.kind) }),
|
|
112
112
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "w-28 shrink-0 text-right text-sm", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(PriceValue, { price: m.price, isLoading: m.isLoading }) })
|
|
113
113
|
] });
|
|
114
114
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/components/coin-card.tsx"],"sourcesContent":["\"use client\";\n\n/**\n * CoinCard / CoinRow — chain-agnostic, art-forward coin discovery tiles.\n *\n * Pure presentation: the price read (`usePrice`) and the link target (`href`)\n * are injected by the consuming app, and the collection is typed structurally\n * (CoinCollectionLike) — so a coin on Starknet, Ethereum, or Solana renders the\n * same with zero changes here. The coin's artwork is the hero; the whole tile\n * links to the coin page (where trading happens), and the only stat is the live\n * spot price with its quote-currency icon. FDV / holders / a \"verified\" mark are\n * intentionally not shown — Medialane indexes none of them.\n */\n\nimport Link from \"next/link\";\nimport Image from \"next/image\";\nimport { cn } from \"../utils/cn.js\";\nimport { ipfsToHttp } from \"../utils/ipfs.js\";\nimport { MotionCard } from \"./motion-primitives.js\";\nimport { CurrencyIcon } from \"./currency-icon.js\";\nimport {\n coinKind, formatCoinPrice,\n type CoinCollectionLike, type CoinPriceLike,\n} from \"../data/coins.js\";\n\n/** Injected per-chain spot-price read. `price` is null while loading/unknown. */\nexport type UseCoinPrice = (collection: CoinCollectionLike) => {\n price: CoinPriceLike | null;\n isLoading: boolean;\n};\n\nexport interface CoinTileProps {\n collection: CoinCollectionLike;\n usePrice: UseCoinPrice;\n /** Link target — internal coin page or the per-chain trading app. */\n href: string;\n}\n\n// Kind tag on a card surface (table row) — brand hues, never red.\nconst KIND_TAG: Record<string, string> = {\n creator: \"border-brand-blue/30 bg-brand-blue/10 text-brand-blue\",\n memecoin: \"border-brand-purple/30 bg-brand-purple/10 text-brand-purple\",\n};\n// Kind tag layered over artwork — solid brand pill, white text for legibility.\nconst KIND_OVER_IMAGE: Record<string, string> = {\n creator: \"bg-brand-blue/85 text-white\",\n memecoin: \"bg-brand-purple/85 text-white\",\n};\n// Art-fallback gradient (no logo) — vivid brand wash.\nconst KIND_FALLBACK: Record<string, string> = {\n creator: \"from-brand-blue to-brand-purple\",\n memecoin: \"from-brand-purple to-brand-rose\",\n};\n\nconst kindLabel = (kind: string) => (kind === \"creator\" ? \"Creator Coin\" : \"Memecoin\");\n\nfunction useTileModel({ collection, usePrice }: Omit<CoinTileProps, \"href\">) {\n const { price, isLoading } = usePrice(collection);\n const kind = coinKind(collection.service);\n const logoUri = collection.profile?.image ?? collection.image;\n const logo = logoUri ? ipfsToHttp(logoUri) : null;\n const initials = (collection.symbol ?? collection.name ?? \"?\").trim().slice(0, 2).toUpperCase();\n return { price, isLoading, kind, logo, initials };\n}\n\nfunction PriceValue({ price, isLoading }: { price: CoinPriceLike | null; isLoading: boolean }) {\n if (isLoading) return <span className=\"inline-block h-4 w-14 rounded bg-muted-foreground/20 animate-pulse\" />;\n if (!price) return <span className=\"text-muted-foreground\">—</span>;\n return (\n <span className=\"inline-flex items-center gap-1 font-semibold tabular-nums\">\n <CurrencyIcon symbol={price.quoteSymbol} size={14} />\n {formatCoinPrice(price.quotePerCoin)}\n </span>\n );\n}\n\nexport function CoinCard({ collection, usePrice, href }: CoinTileProps) {\n const m = useTileModel({ collection, usePrice });\n return (\n <MotionCard className=\"card-base group relative flex h-full flex-col\">\n <Link href={href} className=\"flex h-full flex-col\">\n {/* Hero artwork */}\n <div className=\"relative aspect-square w-full overflow-hidden bg-muted\">\n {m.logo ? (\n <Image\n src={m.logo}\n alt={collection.name ?? \"Coin\"}\n fill\n sizes=\"(min-width:1280px) 25vw, (min-width:1024px) 33vw, (min-width:640px) 50vw, 100vw\"\n className=\"object-cover transition-transform duration-500 group-hover:scale-105\"\n unoptimized\n />\n ) : (\n <div className={cn(\"absolute inset-0 flex items-center justify-center bg-gradient-to-br\", KIND_FALLBACK[m.kind])}>\n <span className=\"select-none text-5xl font-black tracking-tighter text-white/90\">{m.initials}</span>\n </div>\n )}\n <span className={cn(\"absolute left-2 top-2 rounded-full px-2 py-0.5 text-
|
|
1
|
+
{"version":3,"sources":["../../src/components/coin-card.tsx"],"sourcesContent":["\"use client\";\n\n/**\n * CoinCard / CoinRow — chain-agnostic, art-forward coin discovery tiles.\n *\n * Pure presentation: the price read (`usePrice`) and the link target (`href`)\n * are injected by the consuming app, and the collection is typed structurally\n * (CoinCollectionLike) — so a coin on Starknet, Ethereum, or Solana renders the\n * same with zero changes here. The coin's artwork is the hero; the whole tile\n * links to the coin page (where trading happens), and the only stat is the live\n * spot price with its quote-currency icon. FDV / holders / a \"verified\" mark are\n * intentionally not shown — Medialane indexes none of them.\n */\n\nimport Link from \"next/link\";\nimport Image from \"next/image\";\nimport { cn } from \"../utils/cn.js\";\nimport { ipfsToHttp } from \"../utils/ipfs.js\";\nimport { MotionCard } from \"./motion-primitives.js\";\nimport { CurrencyIcon } from \"./currency-icon.js\";\nimport {\n coinKind, formatCoinPrice,\n type CoinCollectionLike, type CoinPriceLike,\n} from \"../data/coins.js\";\n\n/** Injected per-chain spot-price read. `price` is null while loading/unknown. */\nexport type UseCoinPrice = (collection: CoinCollectionLike) => {\n price: CoinPriceLike | null;\n isLoading: boolean;\n};\n\nexport interface CoinTileProps {\n collection: CoinCollectionLike;\n usePrice: UseCoinPrice;\n /** Link target — internal coin page or the per-chain trading app. */\n href: string;\n}\n\n// Kind tag on a card surface (table row) — brand hues, never red.\nconst KIND_TAG: Record<string, string> = {\n creator: \"border-brand-blue/30 bg-brand-blue/10 text-brand-blue\",\n memecoin: \"border-brand-purple/30 bg-brand-purple/10 text-brand-purple\",\n};\n// Kind tag layered over artwork — solid brand pill, white text for legibility.\nconst KIND_OVER_IMAGE: Record<string, string> = {\n creator: \"bg-brand-blue/85 text-white\",\n memecoin: \"bg-brand-purple/85 text-white\",\n};\n// Art-fallback gradient (no logo) — vivid brand wash.\nconst KIND_FALLBACK: Record<string, string> = {\n creator: \"from-brand-blue to-brand-purple\",\n memecoin: \"from-brand-purple to-brand-rose\",\n};\n\nconst kindLabel = (kind: string) => (kind === \"creator\" ? \"Creator Coin\" : \"Memecoin\");\n\nfunction useTileModel({ collection, usePrice }: Omit<CoinTileProps, \"href\">) {\n const { price, isLoading } = usePrice(collection);\n const kind = coinKind(collection.service);\n const logoUri = collection.profile?.image ?? collection.image;\n const logo = logoUri ? ipfsToHttp(logoUri) : null;\n const initials = (collection.symbol ?? collection.name ?? \"?\").trim().slice(0, 2).toUpperCase();\n return { price, isLoading, kind, logo, initials };\n}\n\nfunction PriceValue({ price, isLoading }: { price: CoinPriceLike | null; isLoading: boolean }) {\n if (isLoading) return <span className=\"inline-block h-4 w-14 rounded bg-muted-foreground/20 animate-pulse\" />;\n if (!price) return <span className=\"text-muted-foreground\">—</span>;\n return (\n <span className=\"inline-flex items-center gap-1 font-semibold tabular-nums\">\n <CurrencyIcon symbol={price.quoteSymbol} size={14} />\n {formatCoinPrice(price.quotePerCoin)}\n </span>\n );\n}\n\nexport function CoinCard({ collection, usePrice, href }: CoinTileProps) {\n const m = useTileModel({ collection, usePrice });\n return (\n <MotionCard className=\"card-base group relative flex h-full flex-col\">\n <Link href={href} className=\"flex h-full flex-col\">\n {/* Hero artwork */}\n <div className=\"relative aspect-square w-full overflow-hidden bg-muted\">\n {m.logo ? (\n <Image\n src={m.logo}\n alt={collection.name ?? \"Coin\"}\n fill\n sizes=\"(min-width:1280px) 25vw, (min-width:1024px) 33vw, (min-width:640px) 50vw, 100vw\"\n className=\"object-cover transition-transform duration-500 group-hover:scale-105\"\n unoptimized\n />\n ) : (\n <div className={cn(\"absolute inset-0 flex items-center justify-center bg-gradient-to-br\", KIND_FALLBACK[m.kind])}>\n <span className=\"select-none text-5xl font-black tracking-tighter text-white/90\">{m.initials}</span>\n </div>\n )}\n <span className={cn(\"absolute left-2 top-2 rounded-full px-2 py-0.5 text-2xs font-semibold backdrop-blur-md\", KIND_OVER_IMAGE[m.kind])}>\n {kindLabel(m.kind)}\n </span>\n </div>\n\n {/* Body */}\n <div className=\"flex flex-col gap-2 p-3\">\n <div className=\"min-w-0\">\n <div className=\"truncate font-semibold leading-tight\">{collection.name ?? \"Untitled coin\"}</div>\n <div className=\"truncate text-sm text-muted-foreground\">{collection.symbol ?? \"—\"}</div>\n </div>\n <div className=\"flex items-baseline justify-between gap-2\">\n <span className=\"text-2xs uppercase tracking-wide text-muted-foreground\">Price</span>\n <PriceValue price={m.price} isLoading={m.isLoading} />\n </div>\n </div>\n </Link>\n </MotionCard>\n );\n}\n\nexport function CoinRow({ collection, usePrice, href }: CoinTileProps) {\n const m = useTileModel({ collection, usePrice });\n return (\n <Link href={href} className=\"flex items-center gap-3 rounded-lg border border-border/60 bg-card px-3 py-2.5 transition-colors hover:border-primary/40 active:scale-[0.99]\">\n <div className=\"relative h-9 w-9 shrink-0 overflow-hidden rounded-full bg-muted\">\n {m.logo ? <Image src={m.logo} alt=\"\" fill sizes=\"36px\" className=\"object-cover\" unoptimized /> :\n <div className={cn(\"flex h-full w-full items-center justify-center bg-gradient-to-br text-2xs font-bold text-white\", KIND_FALLBACK[m.kind])}>{m.initials}</div>}\n </div>\n <div className=\"min-w-0 flex-1\">\n <span className=\"truncate text-sm font-semibold\">{collection.name ?? \"Untitled coin\"}</span>\n <span className=\"block truncate text-xs text-muted-foreground\">{collection.symbol ?? \"—\"}</span>\n </div>\n <span className={cn(\"hidden shrink-0 rounded-full border px-2 py-0.5 text-2xs font-medium sm:inline-block\", KIND_TAG[m.kind])}>\n {kindLabel(m.kind)}\n </span>\n <span className=\"w-28 shrink-0 text-right text-sm\">\n <PriceValue price={m.price} isLoading={m.isLoading} />\n </span>\n </Link>\n );\n}\n\nexport function CoinCardSkeleton() {\n return (\n <div className=\"card-base flex flex-col\">\n <div className=\"aspect-square w-full animate-pulse bg-muted\" />\n <div className=\"flex flex-col gap-2 p-3\">\n <div className=\"space-y-1.5\">\n <div className=\"h-4 w-24 rounded bg-muted-foreground/20 animate-pulse\" />\n <div className=\"h-3 w-12 rounded bg-muted-foreground/20 animate-pulse\" />\n </div>\n <div className=\"h-4 w-full rounded bg-muted-foreground/10 animate-pulse\" />\n </div>\n </div>\n );\n}\n"],"mappings":"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","names":["Link","Image"]}
|
|
@@ -54,7 +54,7 @@ function CoinCard({ collection, usePrice, href }) {
|
|
|
54
54
|
unoptimized: true
|
|
55
55
|
}
|
|
56
56
|
) : /* @__PURE__ */ jsx("div", { className: cn("absolute inset-0 flex items-center justify-center bg-gradient-to-br", KIND_FALLBACK[m.kind]), children: /* @__PURE__ */ jsx("span", { className: "select-none text-5xl font-black tracking-tighter text-white/90", children: m.initials }) }),
|
|
57
|
-
/* @__PURE__ */ jsx("span", { className: cn("absolute left-2 top-2 rounded-full px-2 py-0.5 text-
|
|
57
|
+
/* @__PURE__ */ jsx("span", { className: cn("absolute left-2 top-2 rounded-full px-2 py-0.5 text-2xs font-semibold backdrop-blur-md", KIND_OVER_IMAGE[m.kind]), children: kindLabel(m.kind) })
|
|
58
58
|
] }),
|
|
59
59
|
/* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-2 p-3", children: [
|
|
60
60
|
/* @__PURE__ */ jsxs("div", { className: "min-w-0", children: [
|
|
@@ -62,7 +62,7 @@ function CoinCard({ collection, usePrice, href }) {
|
|
|
62
62
|
/* @__PURE__ */ jsx("div", { className: "truncate text-sm text-muted-foreground", children: collection.symbol ?? "\u2014" })
|
|
63
63
|
] }),
|
|
64
64
|
/* @__PURE__ */ jsxs("div", { className: "flex items-baseline justify-between gap-2", children: [
|
|
65
|
-
/* @__PURE__ */ jsx("span", { className: "text-
|
|
65
|
+
/* @__PURE__ */ jsx("span", { className: "text-2xs uppercase tracking-wide text-muted-foreground", children: "Price" }),
|
|
66
66
|
/* @__PURE__ */ jsx(PriceValue, { price: m.price, isLoading: m.isLoading })
|
|
67
67
|
] })
|
|
68
68
|
] })
|
|
@@ -71,12 +71,12 @@ function CoinCard({ collection, usePrice, href }) {
|
|
|
71
71
|
function CoinRow({ collection, usePrice, href }) {
|
|
72
72
|
const m = useTileModel({ collection, usePrice });
|
|
73
73
|
return /* @__PURE__ */ jsxs(Link, { href, className: "flex items-center gap-3 rounded-lg border border-border/60 bg-card px-3 py-2.5 transition-colors hover:border-primary/40 active:scale-[0.99]", children: [
|
|
74
|
-
/* @__PURE__ */ jsx("div", { className: "relative h-9 w-9 shrink-0 overflow-hidden rounded-full bg-muted", children: m.logo ? /* @__PURE__ */ jsx(Image, { src: m.logo, alt: "", fill: true, sizes: "36px", className: "object-cover", unoptimized: true }) : /* @__PURE__ */ jsx("div", { className: cn("flex h-full w-full items-center justify-center bg-gradient-to-br text-
|
|
74
|
+
/* @__PURE__ */ jsx("div", { className: "relative h-9 w-9 shrink-0 overflow-hidden rounded-full bg-muted", children: m.logo ? /* @__PURE__ */ jsx(Image, { src: m.logo, alt: "", fill: true, sizes: "36px", className: "object-cover", unoptimized: true }) : /* @__PURE__ */ jsx("div", { className: cn("flex h-full w-full items-center justify-center bg-gradient-to-br text-2xs font-bold text-white", KIND_FALLBACK[m.kind]), children: m.initials }) }),
|
|
75
75
|
/* @__PURE__ */ jsxs("div", { className: "min-w-0 flex-1", children: [
|
|
76
76
|
/* @__PURE__ */ jsx("span", { className: "truncate text-sm font-semibold", children: collection.name ?? "Untitled coin" }),
|
|
77
77
|
/* @__PURE__ */ jsx("span", { className: "block truncate text-xs text-muted-foreground", children: collection.symbol ?? "\u2014" })
|
|
78
78
|
] }),
|
|
79
|
-
/* @__PURE__ */ jsx("span", { className: cn("hidden shrink-0 rounded-full border px-2 py-0.5 text-
|
|
79
|
+
/* @__PURE__ */ jsx("span", { className: cn("hidden shrink-0 rounded-full border px-2 py-0.5 text-2xs font-medium sm:inline-block", KIND_TAG[m.kind]), children: kindLabel(m.kind) }),
|
|
80
80
|
/* @__PURE__ */ jsx("span", { className: "w-28 shrink-0 text-right text-sm", children: /* @__PURE__ */ jsx(PriceValue, { price: m.price, isLoading: m.isLoading }) })
|
|
81
81
|
] });
|
|
82
82
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/components/coin-card.tsx"],"sourcesContent":["\"use client\";\n\n/**\n * CoinCard / CoinRow — chain-agnostic, art-forward coin discovery tiles.\n *\n * Pure presentation: the price read (`usePrice`) and the link target (`href`)\n * are injected by the consuming app, and the collection is typed structurally\n * (CoinCollectionLike) — so a coin on Starknet, Ethereum, or Solana renders the\n * same with zero changes here. The coin's artwork is the hero; the whole tile\n * links to the coin page (where trading happens), and the only stat is the live\n * spot price with its quote-currency icon. FDV / holders / a \"verified\" mark are\n * intentionally not shown — Medialane indexes none of them.\n */\n\nimport Link from \"next/link\";\nimport Image from \"next/image\";\nimport { cn } from \"../utils/cn.js\";\nimport { ipfsToHttp } from \"../utils/ipfs.js\";\nimport { MotionCard } from \"./motion-primitives.js\";\nimport { CurrencyIcon } from \"./currency-icon.js\";\nimport {\n coinKind, formatCoinPrice,\n type CoinCollectionLike, type CoinPriceLike,\n} from \"../data/coins.js\";\n\n/** Injected per-chain spot-price read. `price` is null while loading/unknown. */\nexport type UseCoinPrice = (collection: CoinCollectionLike) => {\n price: CoinPriceLike | null;\n isLoading: boolean;\n};\n\nexport interface CoinTileProps {\n collection: CoinCollectionLike;\n usePrice: UseCoinPrice;\n /** Link target — internal coin page or the per-chain trading app. */\n href: string;\n}\n\n// Kind tag on a card surface (table row) — brand hues, never red.\nconst KIND_TAG: Record<string, string> = {\n creator: \"border-brand-blue/30 bg-brand-blue/10 text-brand-blue\",\n memecoin: \"border-brand-purple/30 bg-brand-purple/10 text-brand-purple\",\n};\n// Kind tag layered over artwork — solid brand pill, white text for legibility.\nconst KIND_OVER_IMAGE: Record<string, string> = {\n creator: \"bg-brand-blue/85 text-white\",\n memecoin: \"bg-brand-purple/85 text-white\",\n};\n// Art-fallback gradient (no logo) — vivid brand wash.\nconst KIND_FALLBACK: Record<string, string> = {\n creator: \"from-brand-blue to-brand-purple\",\n memecoin: \"from-brand-purple to-brand-rose\",\n};\n\nconst kindLabel = (kind: string) => (kind === \"creator\" ? \"Creator Coin\" : \"Memecoin\");\n\nfunction useTileModel({ collection, usePrice }: Omit<CoinTileProps, \"href\">) {\n const { price, isLoading } = usePrice(collection);\n const kind = coinKind(collection.service);\n const logoUri = collection.profile?.image ?? collection.image;\n const logo = logoUri ? ipfsToHttp(logoUri) : null;\n const initials = (collection.symbol ?? collection.name ?? \"?\").trim().slice(0, 2).toUpperCase();\n return { price, isLoading, kind, logo, initials };\n}\n\nfunction PriceValue({ price, isLoading }: { price: CoinPriceLike | null; isLoading: boolean }) {\n if (isLoading) return <span className=\"inline-block h-4 w-14 rounded bg-muted-foreground/20 animate-pulse\" />;\n if (!price) return <span className=\"text-muted-foreground\">—</span>;\n return (\n <span className=\"inline-flex items-center gap-1 font-semibold tabular-nums\">\n <CurrencyIcon symbol={price.quoteSymbol} size={14} />\n {formatCoinPrice(price.quotePerCoin)}\n </span>\n );\n}\n\nexport function CoinCard({ collection, usePrice, href }: CoinTileProps) {\n const m = useTileModel({ collection, usePrice });\n return (\n <MotionCard className=\"card-base group relative flex h-full flex-col\">\n <Link href={href} className=\"flex h-full flex-col\">\n {/* Hero artwork */}\n <div className=\"relative aspect-square w-full overflow-hidden bg-muted\">\n {m.logo ? (\n <Image\n src={m.logo}\n alt={collection.name ?? \"Coin\"}\n fill\n sizes=\"(min-width:1280px) 25vw, (min-width:1024px) 33vw, (min-width:640px) 50vw, 100vw\"\n className=\"object-cover transition-transform duration-500 group-hover:scale-105\"\n unoptimized\n />\n ) : (\n <div className={cn(\"absolute inset-0 flex items-center justify-center bg-gradient-to-br\", KIND_FALLBACK[m.kind])}>\n <span className=\"select-none text-5xl font-black tracking-tighter text-white/90\">{m.initials}</span>\n </div>\n )}\n <span className={cn(\"absolute left-2 top-2 rounded-full px-2 py-0.5 text-
|
|
1
|
+
{"version":3,"sources":["../../src/components/coin-card.tsx"],"sourcesContent":["\"use client\";\n\n/**\n * CoinCard / CoinRow — chain-agnostic, art-forward coin discovery tiles.\n *\n * Pure presentation: the price read (`usePrice`) and the link target (`href`)\n * are injected by the consuming app, and the collection is typed structurally\n * (CoinCollectionLike) — so a coin on Starknet, Ethereum, or Solana renders the\n * same with zero changes here. The coin's artwork is the hero; the whole tile\n * links to the coin page (where trading happens), and the only stat is the live\n * spot price with its quote-currency icon. FDV / holders / a \"verified\" mark are\n * intentionally not shown — Medialane indexes none of them.\n */\n\nimport Link from \"next/link\";\nimport Image from \"next/image\";\nimport { cn } from \"../utils/cn.js\";\nimport { ipfsToHttp } from \"../utils/ipfs.js\";\nimport { MotionCard } from \"./motion-primitives.js\";\nimport { CurrencyIcon } from \"./currency-icon.js\";\nimport {\n coinKind, formatCoinPrice,\n type CoinCollectionLike, type CoinPriceLike,\n} from \"../data/coins.js\";\n\n/** Injected per-chain spot-price read. `price` is null while loading/unknown. */\nexport type UseCoinPrice = (collection: CoinCollectionLike) => {\n price: CoinPriceLike | null;\n isLoading: boolean;\n};\n\nexport interface CoinTileProps {\n collection: CoinCollectionLike;\n usePrice: UseCoinPrice;\n /** Link target — internal coin page or the per-chain trading app. */\n href: string;\n}\n\n// Kind tag on a card surface (table row) — brand hues, never red.\nconst KIND_TAG: Record<string, string> = {\n creator: \"border-brand-blue/30 bg-brand-blue/10 text-brand-blue\",\n memecoin: \"border-brand-purple/30 bg-brand-purple/10 text-brand-purple\",\n};\n// Kind tag layered over artwork — solid brand pill, white text for legibility.\nconst KIND_OVER_IMAGE: Record<string, string> = {\n creator: \"bg-brand-blue/85 text-white\",\n memecoin: \"bg-brand-purple/85 text-white\",\n};\n// Art-fallback gradient (no logo) — vivid brand wash.\nconst KIND_FALLBACK: Record<string, string> = {\n creator: \"from-brand-blue to-brand-purple\",\n memecoin: \"from-brand-purple to-brand-rose\",\n};\n\nconst kindLabel = (kind: string) => (kind === \"creator\" ? \"Creator Coin\" : \"Memecoin\");\n\nfunction useTileModel({ collection, usePrice }: Omit<CoinTileProps, \"href\">) {\n const { price, isLoading } = usePrice(collection);\n const kind = coinKind(collection.service);\n const logoUri = collection.profile?.image ?? collection.image;\n const logo = logoUri ? ipfsToHttp(logoUri) : null;\n const initials = (collection.symbol ?? collection.name ?? \"?\").trim().slice(0, 2).toUpperCase();\n return { price, isLoading, kind, logo, initials };\n}\n\nfunction PriceValue({ price, isLoading }: { price: CoinPriceLike | null; isLoading: boolean }) {\n if (isLoading) return <span className=\"inline-block h-4 w-14 rounded bg-muted-foreground/20 animate-pulse\" />;\n if (!price) return <span className=\"text-muted-foreground\">—</span>;\n return (\n <span className=\"inline-flex items-center gap-1 font-semibold tabular-nums\">\n <CurrencyIcon symbol={price.quoteSymbol} size={14} />\n {formatCoinPrice(price.quotePerCoin)}\n </span>\n );\n}\n\nexport function CoinCard({ collection, usePrice, href }: CoinTileProps) {\n const m = useTileModel({ collection, usePrice });\n return (\n <MotionCard className=\"card-base group relative flex h-full flex-col\">\n <Link href={href} className=\"flex h-full flex-col\">\n {/* Hero artwork */}\n <div className=\"relative aspect-square w-full overflow-hidden bg-muted\">\n {m.logo ? (\n <Image\n src={m.logo}\n alt={collection.name ?? \"Coin\"}\n fill\n sizes=\"(min-width:1280px) 25vw, (min-width:1024px) 33vw, (min-width:640px) 50vw, 100vw\"\n className=\"object-cover transition-transform duration-500 group-hover:scale-105\"\n unoptimized\n />\n ) : (\n <div className={cn(\"absolute inset-0 flex items-center justify-center bg-gradient-to-br\", KIND_FALLBACK[m.kind])}>\n <span className=\"select-none text-5xl font-black tracking-tighter text-white/90\">{m.initials}</span>\n </div>\n )}\n <span className={cn(\"absolute left-2 top-2 rounded-full px-2 py-0.5 text-2xs font-semibold backdrop-blur-md\", KIND_OVER_IMAGE[m.kind])}>\n {kindLabel(m.kind)}\n </span>\n </div>\n\n {/* Body */}\n <div className=\"flex flex-col gap-2 p-3\">\n <div className=\"min-w-0\">\n <div className=\"truncate font-semibold leading-tight\">{collection.name ?? \"Untitled coin\"}</div>\n <div className=\"truncate text-sm text-muted-foreground\">{collection.symbol ?? \"—\"}</div>\n </div>\n <div className=\"flex items-baseline justify-between gap-2\">\n <span className=\"text-2xs uppercase tracking-wide text-muted-foreground\">Price</span>\n <PriceValue price={m.price} isLoading={m.isLoading} />\n </div>\n </div>\n </Link>\n </MotionCard>\n );\n}\n\nexport function CoinRow({ collection, usePrice, href }: CoinTileProps) {\n const m = useTileModel({ collection, usePrice });\n return (\n <Link href={href} className=\"flex items-center gap-3 rounded-lg border border-border/60 bg-card px-3 py-2.5 transition-colors hover:border-primary/40 active:scale-[0.99]\">\n <div className=\"relative h-9 w-9 shrink-0 overflow-hidden rounded-full bg-muted\">\n {m.logo ? <Image src={m.logo} alt=\"\" fill sizes=\"36px\" className=\"object-cover\" unoptimized /> :\n <div className={cn(\"flex h-full w-full items-center justify-center bg-gradient-to-br text-2xs font-bold text-white\", KIND_FALLBACK[m.kind])}>{m.initials}</div>}\n </div>\n <div className=\"min-w-0 flex-1\">\n <span className=\"truncate text-sm font-semibold\">{collection.name ?? \"Untitled coin\"}</span>\n <span className=\"block truncate text-xs text-muted-foreground\">{collection.symbol ?? \"—\"}</span>\n </div>\n <span className={cn(\"hidden shrink-0 rounded-full border px-2 py-0.5 text-2xs font-medium sm:inline-block\", KIND_TAG[m.kind])}>\n {kindLabel(m.kind)}\n </span>\n <span className=\"w-28 shrink-0 text-right text-sm\">\n <PriceValue price={m.price} isLoading={m.isLoading} />\n </span>\n </Link>\n );\n}\n\nexport function CoinCardSkeleton() {\n return (\n <div className=\"card-base flex flex-col\">\n <div className=\"aspect-square w-full animate-pulse bg-muted\" />\n <div className=\"flex flex-col gap-2 p-3\">\n <div className=\"space-y-1.5\">\n <div className=\"h-4 w-24 rounded bg-muted-foreground/20 animate-pulse\" />\n <div className=\"h-3 w-12 rounded bg-muted-foreground/20 animate-pulse\" />\n </div>\n <div className=\"h-4 w-full rounded bg-muted-foreground/10 animate-pulse\" />\n </div>\n </div>\n );\n}\n"],"mappings":"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","names":[]}
|
|
@@ -55,7 +55,7 @@ function CoinLaunchPreview({ data, className }) {
|
|
|
55
55
|
description ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "text-sm text-muted-foreground leading-relaxed line-clamp-3", children: description }) : null,
|
|
56
56
|
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "grid grid-cols-3 gap-2 rounded-xl bg-muted/50 dark:bg-muted/30 p-3 text-sm", children: [
|
|
57
57
|
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
|
|
58
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "text-
|
|
58
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "text-2xs uppercase tracking-wide text-muted-foreground", children: "Launch price" }),
|
|
59
59
|
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("p", { className: "font-semibold tabular-nums", children: [
|
|
60
60
|
LAUNCH_PRICE,
|
|
61
61
|
" ",
|
|
@@ -63,11 +63,11 @@ function CoinLaunchPreview({ data, className }) {
|
|
|
63
63
|
] })
|
|
64
64
|
] }),
|
|
65
65
|
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
|
|
66
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "text-
|
|
66
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "text-2xs uppercase tracking-wide text-muted-foreground", children: "Market cap" }),
|
|
67
67
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "font-semibold tabular-nums text-brand-price", children: marketCap != null ? `${marketCap.toLocaleString()} ${quoteSymbol}` : "\u2014" })
|
|
68
68
|
] }),
|
|
69
69
|
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
|
|
70
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "text-
|
|
70
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "text-2xs uppercase tracking-wide text-muted-foreground", children: "Your share" }),
|
|
71
71
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "font-semibold tabular-nums", children: yourCoins != null ? `${teamPct}% \xB7 ${yourCoins.toLocaleString()}` : `${teamPct}%` })
|
|
72
72
|
] })
|
|
73
73
|
] }),
|
|
@@ -76,7 +76,7 @@ function CoinLaunchPreview({ data, className }) {
|
|
|
76
76
|
teamPct > 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { width: `${teamPct}%`, background: COIN_GRADIENT } }),
|
|
77
77
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "flex-1" })
|
|
78
78
|
] }),
|
|
79
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex justify-between text-
|
|
79
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex justify-between text-2xs text-muted-foreground", children: [
|
|
80
80
|
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "font-medium text-brand-rose", children: [
|
|
81
81
|
"You ",
|
|
82
82
|
teamPct,
|
|
@@ -89,7 +89,7 @@ function CoinLaunchPreview({ data, className }) {
|
|
|
89
89
|
] })
|
|
90
90
|
] })
|
|
91
91
|
] }),
|
|
92
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "text-
|
|
92
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "text-2xs text-muted-foreground/70", children: "Liquidity is locked forever \u2014 nobody can pull it, including us." })
|
|
93
93
|
] })
|
|
94
94
|
] });
|
|
95
95
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/components/coin-launch-preview.tsx"],"sourcesContent":["\"use client\";\n\nimport Image from \"next/image\";\nimport { TrendingUp } from \"lucide-react\";\nimport { cn } from \"../utils/cn.js\";\n\nexport interface CoinPreviewData {\n name: string;\n symbol: string;\n description: string;\n /** Local object URL or resolved IPFS URL for the feature image */\n imageUrl: string | null;\n /** Human supply (already validated) or null while typing */\n supplyHuman: number | null;\n quoteSymbol: string;\n teamPct: number;\n}\n\nconst LAUNCH_PRICE = 0.01; // fixed, quote per coin\n\n/** Warm coin gradient — rose → orange, the brand's coin palette. */\nconst COIN_GRADIENT = \"linear-gradient(135deg, #f6608f, #fb8b46)\";\n\n/** Live preview for the Creator Coin launch studio — builds the coin in front\n * of the creator as they type. Mirrors the coin's eventual discovery card so\n * what they design is what their fans will see. Quiet borderless panel in the\n * launchpad-rail language; the warm coin gradient appears only as the avatar\n * ring and the allocation bar. */\nexport function CoinLaunchPreview({ data, className }: { data: CoinPreviewData; className?: string }) {\n const { name, symbol, description, imageUrl, supplyHuman, quoteSymbol, teamPct } = data;\n const marketCap = supplyHuman != null ? supplyHuman * LAUNCH_PRICE : null;\n const yourCoins = supplyHuman != null ? supplyHuman * (teamPct / 100) : null;\n\n return (\n <div className={cn(\"rounded-2xl bg-muted/50 dark:bg-card overflow-hidden\", className)}>\n <div className=\"px-5 pt-4\">\n <p className=\"section-label\">Live preview</p>\n </div>\n\n <div className=\"p-5 space-y-4\">\n {/* Identity — avatar in the warm coin ring */}\n <div className=\"flex items-center gap-3\">\n <div className=\"shrink-0 rounded-full p-[2px]\" style={{ background: COIN_GRADIENT }}>\n <div className=\"relative h-14 w-14 rounded-full overflow-hidden bg-card flex items-center justify-center\">\n {imageUrl ? (\n <Image src={imageUrl} alt=\"\" fill sizes=\"56px\" className=\"object-cover\" unoptimized />\n ) : (\n <TrendingUp className=\"h-6 w-6 text-brand-rose/70\" />\n )}\n </div>\n </div>\n <div className=\"min-w-0 flex-1\">\n <p className={cn(\"font-bold text-lg leading-snug truncate\", !name && \"text-muted-foreground/50\")}>\n {name || \"Your coin\"}\n </p>\n <p className=\"text-sm text-muted-foreground tabular-nums\">{symbol ? `$${symbol}` : \"$SYMBOL\"}</p>\n </div>\n </div>\n\n {description ? (\n <p className=\"text-sm text-muted-foreground leading-relaxed line-clamp-3\">{description}</p>\n ) : null}\n\n {/* Economics */}\n <div className=\"grid grid-cols-3 gap-2 rounded-xl bg-muted/50 dark:bg-muted/30 p-3 text-sm\">\n <div>\n <p className=\"text-
|
|
1
|
+
{"version":3,"sources":["../../src/components/coin-launch-preview.tsx"],"sourcesContent":["\"use client\";\n\nimport Image from \"next/image\";\nimport { TrendingUp } from \"lucide-react\";\nimport { cn } from \"../utils/cn.js\";\n\nexport interface CoinPreviewData {\n name: string;\n symbol: string;\n description: string;\n /** Local object URL or resolved IPFS URL for the feature image */\n imageUrl: string | null;\n /** Human supply (already validated) or null while typing */\n supplyHuman: number | null;\n quoteSymbol: string;\n teamPct: number;\n}\n\nconst LAUNCH_PRICE = 0.01; // fixed, quote per coin\n\n/** Warm coin gradient — rose → orange, the brand's coin palette. */\nconst COIN_GRADIENT = \"linear-gradient(135deg, #f6608f, #fb8b46)\";\n\n/** Live preview for the Creator Coin launch studio — builds the coin in front\n * of the creator as they type. Mirrors the coin's eventual discovery card so\n * what they design is what their fans will see. Quiet borderless panel in the\n * launchpad-rail language; the warm coin gradient appears only as the avatar\n * ring and the allocation bar. */\nexport function CoinLaunchPreview({ data, className }: { data: CoinPreviewData; className?: string }) {\n const { name, symbol, description, imageUrl, supplyHuman, quoteSymbol, teamPct } = data;\n const marketCap = supplyHuman != null ? supplyHuman * LAUNCH_PRICE : null;\n const yourCoins = supplyHuman != null ? supplyHuman * (teamPct / 100) : null;\n\n return (\n <div className={cn(\"rounded-2xl bg-muted/50 dark:bg-card overflow-hidden\", className)}>\n <div className=\"px-5 pt-4\">\n <p className=\"section-label\">Live preview</p>\n </div>\n\n <div className=\"p-5 space-y-4\">\n {/* Identity — avatar in the warm coin ring */}\n <div className=\"flex items-center gap-3\">\n <div className=\"shrink-0 rounded-full p-[2px]\" style={{ background: COIN_GRADIENT }}>\n <div className=\"relative h-14 w-14 rounded-full overflow-hidden bg-card flex items-center justify-center\">\n {imageUrl ? (\n <Image src={imageUrl} alt=\"\" fill sizes=\"56px\" className=\"object-cover\" unoptimized />\n ) : (\n <TrendingUp className=\"h-6 w-6 text-brand-rose/70\" />\n )}\n </div>\n </div>\n <div className=\"min-w-0 flex-1\">\n <p className={cn(\"font-bold text-lg leading-snug truncate\", !name && \"text-muted-foreground/50\")}>\n {name || \"Your coin\"}\n </p>\n <p className=\"text-sm text-muted-foreground tabular-nums\">{symbol ? `$${symbol}` : \"$SYMBOL\"}</p>\n </div>\n </div>\n\n {description ? (\n <p className=\"text-sm text-muted-foreground leading-relaxed line-clamp-3\">{description}</p>\n ) : null}\n\n {/* Economics */}\n <div className=\"grid grid-cols-3 gap-2 rounded-xl bg-muted/50 dark:bg-muted/30 p-3 text-sm\">\n <div>\n <p className=\"text-2xs uppercase tracking-wide text-muted-foreground\">Launch price</p>\n <p className=\"font-semibold tabular-nums\">{LAUNCH_PRICE} {quoteSymbol}</p>\n </div>\n <div>\n <p className=\"text-2xs uppercase tracking-wide text-muted-foreground\">Market cap</p>\n <p className=\"font-semibold tabular-nums text-brand-price\">\n {marketCap != null ? `${marketCap.toLocaleString()} ${quoteSymbol}` : \"—\"}\n </p>\n </div>\n <div>\n <p className=\"text-2xs uppercase tracking-wide text-muted-foreground\">Your share</p>\n <p className=\"font-semibold tabular-nums\">\n {yourCoins != null ? `${teamPct}% · ${yourCoins.toLocaleString()}` : `${teamPct}%`}\n </p>\n </div>\n </div>\n\n {/* Allocation split bar */}\n <div className=\"space-y-1.5\">\n <div className=\"flex h-2.5 rounded-full overflow-hidden bg-muted-foreground/15\">\n {teamPct > 0 && <div style={{ width: `${teamPct}%`, background: COIN_GRADIENT }} />}\n <div className=\"flex-1\" />\n </div>\n <div className=\"flex justify-between text-2xs text-muted-foreground\">\n <span className=\"font-medium text-brand-rose\">You {teamPct}%</span>\n <span>Community {100 - teamPct}%</span>\n </div>\n </div>\n\n <p className=\"text-2xs text-muted-foreground/70\">\n Liquidity is locked forever — nobody can pull it, including us.\n </p>\n </div>\n </div>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAoCQ;AAlCR,mBAAkB;AAClB,0BAA2B;AAC3B,gBAAmB;AAcnB,MAAM,eAAe;AAGrB,MAAM,gBAAgB;AAOf,SAAS,kBAAkB,EAAE,MAAM,UAAU,GAAkD;AACpG,QAAM,EAAE,MAAM,QAAQ,aAAa,UAAU,aAAa,aAAa,QAAQ,IAAI;AACnF,QAAM,YAAY,eAAe,OAAO,cAAc,eAAe;AACrE,QAAM,YAAY,eAAe,OAAO,eAAe,UAAU,OAAO;AAExE,SACE,6CAAC,SAAI,eAAW,cAAG,wDAAwD,SAAS,GAClF;AAAA,gDAAC,SAAI,WAAU,aACb,sDAAC,OAAE,WAAU,iBAAgB,0BAAY,GAC3C;AAAA,IAEA,6CAAC,SAAI,WAAU,iBAEb;AAAA,mDAAC,SAAI,WAAU,2BACb;AAAA,oDAAC,SAAI,WAAU,iCAAgC,OAAO,EAAE,YAAY,cAAc,GAChF,sDAAC,SAAI,WAAU,4FACZ,qBACC,4CAAC,aAAAA,SAAA,EAAM,KAAK,UAAU,KAAI,IAAG,MAAI,MAAC,OAAM,QAAO,WAAU,gBAAe,aAAW,MAAC,IAEpF,4CAAC,kCAAW,WAAU,8BAA6B,GAEvD,GACF;AAAA,QACA,6CAAC,SAAI,WAAU,kBACb;AAAA,sDAAC,OAAE,eAAW,cAAG,2CAA2C,CAAC,QAAQ,0BAA0B,GAC5F,kBAAQ,aACX;AAAA,UACA,4CAAC,OAAE,WAAU,8CAA8C,mBAAS,IAAI,MAAM,KAAK,WAAU;AAAA,WAC/F;AAAA,SACF;AAAA,MAEC,cACC,4CAAC,OAAE,WAAU,8DAA8D,uBAAY,IACrF;AAAA,MAGJ,6CAAC,SAAI,WAAU,8EACb;AAAA,qDAAC,SACC;AAAA,sDAAC,OAAE,WAAU,0DAAyD,0BAAY;AAAA,UAClF,6CAAC,OAAE,WAAU,8BAA8B;AAAA;AAAA,YAAa;AAAA,YAAE;AAAA,aAAY;AAAA,WACxE;AAAA,QACA,6CAAC,SACC;AAAA,sDAAC,OAAE,WAAU,0DAAyD,wBAAU;AAAA,UAChF,4CAAC,OAAE,WAAU,+CACV,uBAAa,OAAO,GAAG,UAAU,eAAe,CAAC,IAAI,WAAW,KAAK,UACxE;AAAA,WACF;AAAA,QACA,6CAAC,SACC;AAAA,sDAAC,OAAE,WAAU,0DAAyD,wBAAU;AAAA,UAChF,4CAAC,OAAE,WAAU,8BACV,uBAAa,OAAO,GAAG,OAAO,UAAO,UAAU,eAAe,CAAC,KAAK,GAAG,OAAO,KACjF;AAAA,WACF;AAAA,SACF;AAAA,MAGA,6CAAC,SAAI,WAAU,eACb;AAAA,qDAAC,SAAI,WAAU,kEACZ;AAAA,oBAAU,KAAK,4CAAC,SAAI,OAAO,EAAE,OAAO,GAAG,OAAO,KAAK,YAAY,cAAc,GAAG;AAAA,UACjF,4CAAC,SAAI,WAAU,UAAS;AAAA,WAC1B;AAAA,QACA,6CAAC,SAAI,WAAU,uDACb;AAAA,uDAAC,UAAK,WAAU,+BAA8B;AAAA;AAAA,YAAK;AAAA,YAAQ;AAAA,aAAC;AAAA,UAC5D,6CAAC,UAAK;AAAA;AAAA,YAAW,MAAM;AAAA,YAAQ;AAAA,aAAC;AAAA,WAClC;AAAA,SACF;AAAA,MAEA,4CAAC,OAAE,WAAU,qCAAoC,kFAEjD;AAAA,OACF;AAAA,KACF;AAEJ;","names":["Image"]}
|
|
@@ -22,7 +22,7 @@ function CoinLaunchPreview({ data, className }) {
|
|
|
22
22
|
description ? /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground leading-relaxed line-clamp-3", children: description }) : null,
|
|
23
23
|
/* @__PURE__ */ jsxs("div", { className: "grid grid-cols-3 gap-2 rounded-xl bg-muted/50 dark:bg-muted/30 p-3 text-sm", children: [
|
|
24
24
|
/* @__PURE__ */ jsxs("div", { children: [
|
|
25
|
-
/* @__PURE__ */ jsx("p", { className: "text-
|
|
25
|
+
/* @__PURE__ */ jsx("p", { className: "text-2xs uppercase tracking-wide text-muted-foreground", children: "Launch price" }),
|
|
26
26
|
/* @__PURE__ */ jsxs("p", { className: "font-semibold tabular-nums", children: [
|
|
27
27
|
LAUNCH_PRICE,
|
|
28
28
|
" ",
|
|
@@ -30,11 +30,11 @@ function CoinLaunchPreview({ data, className }) {
|
|
|
30
30
|
] })
|
|
31
31
|
] }),
|
|
32
32
|
/* @__PURE__ */ jsxs("div", { children: [
|
|
33
|
-
/* @__PURE__ */ jsx("p", { className: "text-
|
|
33
|
+
/* @__PURE__ */ jsx("p", { className: "text-2xs uppercase tracking-wide text-muted-foreground", children: "Market cap" }),
|
|
34
34
|
/* @__PURE__ */ jsx("p", { className: "font-semibold tabular-nums text-brand-price", children: marketCap != null ? `${marketCap.toLocaleString()} ${quoteSymbol}` : "\u2014" })
|
|
35
35
|
] }),
|
|
36
36
|
/* @__PURE__ */ jsxs("div", { children: [
|
|
37
|
-
/* @__PURE__ */ jsx("p", { className: "text-
|
|
37
|
+
/* @__PURE__ */ jsx("p", { className: "text-2xs uppercase tracking-wide text-muted-foreground", children: "Your share" }),
|
|
38
38
|
/* @__PURE__ */ jsx("p", { className: "font-semibold tabular-nums", children: yourCoins != null ? `${teamPct}% \xB7 ${yourCoins.toLocaleString()}` : `${teamPct}%` })
|
|
39
39
|
] })
|
|
40
40
|
] }),
|
|
@@ -43,7 +43,7 @@ function CoinLaunchPreview({ data, className }) {
|
|
|
43
43
|
teamPct > 0 && /* @__PURE__ */ jsx("div", { style: { width: `${teamPct}%`, background: COIN_GRADIENT } }),
|
|
44
44
|
/* @__PURE__ */ jsx("div", { className: "flex-1" })
|
|
45
45
|
] }),
|
|
46
|
-
/* @__PURE__ */ jsxs("div", { className: "flex justify-between text-
|
|
46
|
+
/* @__PURE__ */ jsxs("div", { className: "flex justify-between text-2xs text-muted-foreground", children: [
|
|
47
47
|
/* @__PURE__ */ jsxs("span", { className: "font-medium text-brand-rose", children: [
|
|
48
48
|
"You ",
|
|
49
49
|
teamPct,
|
|
@@ -56,7 +56,7 @@ function CoinLaunchPreview({ data, className }) {
|
|
|
56
56
|
] })
|
|
57
57
|
] })
|
|
58
58
|
] }),
|
|
59
|
-
/* @__PURE__ */ jsx("p", { className: "text-
|
|
59
|
+
/* @__PURE__ */ jsx("p", { className: "text-2xs text-muted-foreground/70", children: "Liquidity is locked forever \u2014 nobody can pull it, including us." })
|
|
60
60
|
] })
|
|
61
61
|
] });
|
|
62
62
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/components/coin-launch-preview.tsx"],"sourcesContent":["\"use client\";\n\nimport Image from \"next/image\";\nimport { TrendingUp } from \"lucide-react\";\nimport { cn } from \"../utils/cn.js\";\n\nexport interface CoinPreviewData {\n name: string;\n symbol: string;\n description: string;\n /** Local object URL or resolved IPFS URL for the feature image */\n imageUrl: string | null;\n /** Human supply (already validated) or null while typing */\n supplyHuman: number | null;\n quoteSymbol: string;\n teamPct: number;\n}\n\nconst LAUNCH_PRICE = 0.01; // fixed, quote per coin\n\n/** Warm coin gradient — rose → orange, the brand's coin palette. */\nconst COIN_GRADIENT = \"linear-gradient(135deg, #f6608f, #fb8b46)\";\n\n/** Live preview for the Creator Coin launch studio — builds the coin in front\n * of the creator as they type. Mirrors the coin's eventual discovery card so\n * what they design is what their fans will see. Quiet borderless panel in the\n * launchpad-rail language; the warm coin gradient appears only as the avatar\n * ring and the allocation bar. */\nexport function CoinLaunchPreview({ data, className }: { data: CoinPreviewData; className?: string }) {\n const { name, symbol, description, imageUrl, supplyHuman, quoteSymbol, teamPct } = data;\n const marketCap = supplyHuman != null ? supplyHuman * LAUNCH_PRICE : null;\n const yourCoins = supplyHuman != null ? supplyHuman * (teamPct / 100) : null;\n\n return (\n <div className={cn(\"rounded-2xl bg-muted/50 dark:bg-card overflow-hidden\", className)}>\n <div className=\"px-5 pt-4\">\n <p className=\"section-label\">Live preview</p>\n </div>\n\n <div className=\"p-5 space-y-4\">\n {/* Identity — avatar in the warm coin ring */}\n <div className=\"flex items-center gap-3\">\n <div className=\"shrink-0 rounded-full p-[2px]\" style={{ background: COIN_GRADIENT }}>\n <div className=\"relative h-14 w-14 rounded-full overflow-hidden bg-card flex items-center justify-center\">\n {imageUrl ? (\n <Image src={imageUrl} alt=\"\" fill sizes=\"56px\" className=\"object-cover\" unoptimized />\n ) : (\n <TrendingUp className=\"h-6 w-6 text-brand-rose/70\" />\n )}\n </div>\n </div>\n <div className=\"min-w-0 flex-1\">\n <p className={cn(\"font-bold text-lg leading-snug truncate\", !name && \"text-muted-foreground/50\")}>\n {name || \"Your coin\"}\n </p>\n <p className=\"text-sm text-muted-foreground tabular-nums\">{symbol ? `$${symbol}` : \"$SYMBOL\"}</p>\n </div>\n </div>\n\n {description ? (\n <p className=\"text-sm text-muted-foreground leading-relaxed line-clamp-3\">{description}</p>\n ) : null}\n\n {/* Economics */}\n <div className=\"grid grid-cols-3 gap-2 rounded-xl bg-muted/50 dark:bg-muted/30 p-3 text-sm\">\n <div>\n <p className=\"text-
|
|
1
|
+
{"version":3,"sources":["../../src/components/coin-launch-preview.tsx"],"sourcesContent":["\"use client\";\n\nimport Image from \"next/image\";\nimport { TrendingUp } from \"lucide-react\";\nimport { cn } from \"../utils/cn.js\";\n\nexport interface CoinPreviewData {\n name: string;\n symbol: string;\n description: string;\n /** Local object URL or resolved IPFS URL for the feature image */\n imageUrl: string | null;\n /** Human supply (already validated) or null while typing */\n supplyHuman: number | null;\n quoteSymbol: string;\n teamPct: number;\n}\n\nconst LAUNCH_PRICE = 0.01; // fixed, quote per coin\n\n/** Warm coin gradient — rose → orange, the brand's coin palette. */\nconst COIN_GRADIENT = \"linear-gradient(135deg, #f6608f, #fb8b46)\";\n\n/** Live preview for the Creator Coin launch studio — builds the coin in front\n * of the creator as they type. Mirrors the coin's eventual discovery card so\n * what they design is what their fans will see. Quiet borderless panel in the\n * launchpad-rail language; the warm coin gradient appears only as the avatar\n * ring and the allocation bar. */\nexport function CoinLaunchPreview({ data, className }: { data: CoinPreviewData; className?: string }) {\n const { name, symbol, description, imageUrl, supplyHuman, quoteSymbol, teamPct } = data;\n const marketCap = supplyHuman != null ? supplyHuman * LAUNCH_PRICE : null;\n const yourCoins = supplyHuman != null ? supplyHuman * (teamPct / 100) : null;\n\n return (\n <div className={cn(\"rounded-2xl bg-muted/50 dark:bg-card overflow-hidden\", className)}>\n <div className=\"px-5 pt-4\">\n <p className=\"section-label\">Live preview</p>\n </div>\n\n <div className=\"p-5 space-y-4\">\n {/* Identity — avatar in the warm coin ring */}\n <div className=\"flex items-center gap-3\">\n <div className=\"shrink-0 rounded-full p-[2px]\" style={{ background: COIN_GRADIENT }}>\n <div className=\"relative h-14 w-14 rounded-full overflow-hidden bg-card flex items-center justify-center\">\n {imageUrl ? (\n <Image src={imageUrl} alt=\"\" fill sizes=\"56px\" className=\"object-cover\" unoptimized />\n ) : (\n <TrendingUp className=\"h-6 w-6 text-brand-rose/70\" />\n )}\n </div>\n </div>\n <div className=\"min-w-0 flex-1\">\n <p className={cn(\"font-bold text-lg leading-snug truncate\", !name && \"text-muted-foreground/50\")}>\n {name || \"Your coin\"}\n </p>\n <p className=\"text-sm text-muted-foreground tabular-nums\">{symbol ? `$${symbol}` : \"$SYMBOL\"}</p>\n </div>\n </div>\n\n {description ? (\n <p className=\"text-sm text-muted-foreground leading-relaxed line-clamp-3\">{description}</p>\n ) : null}\n\n {/* Economics */}\n <div className=\"grid grid-cols-3 gap-2 rounded-xl bg-muted/50 dark:bg-muted/30 p-3 text-sm\">\n <div>\n <p className=\"text-2xs uppercase tracking-wide text-muted-foreground\">Launch price</p>\n <p className=\"font-semibold tabular-nums\">{LAUNCH_PRICE} {quoteSymbol}</p>\n </div>\n <div>\n <p className=\"text-2xs uppercase tracking-wide text-muted-foreground\">Market cap</p>\n <p className=\"font-semibold tabular-nums text-brand-price\">\n {marketCap != null ? `${marketCap.toLocaleString()} ${quoteSymbol}` : \"—\"}\n </p>\n </div>\n <div>\n <p className=\"text-2xs uppercase tracking-wide text-muted-foreground\">Your share</p>\n <p className=\"font-semibold tabular-nums\">\n {yourCoins != null ? `${teamPct}% · ${yourCoins.toLocaleString()}` : `${teamPct}%`}\n </p>\n </div>\n </div>\n\n {/* Allocation split bar */}\n <div className=\"space-y-1.5\">\n <div className=\"flex h-2.5 rounded-full overflow-hidden bg-muted-foreground/15\">\n {teamPct > 0 && <div style={{ width: `${teamPct}%`, background: COIN_GRADIENT }} />}\n <div className=\"flex-1\" />\n </div>\n <div className=\"flex justify-between text-2xs text-muted-foreground\">\n <span className=\"font-medium text-brand-rose\">You {teamPct}%</span>\n <span>Community {100 - teamPct}%</span>\n </div>\n </div>\n\n <p className=\"text-2xs text-muted-foreground/70\">\n Liquidity is locked forever — nobody can pull it, including us.\n </p>\n </div>\n </div>\n );\n}\n"],"mappings":";AAoCQ,cAeE,YAfF;AAlCR,OAAO,WAAW;AAClB,SAAS,kBAAkB;AAC3B,SAAS,UAAU;AAcnB,MAAM,eAAe;AAGrB,MAAM,gBAAgB;AAOf,SAAS,kBAAkB,EAAE,MAAM,UAAU,GAAkD;AACpG,QAAM,EAAE,MAAM,QAAQ,aAAa,UAAU,aAAa,aAAa,QAAQ,IAAI;AACnF,QAAM,YAAY,eAAe,OAAO,cAAc,eAAe;AACrE,QAAM,YAAY,eAAe,OAAO,eAAe,UAAU,OAAO;AAExE,SACE,qBAAC,SAAI,WAAW,GAAG,wDAAwD,SAAS,GAClF;AAAA,wBAAC,SAAI,WAAU,aACb,8BAAC,OAAE,WAAU,iBAAgB,0BAAY,GAC3C;AAAA,IAEA,qBAAC,SAAI,WAAU,iBAEb;AAAA,2BAAC,SAAI,WAAU,2BACb;AAAA,4BAAC,SAAI,WAAU,iCAAgC,OAAO,EAAE,YAAY,cAAc,GAChF,8BAAC,SAAI,WAAU,4FACZ,qBACC,oBAAC,SAAM,KAAK,UAAU,KAAI,IAAG,MAAI,MAAC,OAAM,QAAO,WAAU,gBAAe,aAAW,MAAC,IAEpF,oBAAC,cAAW,WAAU,8BAA6B,GAEvD,GACF;AAAA,QACA,qBAAC,SAAI,WAAU,kBACb;AAAA,8BAAC,OAAE,WAAW,GAAG,2CAA2C,CAAC,QAAQ,0BAA0B,GAC5F,kBAAQ,aACX;AAAA,UACA,oBAAC,OAAE,WAAU,8CAA8C,mBAAS,IAAI,MAAM,KAAK,WAAU;AAAA,WAC/F;AAAA,SACF;AAAA,MAEC,cACC,oBAAC,OAAE,WAAU,8DAA8D,uBAAY,IACrF;AAAA,MAGJ,qBAAC,SAAI,WAAU,8EACb;AAAA,6BAAC,SACC;AAAA,8BAAC,OAAE,WAAU,0DAAyD,0BAAY;AAAA,UAClF,qBAAC,OAAE,WAAU,8BAA8B;AAAA;AAAA,YAAa;AAAA,YAAE;AAAA,aAAY;AAAA,WACxE;AAAA,QACA,qBAAC,SACC;AAAA,8BAAC,OAAE,WAAU,0DAAyD,wBAAU;AAAA,UAChF,oBAAC,OAAE,WAAU,+CACV,uBAAa,OAAO,GAAG,UAAU,eAAe,CAAC,IAAI,WAAW,KAAK,UACxE;AAAA,WACF;AAAA,QACA,qBAAC,SACC;AAAA,8BAAC,OAAE,WAAU,0DAAyD,wBAAU;AAAA,UAChF,oBAAC,OAAE,WAAU,8BACV,uBAAa,OAAO,GAAG,OAAO,UAAO,UAAU,eAAe,CAAC,KAAK,GAAG,OAAO,KACjF;AAAA,WACF;AAAA,SACF;AAAA,MAGA,qBAAC,SAAI,WAAU,eACb;AAAA,6BAAC,SAAI,WAAU,kEACZ;AAAA,oBAAU,KAAK,oBAAC,SAAI,OAAO,EAAE,OAAO,GAAG,OAAO,KAAK,YAAY,cAAc,GAAG;AAAA,UACjF,oBAAC,SAAI,WAAU,UAAS;AAAA,WAC1B;AAAA,QACA,qBAAC,SAAI,WAAU,uDACb;AAAA,+BAAC,UAAK,WAAU,+BAA8B;AAAA;AAAA,YAAK;AAAA,YAAQ;AAAA,aAAC;AAAA,UAC5D,qBAAC,UAAK;AAAA;AAAA,YAAW,MAAM;AAAA,YAAQ;AAAA,aAAC;AAAA,WAClC;AAAA,SACF;AAAA,MAEA,oBAAC,OAAE,WAAU,qCAAoC,kFAEjD;AAAA,OACF;AAAA,KACF;AAEJ;","names":[]}
|
|
@@ -89,7 +89,7 @@ function CoinsExplorer({ useCoins, usePrice, coinHref, heading = true }) {
|
|
|
89
89
|
children: [
|
|
90
90
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_lucide_react.SlidersHorizontal, { className: "h-3.5 w-3.5" }),
|
|
91
91
|
"Filters",
|
|
92
|
-
filterCount > 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "ml-0.5 inline-flex h-4 min-w-4 items-center justify-center rounded-full bg-primary px-1 text-
|
|
92
|
+
filterCount > 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "ml-0.5 inline-flex h-4 min-w-4 items-center justify-center rounded-full bg-primary px-1 text-2xs font-semibold text-primary-foreground", children: filterCount })
|
|
93
93
|
]
|
|
94
94
|
}
|
|
95
95
|
),
|
|
@@ -109,7 +109,7 @@ function CoinsExplorer({ useCoins, usePrice, coinHref, heading = true }) {
|
|
|
109
109
|
sort !== "recent" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Chip, { onClear: () => setSort("recent"), children: sortLabel(sort) })
|
|
110
110
|
] })
|
|
111
111
|
] }),
|
|
112
|
-
isLoading && items.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "grid grid-cols-1 gap-4 sm:grid-cols-2
|
|
112
|
+
isLoading && items.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "grid grid-cols-1 gap-4 sm:grid-cols-2 md:grid-cols-3 xl:grid-cols-4", children: Array.from({ length: 8 }).map((_, i) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_coin_card.CoinCardSkeleton, {}, i)) }) : items.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rounded-xl border border-border/60 py-16 text-center text-muted-foreground", children: query.trim() ? `No coins match "${query.trim()}".` : "No coins yet." }) : view === "table" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "space-y-2", children: items.map((c) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_coin_card.CoinRow, { collection: c, usePrice, href: coinHref(c) }, `${c.chain}-${c.contractAddress}`)) }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "grid grid-cols-1 gap-4 sm:grid-cols-2 md:grid-cols-3 xl:grid-cols-4", children: items.map((c) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_coin_card.CoinCard, { collection: c, usePrice, href: coinHref(c) }, `${c.chain}-${c.contractAddress}`)) }),
|
|
113
113
|
filtersOpen && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "fixed inset-0 z-50 flex items-center justify-center p-4", role: "dialog", "aria-modal": "true", "aria-label": "Filters", children: [
|
|
114
114
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "absolute inset-0 bg-background/70 backdrop-blur-sm", onClick: () => setFiltersOpen(false) }),
|
|
115
115
|
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "card-base relative z-10 w-full max-w-sm space-y-5 p-5", children: [
|
|
@@ -151,7 +151,7 @@ function CoinsExplorer({ useCoins, usePrice, coinHref, heading = true }) {
|
|
|
151
151
|
}
|
|
152
152
|
function FilterGroup({ label, children }) {
|
|
153
153
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "space-y-2", children: [
|
|
154
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "text-
|
|
154
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "text-2xs font-medium text-muted-foreground", children: label }),
|
|
155
155
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "flex flex-wrap gap-1.5", children })
|
|
156
156
|
] });
|
|
157
157
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/components/coins-explorer.tsx"],"sourcesContent":["\"use client\";\n\n/**\n * CoinsExplorer — the shared coin-discovery surface (chain-agnostic).\n *\n * Owns view/search/filter/sort UI only. The data source (`useCoins`), the price\n * read (`usePrice`), and the link target (`coinHref`) are injected, so each app\n * (and each chain) wires its own without forking this component.\n *\n * Type + sort live in a centered Filters dialog (the \"action focus\" pattern, and\n * matching the marketplace page); the header stays to two lines.\n */\n\nimport { useState, useMemo, useEffect } from \"react\";\nimport { Coins, LayoutGrid, List, Search, SlidersHorizontal, X } from \"lucide-react\";\nimport { cn } from \"../utils/cn.js\";\nimport { CoinCard, CoinRow, CoinCardSkeleton, type UseCoinPrice } from \"./coin-card.js\";\nimport type { CoinCollectionLike } from \"../data/coins.js\";\n\nexport type CoinFilter = \"all\" | \"creator\" | \"memecoin\";\nexport type CoinSort = \"recent\" | \"name\";\nexport type UseCoins = (opts: { filter: CoinFilter; sort: CoinSort }) => {\n collections: CoinCollectionLike[];\n isLoading: boolean;\n};\n\nexport interface CoinsExplorerProps {\n useCoins: UseCoins;\n usePrice: UseCoinPrice;\n /** Build the link target for a coin (internal or per-chain trading app). */\n coinHref: (collection: CoinCollectionLike) => string;\n heading?: boolean;\n}\n\nconst FILTER_TABS: { label: string; value: CoinFilter }[] = [\n { label: \"All\", value: \"all\" },\n { label: \"Creator Coins\", value: \"creator\" },\n { label: \"Memecoins\", value: \"memecoin\" },\n];\n\n// Recency default — never raw swap volume (05 §11 anti-wash hygiene).\nconst SORT_OPTIONS: { label: string; value: CoinSort }[] = [\n { label: \"Recently launched\", value: \"recent\" },\n { label: \"Name\", value: \"name\" },\n];\n\nconst filterLabel = (v: CoinFilter) => FILTER_TABS.find((t) => t.value === v)?.label ?? \"\";\nconst sortLabel = (v: CoinSort) => SORT_OPTIONS.find((o) => o.value === v)?.label ?? \"\";\n\nexport function CoinsExplorer({ useCoins, usePrice, coinHref, heading = true }: CoinsExplorerProps) {\n const [filter, setFilter] = useState<CoinFilter>(\"all\");\n const [sort, setSort] = useState<CoinSort>(\"recent\");\n const [view, setView] = useState<\"grid\" | \"table\">(\"grid\");\n const [query, setQuery] = useState(\"\");\n const [filtersOpen, setFiltersOpen] = useState(false);\n\n const filterCount = (filter !== \"all\" ? 1 : 0) + (sort !== \"recent\" ? 1 : 0);\n\n useEffect(() => {\n if (!filtersOpen) return;\n const onKey = (e: KeyboardEvent) => e.key === \"Escape\" && setFiltersOpen(false);\n window.addEventListener(\"keydown\", onKey);\n return () => window.removeEventListener(\"keydown\", onKey);\n }, [filtersOpen]);\n\n const { collections, isLoading } = useCoins({ filter, sort });\n const items = useMemo(() => {\n const q = query.trim().toLowerCase();\n if (!q) return collections;\n return collections.filter(\n (c) => (c.name ?? \"\").toLowerCase().includes(q) || (c.symbol ?? \"\").toLowerCase().includes(q)\n );\n }, [collections, query]);\n\n return (\n <div className=\"space-y-6\">\n {heading && (\n <div className=\"space-y-2\">\n <div className=\"flex items-center gap-2 text-primary\">\n <Coins className=\"h-5 w-5\" />\n <span className=\"text-sm font-semibold\">Coins</span>\n </div>\n <h1 className=\"text-3xl\">Creator coins & memecoins</h1>\n </div>\n )}\n\n {/* Controls — search + filters + view, kept lean. */}\n <div className=\"space-y-3 border-b border-border/60 pb-3\">\n <div className=\"flex items-center gap-2\">\n <div className=\"relative flex-1\">\n <Search className=\"pointer-events-none absolute left-3 top-1/2 h-3.5 w-3.5 -translate-y-1/2 text-muted-foreground\" />\n <input\n value={query}\n onChange={(e) => setQuery(e.target.value)}\n placeholder=\"Search coins by name or symbol…\"\n className=\"w-full rounded-lg border border-border bg-background py-2 pl-9 pr-3 text-sm outline-none focus:border-primary/50\"\n />\n </div>\n <button\n onClick={() => setFiltersOpen(true)}\n className=\"inline-flex shrink-0 items-center gap-1.5 rounded-lg border border-border px-3 py-2 text-xs font-medium text-foreground hover:border-primary/50\"\n >\n <SlidersHorizontal className=\"h-3.5 w-3.5\" />\n Filters\n {filterCount > 0 && (\n <span className=\"ml-0.5 inline-flex h-4 min-w-4 items-center justify-center rounded-full bg-primary px-1 text-[10px] font-semibold text-primary-foreground\">\n {filterCount}\n </span>\n )}\n </button>\n <div className=\"inline-flex shrink-0 rounded-lg border border-border p-0.5\">\n {([{ v: \"grid\", Icon: LayoutGrid }, { v: \"table\", Icon: List }] as const).map(({ v, Icon }) => (\n <button\n key={v}\n onClick={() => setView(v)}\n aria-label={v === \"grid\" ? \"Grid view\" : \"Table view\"}\n className={cn(\"rounded-md p-1.5 transition-colors\", view === v ? \"bg-primary/10 text-primary\" : \"text-muted-foreground hover:text-foreground\")}\n >\n <Icon className=\"h-4 w-4\" />\n </button>\n ))}\n </div>\n </div>\n\n {/* Active-filter chips */}\n {filterCount > 0 && (\n <div className=\"flex flex-wrap items-center gap-1.5\">\n {filter !== \"all\" && (\n <Chip onClear={() => setFilter(\"all\")}>{filterLabel(filter)}</Chip>\n )}\n {sort !== \"recent\" && (\n <Chip onClear={() => setSort(\"recent\")}>{sortLabel(sort)}</Chip>\n )}\n </div>\n )}\n </div>\n\n {isLoading && items.length === 0 ? (\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4\">\n {Array.from({ length: 8 }).map((_, i) => <CoinCardSkeleton key={i} />)}\n </div>\n ) : items.length === 0 ? (\n <div className=\"rounded-xl border border-border/60 py-16 text-center text-muted-foreground\">\n {query.trim() ? `No coins match \"${query.trim()}\".` : \"No coins yet.\"}\n </div>\n ) : view === \"table\" ? (\n <div className=\"space-y-2\">\n {items.map((c) => <CoinRow key={`${c.chain}-${c.contractAddress}`} collection={c} usePrice={usePrice} href={coinHref(c)} />)}\n </div>\n ) : (\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4\">\n {items.map((c) => <CoinCard key={`${c.chain}-${c.contractAddress}`} collection={c} usePrice={usePrice} href={coinHref(c)} />)}\n </div>\n )}\n\n {filtersOpen && (\n <div className=\"fixed inset-0 z-50 flex items-center justify-center p-4\" role=\"dialog\" aria-modal=\"true\" aria-label=\"Filters\">\n <div className=\"absolute inset-0 bg-background/70 backdrop-blur-sm\" onClick={() => setFiltersOpen(false)} />\n <div className=\"card-base relative z-10 w-full max-w-sm space-y-5 p-5\">\n <div className=\"flex items-center justify-between\">\n <h2 className=\"flex items-center gap-2 text-base font-bold\">\n <SlidersHorizontal className=\"h-4 w-4 text-primary\" />\n Filters\n </h2>\n {filterCount > 0 && (\n <button\n onClick={() => { setFilter(\"all\"); setSort(\"recent\"); }}\n className=\"text-xs font-medium text-muted-foreground hover:text-foreground\"\n >\n Clear all\n </button>\n )}\n </div>\n\n <FilterGroup label=\"Type\">\n {FILTER_TABS.map(({ label, value }) => (\n <PillButton key={value} active={filter === value} onClick={() => setFilter(value)}>{label}</PillButton>\n ))}\n </FilterGroup>\n\n <FilterGroup label=\"Sort\">\n {SORT_OPTIONS.map(({ label, value }) => (\n <PillButton key={value} active={sort === value} onClick={() => setSort(value)}>{label}</PillButton>\n ))}\n </FilterGroup>\n\n <button\n onClick={() => setFiltersOpen(false)}\n className=\"w-full rounded-lg bg-gradient-to-r from-brand-blue to-brand-purple py-2.5 text-sm font-semibold text-white\"\n >\n Show {items.length} {items.length === 1 ? \"coin\" : \"coins\"}\n </button>\n </div>\n </div>\n )}\n </div>\n );\n}\n\nfunction FilterGroup({ label, children }: { label: string; children: React.ReactNode }) {\n return (\n <div className=\"space-y-2\">\n <p className=\"text-[10px] font-medium text-muted-foreground\">{label}</p>\n <div className=\"flex flex-wrap gap-1.5\">{children}</div>\n </div>\n );\n}\n\nfunction PillButton({ active, onClick, children }: { active: boolean; onClick: () => void; children: React.ReactNode }) {\n return (\n <button\n onClick={onClick}\n className={cn(\n \"rounded-lg border px-3 py-1.5 text-xs font-medium transition-colors\",\n active ? \"border-primary bg-primary/10 text-primary\" : \"border-border text-muted-foreground hover:border-primary/50 hover:text-foreground\"\n )}\n >\n {children}\n </button>\n );\n}\n\nfunction Chip({ children, onClear }: { children: React.ReactNode; onClear: () => void }) {\n return (\n <span className=\"inline-flex items-center gap-1 rounded-full border border-primary/30 bg-primary/10 px-2.5 py-1 text-xs font-medium text-primary\">\n {children}\n <button onClick={onClear} aria-label=\"Clear filter\" className=\"hover:text-primary/60\">\n <X className=\"h-3 w-3\" />\n </button>\n </span>\n );\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../src/components/coins-explorer.tsx"],"sourcesContent":["\"use client\";\n\n/**\n * CoinsExplorer — the shared coin-discovery surface (chain-agnostic).\n *\n * Owns view/search/filter/sort UI only. The data source (`useCoins`), the price\n * read (`usePrice`), and the link target (`coinHref`) are injected, so each app\n * (and each chain) wires its own without forking this component.\n *\n * Type + sort live in a centered Filters dialog (the \"action focus\" pattern, and\n * matching the marketplace page); the header stays to two lines.\n */\n\nimport { useState, useMemo, useEffect } from \"react\";\nimport { Coins, LayoutGrid, List, Search, SlidersHorizontal, X } from \"lucide-react\";\nimport { cn } from \"../utils/cn.js\";\nimport { CoinCard, CoinRow, CoinCardSkeleton, type UseCoinPrice } from \"./coin-card.js\";\nimport type { CoinCollectionLike } from \"../data/coins.js\";\n\nexport type CoinFilter = \"all\" | \"creator\" | \"memecoin\";\nexport type CoinSort = \"recent\" | \"name\";\nexport type UseCoins = (opts: { filter: CoinFilter; sort: CoinSort }) => {\n collections: CoinCollectionLike[];\n isLoading: boolean;\n};\n\nexport interface CoinsExplorerProps {\n useCoins: UseCoins;\n usePrice: UseCoinPrice;\n /** Build the link target for a coin (internal or per-chain trading app). */\n coinHref: (collection: CoinCollectionLike) => string;\n heading?: boolean;\n}\n\nconst FILTER_TABS: { label: string; value: CoinFilter }[] = [\n { label: \"All\", value: \"all\" },\n { label: \"Creator Coins\", value: \"creator\" },\n { label: \"Memecoins\", value: \"memecoin\" },\n];\n\n// Recency default — never raw swap volume (05 §11 anti-wash hygiene).\nconst SORT_OPTIONS: { label: string; value: CoinSort }[] = [\n { label: \"Recently launched\", value: \"recent\" },\n { label: \"Name\", value: \"name\" },\n];\n\nconst filterLabel = (v: CoinFilter) => FILTER_TABS.find((t) => t.value === v)?.label ?? \"\";\nconst sortLabel = (v: CoinSort) => SORT_OPTIONS.find((o) => o.value === v)?.label ?? \"\";\n\nexport function CoinsExplorer({ useCoins, usePrice, coinHref, heading = true }: CoinsExplorerProps) {\n const [filter, setFilter] = useState<CoinFilter>(\"all\");\n const [sort, setSort] = useState<CoinSort>(\"recent\");\n const [view, setView] = useState<\"grid\" | \"table\">(\"grid\");\n const [query, setQuery] = useState(\"\");\n const [filtersOpen, setFiltersOpen] = useState(false);\n\n const filterCount = (filter !== \"all\" ? 1 : 0) + (sort !== \"recent\" ? 1 : 0);\n\n useEffect(() => {\n if (!filtersOpen) return;\n const onKey = (e: KeyboardEvent) => e.key === \"Escape\" && setFiltersOpen(false);\n window.addEventListener(\"keydown\", onKey);\n return () => window.removeEventListener(\"keydown\", onKey);\n }, [filtersOpen]);\n\n const { collections, isLoading } = useCoins({ filter, sort });\n const items = useMemo(() => {\n const q = query.trim().toLowerCase();\n if (!q) return collections;\n return collections.filter(\n (c) => (c.name ?? \"\").toLowerCase().includes(q) || (c.symbol ?? \"\").toLowerCase().includes(q)\n );\n }, [collections, query]);\n\n return (\n <div className=\"space-y-6\">\n {heading && (\n <div className=\"space-y-2\">\n <div className=\"flex items-center gap-2 text-primary\">\n <Coins className=\"h-5 w-5\" />\n <span className=\"text-sm font-semibold\">Coins</span>\n </div>\n <h1 className=\"text-3xl\">Creator coins & memecoins</h1>\n </div>\n )}\n\n {/* Controls — search + filters + view, kept lean. */}\n <div className=\"space-y-3 border-b border-border/60 pb-3\">\n <div className=\"flex items-center gap-2\">\n <div className=\"relative flex-1\">\n <Search className=\"pointer-events-none absolute left-3 top-1/2 h-3.5 w-3.5 -translate-y-1/2 text-muted-foreground\" />\n <input\n value={query}\n onChange={(e) => setQuery(e.target.value)}\n placeholder=\"Search coins by name or symbol…\"\n className=\"w-full rounded-lg border border-border bg-background py-2 pl-9 pr-3 text-sm outline-none focus:border-primary/50\"\n />\n </div>\n <button\n onClick={() => setFiltersOpen(true)}\n className=\"inline-flex shrink-0 items-center gap-1.5 rounded-lg border border-border px-3 py-2 text-xs font-medium text-foreground hover:border-primary/50\"\n >\n <SlidersHorizontal className=\"h-3.5 w-3.5\" />\n Filters\n {filterCount > 0 && (\n <span className=\"ml-0.5 inline-flex h-4 min-w-4 items-center justify-center rounded-full bg-primary px-1 text-2xs font-semibold text-primary-foreground\">\n {filterCount}\n </span>\n )}\n </button>\n <div className=\"inline-flex shrink-0 rounded-lg border border-border p-0.5\">\n {([{ v: \"grid\", Icon: LayoutGrid }, { v: \"table\", Icon: List }] as const).map(({ v, Icon }) => (\n <button\n key={v}\n onClick={() => setView(v)}\n aria-label={v === \"grid\" ? \"Grid view\" : \"Table view\"}\n className={cn(\"rounded-md p-1.5 transition-colors\", view === v ? \"bg-primary/10 text-primary\" : \"text-muted-foreground hover:text-foreground\")}\n >\n <Icon className=\"h-4 w-4\" />\n </button>\n ))}\n </div>\n </div>\n\n {/* Active-filter chips */}\n {filterCount > 0 && (\n <div className=\"flex flex-wrap items-center gap-1.5\">\n {filter !== \"all\" && (\n <Chip onClear={() => setFilter(\"all\")}>{filterLabel(filter)}</Chip>\n )}\n {sort !== \"recent\" && (\n <Chip onClear={() => setSort(\"recent\")}>{sortLabel(sort)}</Chip>\n )}\n </div>\n )}\n </div>\n\n {isLoading && items.length === 0 ? (\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2 md:grid-cols-3 xl:grid-cols-4\">\n {Array.from({ length: 8 }).map((_, i) => <CoinCardSkeleton key={i} />)}\n </div>\n ) : items.length === 0 ? (\n <div className=\"rounded-xl border border-border/60 py-16 text-center text-muted-foreground\">\n {query.trim() ? `No coins match \"${query.trim()}\".` : \"No coins yet.\"}\n </div>\n ) : view === \"table\" ? (\n <div className=\"space-y-2\">\n {items.map((c) => <CoinRow key={`${c.chain}-${c.contractAddress}`} collection={c} usePrice={usePrice} href={coinHref(c)} />)}\n </div>\n ) : (\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2 md:grid-cols-3 xl:grid-cols-4\">\n {items.map((c) => <CoinCard key={`${c.chain}-${c.contractAddress}`} collection={c} usePrice={usePrice} href={coinHref(c)} />)}\n </div>\n )}\n\n {filtersOpen && (\n <div className=\"fixed inset-0 z-50 flex items-center justify-center p-4\" role=\"dialog\" aria-modal=\"true\" aria-label=\"Filters\">\n <div className=\"absolute inset-0 bg-background/70 backdrop-blur-sm\" onClick={() => setFiltersOpen(false)} />\n <div className=\"card-base relative z-10 w-full max-w-sm space-y-5 p-5\">\n <div className=\"flex items-center justify-between\">\n <h2 className=\"flex items-center gap-2 text-base font-bold\">\n <SlidersHorizontal className=\"h-4 w-4 text-primary\" />\n Filters\n </h2>\n {filterCount > 0 && (\n <button\n onClick={() => { setFilter(\"all\"); setSort(\"recent\"); }}\n className=\"text-xs font-medium text-muted-foreground hover:text-foreground\"\n >\n Clear all\n </button>\n )}\n </div>\n\n <FilterGroup label=\"Type\">\n {FILTER_TABS.map(({ label, value }) => (\n <PillButton key={value} active={filter === value} onClick={() => setFilter(value)}>{label}</PillButton>\n ))}\n </FilterGroup>\n\n <FilterGroup label=\"Sort\">\n {SORT_OPTIONS.map(({ label, value }) => (\n <PillButton key={value} active={sort === value} onClick={() => setSort(value)}>{label}</PillButton>\n ))}\n </FilterGroup>\n\n <button\n onClick={() => setFiltersOpen(false)}\n className=\"w-full rounded-lg bg-gradient-to-r from-brand-blue to-brand-purple py-2.5 text-sm font-semibold text-white\"\n >\n Show {items.length} {items.length === 1 ? \"coin\" : \"coins\"}\n </button>\n </div>\n </div>\n )}\n </div>\n );\n}\n\nfunction FilterGroup({ label, children }: { label: string; children: React.ReactNode }) {\n return (\n <div className=\"space-y-2\">\n <p className=\"text-2xs font-medium text-muted-foreground\">{label}</p>\n <div className=\"flex flex-wrap gap-1.5\">{children}</div>\n </div>\n );\n}\n\nfunction PillButton({ active, onClick, children }: { active: boolean; onClick: () => void; children: React.ReactNode }) {\n return (\n <button\n onClick={onClick}\n className={cn(\n \"rounded-lg border px-3 py-1.5 text-xs font-medium transition-colors\",\n active ? \"border-primary bg-primary/10 text-primary\" : \"border-border text-muted-foreground hover:border-primary/50 hover:text-foreground\"\n )}\n >\n {children}\n </button>\n );\n}\n\nfunction Chip({ children, onClear }: { children: React.ReactNode; onClear: () => void }) {\n return (\n <span className=\"inline-flex items-center gap-1 rounded-full border border-primary/30 bg-primary/10 px-2.5 py-1 text-xs font-medium text-primary\">\n {children}\n <button onClick={onClear} aria-label=\"Clear filter\" className=\"hover:text-primary/60\">\n <X className=\"h-3 w-3\" />\n </button>\n </span>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AA8EU;AAjEV,mBAA6C;AAC7C,0BAAsE;AACtE,gBAAmB;AACnB,uBAAuE;AAkBvE,MAAM,cAAsD;AAAA,EAC1D,EAAE,OAAO,OAAO,OAAO,MAAM;AAAA,EAC7B,EAAE,OAAO,iBAAiB,OAAO,UAAU;AAAA,EAC3C,EAAE,OAAO,aAAa,OAAO,WAAW;AAC1C;AAGA,MAAM,eAAqD;AAAA,EACzD,EAAE,OAAO,qBAAqB,OAAO,SAAS;AAAA,EAC9C,EAAE,OAAO,QAAQ,OAAO,OAAO;AACjC;AAEA,MAAM,cAAc,CAAC,MAAkB,YAAY,KAAK,CAAC,MAAM,EAAE,UAAU,CAAC,GAAG,SAAS;AACxF,MAAM,YAAY,CAAC,MAAgB,aAAa,KAAK,CAAC,MAAM,EAAE,UAAU,CAAC,GAAG,SAAS;AAE9E,SAAS,cAAc,EAAE,UAAU,UAAU,UAAU,UAAU,KAAK,GAAuB;AAClG,QAAM,CAAC,QAAQ,SAAS,QAAI,uBAAqB,KAAK;AACtD,QAAM,CAAC,MAAM,OAAO,QAAI,uBAAmB,QAAQ;AACnD,QAAM,CAAC,MAAM,OAAO,QAAI,uBAA2B,MAAM;AACzD,QAAM,CAAC,OAAO,QAAQ,QAAI,uBAAS,EAAE;AACrC,QAAM,CAAC,aAAa,cAAc,QAAI,uBAAS,KAAK;AAEpD,QAAM,eAAe,WAAW,QAAQ,IAAI,MAAM,SAAS,WAAW,IAAI;AAE1E,8BAAU,MAAM;AACd,QAAI,CAAC,YAAa;AAClB,UAAM,QAAQ,CAAC,MAAqB,EAAE,QAAQ,YAAY,eAAe,KAAK;AAC9E,WAAO,iBAAiB,WAAW,KAAK;AACxC,WAAO,MAAM,OAAO,oBAAoB,WAAW,KAAK;AAAA,EAC1D,GAAG,CAAC,WAAW,CAAC;AAEhB,QAAM,EAAE,aAAa,UAAU,IAAI,SAAS,EAAE,QAAQ,KAAK,CAAC;AAC5D,QAAM,YAAQ,sBAAQ,MAAM;AAC1B,UAAM,IAAI,MAAM,KAAK,EAAE,YAAY;AACnC,QAAI,CAAC,EAAG,QAAO;AACf,WAAO,YAAY;AAAA,MACjB,CAAC,OAAO,EAAE,QAAQ,IAAI,YAAY,EAAE,SAAS,CAAC,MAAM,EAAE,UAAU,IAAI,YAAY,EAAE,SAAS,CAAC;AAAA,IAC9F;AAAA,EACF,GAAG,CAAC,aAAa,KAAK,CAAC;AAEvB,SACE,6CAAC,SAAI,WAAU,aACZ;AAAA,eACC,6CAAC,SAAI,WAAU,aACb;AAAA,mDAAC,SAAI,WAAU,wCACb;AAAA,oDAAC,6BAAM,WAAU,WAAU;AAAA,QAC3B,4CAAC,UAAK,WAAU,yBAAwB,mBAAK;AAAA,SAC/C;AAAA,MACA,4CAAC,QAAG,WAAU,YAAW,uCAA6B;AAAA,OACxD;AAAA,IAIF,6CAAC,SAAI,WAAU,4CACb;AAAA,mDAAC,SAAI,WAAU,2BACb;AAAA,qDAAC,SAAI,WAAU,mBACb;AAAA,sDAAC,8BAAO,WAAU,kGAAiG;AAAA,UACnH;AAAA,YAAC;AAAA;AAAA,cACC,OAAO;AAAA,cACP,UAAU,CAAC,MAAM,SAAS,EAAE,OAAO,KAAK;AAAA,cACxC,aAAY;AAAA,cACZ,WAAU;AAAA;AAAA,UACZ;AAAA,WACF;AAAA,QACA;AAAA,UAAC;AAAA;AAAA,YACC,SAAS,MAAM,eAAe,IAAI;AAAA,YAClC,WAAU;AAAA,YAEV;AAAA,0DAAC,yCAAkB,WAAU,eAAc;AAAA,cAAE;AAAA,cAE5C,cAAc,KACb,4CAAC,UAAK,WAAU,0IACb,uBACH;AAAA;AAAA;AAAA,QAEJ;AAAA,QACA,4CAAC,SAAI,WAAU,8DACX,WAAC,EAAE,GAAG,QAAQ,MAAM,+BAAW,GAAG,EAAE,GAAG,SAAS,MAAM,yBAAK,CAAC,EAAY,IAAI,CAAC,EAAE,GAAG,KAAK,MACvF;AAAA,UAAC;AAAA;AAAA,YAEC,SAAS,MAAM,QAAQ,CAAC;AAAA,YACxB,cAAY,MAAM,SAAS,cAAc;AAAA,YACzC,eAAW,cAAG,sCAAsC,SAAS,IAAI,+BAA+B,6CAA6C;AAAA,YAE7I,sDAAC,QAAK,WAAU,WAAU;AAAA;AAAA,UALrB;AAAA,QAMP,CACD,GACH;AAAA,SACF;AAAA,MAGC,cAAc,KACb,6CAAC,SAAI,WAAU,uCACZ;AAAA,mBAAW,SACV,4CAAC,QAAK,SAAS,MAAM,UAAU,KAAK,GAAI,sBAAY,MAAM,GAAE;AAAA,QAE7D,SAAS,YACR,4CAAC,QAAK,SAAS,MAAM,QAAQ,QAAQ,GAAI,oBAAU,IAAI,GAAE;AAAA,SAE7D;AAAA,OAEJ;AAAA,IAEC,aAAa,MAAM,WAAW,IAC7B,4CAAC,SAAI,WAAU,uEACZ,gBAAM,KAAK,EAAE,QAAQ,EAAE,CAAC,EAAE,IAAI,CAAC,GAAG,MAAM,4CAAC,uCAAsB,CAAG,CAAE,GACvE,IACE,MAAM,WAAW,IACnB,4CAAC,SAAI,WAAU,8EACZ,gBAAM,KAAK,IAAI,mBAAmB,MAAM,KAAK,CAAC,OAAO,iBACxD,IACE,SAAS,UACX,4CAAC,SAAI,WAAU,aACZ,gBAAM,IAAI,CAAC,MAAM,4CAAC,4BAAgD,YAAY,GAAG,UAAoB,MAAM,SAAS,CAAC,KAAtF,GAAG,EAAE,KAAK,IAAI,EAAE,eAAe,EAA0D,CAAE,GAC7H,IAEA,4CAAC,SAAI,WAAU,uEACZ,gBAAM,IAAI,CAAC,MAAM,4CAAC,6BAAiD,YAAY,GAAG,UAAoB,MAAM,SAAS,CAAC,KAAtF,GAAG,EAAE,KAAK,IAAI,EAAE,eAAe,EAA0D,CAAE,GAC9H;AAAA,IAGD,eACC,6CAAC,SAAI,WAAU,2DAA0D,MAAK,UAAS,cAAW,QAAO,cAAW,WAClH;AAAA,kDAAC,SAAI,WAAU,sDAAqD,SAAS,MAAM,eAAe,KAAK,GAAG;AAAA,MAC1G,6CAAC,SAAI,WAAU,yDACb;AAAA,qDAAC,SAAI,WAAU,qCACb;AAAA,uDAAC,QAAG,WAAU,+CACZ;AAAA,wDAAC,yCAAkB,WAAU,wBAAuB;AAAA,YAAE;AAAA,aAExD;AAAA,UACC,cAAc,KACb;AAAA,YAAC;AAAA;AAAA,cACC,SAAS,MAAM;AAAE,0BAAU,KAAK;AAAG,wBAAQ,QAAQ;AAAA,cAAG;AAAA,cACtD,WAAU;AAAA,cACX;AAAA;AAAA,UAED;AAAA,WAEJ;AAAA,QAEA,4CAAC,eAAY,OAAM,QAChB,sBAAY,IAAI,CAAC,EAAE,OAAO,MAAM,MAC/B,4CAAC,cAAuB,QAAQ,WAAW,OAAO,SAAS,MAAM,UAAU,KAAK,GAAI,mBAAnE,KAAyE,CAC3F,GACH;AAAA,QAEA,4CAAC,eAAY,OAAM,QAChB,uBAAa,IAAI,CAAC,EAAE,OAAO,MAAM,MAChC,4CAAC,cAAuB,QAAQ,SAAS,OAAO,SAAS,MAAM,QAAQ,KAAK,GAAI,mBAA/D,KAAqE,CACvF,GACH;AAAA,QAEA;AAAA,UAAC;AAAA;AAAA,YACC,SAAS,MAAM,eAAe,KAAK;AAAA,YACnC,WAAU;AAAA,YACX;AAAA;AAAA,cACO,MAAM;AAAA,cAAO;AAAA,cAAE,MAAM,WAAW,IAAI,SAAS;AAAA;AAAA;AAAA,QACrD;AAAA,SACF;AAAA,OACF;AAAA,KAEJ;AAEJ;AAEA,SAAS,YAAY,EAAE,OAAO,SAAS,GAAiD;AACtF,SACE,6CAAC,SAAI,WAAU,aACb;AAAA,gDAAC,OAAE,WAAU,8CAA8C,iBAAM;AAAA,IACjE,4CAAC,SAAI,WAAU,0BAA0B,UAAS;AAAA,KACpD;AAEJ;AAEA,SAAS,WAAW,EAAE,QAAQ,SAAS,SAAS,GAAwE;AACtH,SACE;AAAA,IAAC;AAAA;AAAA,MACC;AAAA,MACA,eAAW;AAAA,QACT;AAAA,QACA,SAAS,8CAA8C;AAAA,MACzD;AAAA,MAEC;AAAA;AAAA,EACH;AAEJ;AAEA,SAAS,KAAK,EAAE,UAAU,QAAQ,GAAuD;AACvF,SACE,6CAAC,UAAK,WAAU,mIACb;AAAA;AAAA,IACD,4CAAC,YAAO,SAAS,SAAS,cAAW,gBAAe,WAAU,yBAC5D,sDAAC,yBAAE,WAAU,WAAU,GACzB;AAAA,KACF;AAEJ;","names":[]}
|
|
@@ -66,7 +66,7 @@ function CoinsExplorer({ useCoins, usePrice, coinHref, heading = true }) {
|
|
|
66
66
|
children: [
|
|
67
67
|
/* @__PURE__ */ jsx(SlidersHorizontal, { className: "h-3.5 w-3.5" }),
|
|
68
68
|
"Filters",
|
|
69
|
-
filterCount > 0 && /* @__PURE__ */ jsx("span", { className: "ml-0.5 inline-flex h-4 min-w-4 items-center justify-center rounded-full bg-primary px-1 text-
|
|
69
|
+
filterCount > 0 && /* @__PURE__ */ jsx("span", { className: "ml-0.5 inline-flex h-4 min-w-4 items-center justify-center rounded-full bg-primary px-1 text-2xs font-semibold text-primary-foreground", children: filterCount })
|
|
70
70
|
]
|
|
71
71
|
}
|
|
72
72
|
),
|
|
@@ -86,7 +86,7 @@ function CoinsExplorer({ useCoins, usePrice, coinHref, heading = true }) {
|
|
|
86
86
|
sort !== "recent" && /* @__PURE__ */ jsx(Chip, { onClear: () => setSort("recent"), children: sortLabel(sort) })
|
|
87
87
|
] })
|
|
88
88
|
] }),
|
|
89
|
-
isLoading && items.length === 0 ? /* @__PURE__ */ jsx("div", { className: "grid grid-cols-1 gap-4 sm:grid-cols-2
|
|
89
|
+
isLoading && items.length === 0 ? /* @__PURE__ */ jsx("div", { className: "grid grid-cols-1 gap-4 sm:grid-cols-2 md:grid-cols-3 xl:grid-cols-4", children: Array.from({ length: 8 }).map((_, i) => /* @__PURE__ */ jsx(CoinCardSkeleton, {}, i)) }) : items.length === 0 ? /* @__PURE__ */ jsx("div", { className: "rounded-xl border border-border/60 py-16 text-center text-muted-foreground", children: query.trim() ? `No coins match "${query.trim()}".` : "No coins yet." }) : view === "table" ? /* @__PURE__ */ jsx("div", { className: "space-y-2", children: items.map((c) => /* @__PURE__ */ jsx(CoinRow, { collection: c, usePrice, href: coinHref(c) }, `${c.chain}-${c.contractAddress}`)) }) : /* @__PURE__ */ jsx("div", { className: "grid grid-cols-1 gap-4 sm:grid-cols-2 md:grid-cols-3 xl:grid-cols-4", children: items.map((c) => /* @__PURE__ */ jsx(CoinCard, { collection: c, usePrice, href: coinHref(c) }, `${c.chain}-${c.contractAddress}`)) }),
|
|
90
90
|
filtersOpen && /* @__PURE__ */ jsxs("div", { className: "fixed inset-0 z-50 flex items-center justify-center p-4", role: "dialog", "aria-modal": "true", "aria-label": "Filters", children: [
|
|
91
91
|
/* @__PURE__ */ jsx("div", { className: "absolute inset-0 bg-background/70 backdrop-blur-sm", onClick: () => setFiltersOpen(false) }),
|
|
92
92
|
/* @__PURE__ */ jsxs("div", { className: "card-base relative z-10 w-full max-w-sm space-y-5 p-5", children: [
|
|
@@ -128,7 +128,7 @@ function CoinsExplorer({ useCoins, usePrice, coinHref, heading = true }) {
|
|
|
128
128
|
}
|
|
129
129
|
function FilterGroup({ label, children }) {
|
|
130
130
|
return /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
|
|
131
|
-
/* @__PURE__ */ jsx("p", { className: "text-
|
|
131
|
+
/* @__PURE__ */ jsx("p", { className: "text-2xs font-medium text-muted-foreground", children: label }),
|
|
132
132
|
/* @__PURE__ */ jsx("div", { className: "flex flex-wrap gap-1.5", children })
|
|
133
133
|
] });
|
|
134
134
|
}
|