@medialane/ui 0.83.0 → 0.85.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. package/dist/components/animated-token-media.cjs +100 -0
  2. package/dist/components/animated-token-media.cjs.map +1 -0
  3. package/dist/components/animated-token-media.d.cts +28 -0
  4. package/dist/components/animated-token-media.d.ts +28 -0
  5. package/dist/components/animated-token-media.js +66 -0
  6. package/dist/components/animated-token-media.js.map +1 -0
  7. package/dist/components/asset-card.cjs +14 -13
  8. package/dist/components/asset-card.cjs.map +1 -1
  9. package/dist/components/asset-card.d.cts +5 -1
  10. package/dist/components/asset-card.d.ts +5 -1
  11. package/dist/components/asset-card.js +14 -13
  12. package/dist/components/asset-card.js.map +1 -1
  13. package/dist/components/asset-top-sections.cjs +58 -27
  14. package/dist/components/asset-top-sections.cjs.map +1 -1
  15. package/dist/components/asset-top-sections.d.cts +19 -2
  16. package/dist/components/asset-top-sections.d.ts +19 -2
  17. package/dist/components/asset-top-sections.js +58 -27
  18. package/dist/components/asset-top-sections.js.map +1 -1
  19. package/dist/components/nav-command-menu.cjs +10 -3
  20. package/dist/components/nav-command-menu.cjs.map +1 -1
  21. package/dist/components/nav-command-menu.d.cts +13 -1
  22. package/dist/components/nav-command-menu.d.ts +13 -1
  23. package/dist/components/nav-command-menu.js +10 -3
  24. package/dist/components/nav-command-menu.js.map +1 -1
  25. package/dist/components/token-card.cjs +15 -13
  26. package/dist/components/token-card.cjs.map +1 -1
  27. package/dist/components/token-card.js +15 -13
  28. package/dist/components/token-card.js.map +1 -1
  29. package/dist/data/living-render-collections.cjs +43 -0
  30. package/dist/data/living-render-collections.cjs.map +1 -0
  31. package/dist/data/living-render-collections.d.cts +16 -0
  32. package/dist/data/living-render-collections.d.ts +16 -0
  33. package/dist/data/living-render-collections.js +18 -0
  34. package/dist/data/living-render-collections.js.map +1 -0
  35. package/dist/index.cjs +11 -0
  36. package/dist/index.cjs.map +1 -1
  37. package/dist/index.d.cts +4 -1
  38. package/dist/index.d.ts +4 -1
  39. package/dist/index.js +7 -0
  40. package/dist/index.js.map +1 -1
  41. package/dist/utils/use-intersection-active.cjs +49 -0
  42. package/dist/utils/use-intersection-active.cjs.map +1 -0
  43. package/dist/utils/use-intersection-active.d.cts +12 -0
  44. package/dist/utils/use-intersection-active.d.ts +12 -0
  45. package/dist/utils/use-intersection-active.js +25 -0
  46. package/dist/utils/use-intersection-active.js.map +1 -0
  47. package/package.json +4 -3
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/components/asset-top-sections.tsx"],"sourcesContent":["\"use client\";\n\nimport Image from \"next/image\";\nimport Link from \"next/link\";\nimport { motion } from \"framer-motion\";\nimport { AddressDisplay } from \"./address-display.js\";\nimport { ParentAttributionBanner } from \"./parent-attribution-banner.js\";\nimport { IpTypeBadge } from \"./ip-type-badge.js\";\nimport { Layers, Users } from \"lucide-react\";\n\ninterface AssetMediaColumnProps {\n shouldReduce: boolean;\n image: string;\n imageAlt: string;\n imgError: boolean;\n onImageError: () => void;\n fallback: React.ReactNode;\n stats?: Array<{\n value: string;\n label: string;\n icon: React.ReactNode;\n }>;\n}\n\nexport function AssetMediaColumn({\n shouldReduce,\n image,\n imageAlt,\n imgError,\n onImageError,\n fallback,\n stats,\n}: AssetMediaColumnProps) {\n return (\n <motion.div\n initial={shouldReduce ? false : { scale: 1.0, opacity: 0 }}\n animate={{ scale: 1.02, opacity: 1 }}\n transition={{ duration: 0.6, ease: \"easeOut\" }}\n className=\"overflow-hidden rounded-xl lg:sticky lg:top-16\"\n >\n <div className=\"rounded-2xl overflow-hidden border border-border bg-muted\">\n {image && !imgError ? (\n <Image\n src={image}\n alt={imageAlt}\n width={0}\n height={0}\n sizes=\"(max-width: 1024px) 100vw, 66vw\"\n className=\"w-full h-auto\"\n onError={onImageError}\n crossOrigin=\"anonymous\"\n priority\n />\n ) : (\n fallback\n )}\n </div>\n\n {stats && stats.length > 0 ? (\n <div className={`grid gap-3 mt-4 ${stats.length === 2 ? \"grid-cols-2\" : \"grid-cols-1\"}`}>\n {stats.map((stat) => (\n <div key={stat.label} className=\"rounded-xl border border-border bg-muted/20 p-4 text-center\">\n <p className=\"text-2xl font-black\">{stat.value}</p>\n <div className=\"flex items-center justify-center gap-1 text-xs text-muted-foreground mt-1\">\n {stat.icon}\n {stat.label}\n </div>\n </div>\n ))}\n </div>\n ) : null}\n </motion.div>\n );\n}\n\ninterface AssetHeaderBlockProps {\n name: string;\n description?: string | null;\n ipType?: string | null;\n showMultiEditionBadge?: boolean;\n parentContract?: string | null;\n parentTokenId?: string | null;\n}\n\nexport function AssetHeaderBlock({\n name,\n description,\n ipType,\n showMultiEditionBadge = false,\n parentContract,\n parentTokenId,\n}: AssetHeaderBlockProps) {\n return (\n <div>\n {ipType || showMultiEditionBadge ? (\n <div className=\"flex items-center gap-2 flex-wrap mb-2\">\n {ipType ? <IpTypeBadge ipType={ipType} size=\"md\" /> : null}\n {showMultiEditionBadge ? (\n <span className=\"inline-flex items-center gap-1 text-[11px] font-semibold px-2.5 py-1 rounded-full border border-violet-500/30 bg-violet-500/10 text-violet-500\">\n <Layers className=\"h-3 w-3\" />\n Multi-edition\n </span>\n ) : null}\n </div>\n ) : null}\n {parentContract && parentTokenId ? (\n <div className=\"mb-3\">\n <ParentAttributionBanner\n parentContract={parentContract}\n parentTokenId={parentTokenId}\n parentName={`Token #${parentTokenId}`}\n />\n </div>\n ) : null}\n <h1 className=\"text-3xl lg:text-5xl font-bold\">{name}</h1>\n {description ? (\n <p className=\"text-sm text-muted-foreground leading-relaxed mt-1\">{description}</p>\n ) : null}\n </div>\n );\n}\n\nexport interface AssetOwnerRowProps {\n ownerAddress: string;\n ownerHref: string;\n}\n\n/** Single-owner identity (ERC-721) — its own sibling row, not nested inside\n * the collection bar (that reads as if ownership were a collection\n * attribute, 2026-07-05 feedback). ERC-1155 editions use `AssetOwnersPanel`\n * instead (multiple owners). */\nexport function AssetOwnerRow({ ownerAddress, ownerHref }: AssetOwnerRowProps) {\n return (\n <div className=\"flex items-center gap-1.5 text-xs text-muted-foreground\">\n <span>Owner</span>\n <Link href={ownerHref} className=\"hover:text-primary transition-colors font-medium\">\n <AddressDisplay address={ownerAddress} />\n </Link>\n </div>\n );\n}\n\nexport function buildEditionStats(totalEditions: number, uniqueOwners: number) {\n return [\n {\n value: totalEditions.toLocaleString(),\n label: \"editions minted\",\n icon: <Layers className=\"h-3 w-3\" />,\n },\n {\n value: uniqueOwners.toLocaleString(),\n label: \"unique owners\",\n icon: <Users className=\"h-3 w-3\" />,\n },\n ];\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AA0CU;AAxCV,mBAAkB;AAClB,kBAAiB;AACjB,2BAAuB;AACvB,6BAA+B;AAC/B,uCAAwC;AACxC,2BAA4B;AAC5B,0BAA8B;AAgBvB,SAAS,iBAAiB;AAAA,EAC/B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAA0B;AACxB,SACE;AAAA,IAAC,4BAAO;AAAA,IAAP;AAAA,MACC,SAAS,eAAe,QAAQ,EAAE,OAAO,GAAK,SAAS,EAAE;AAAA,MACzD,SAAS,EAAE,OAAO,MAAM,SAAS,EAAE;AAAA,MACnC,YAAY,EAAE,UAAU,KAAK,MAAM,UAAU;AAAA,MAC7C,WAAU;AAAA,MAEV;AAAA,oDAAC,SAAI,WAAU,6DACZ,mBAAS,CAAC,WACT;AAAA,UAAC,aAAAA;AAAA,UAAA;AAAA,YACC,KAAK;AAAA,YACL,KAAK;AAAA,YACL,OAAO;AAAA,YACP,QAAQ;AAAA,YACR,OAAM;AAAA,YACN,WAAU;AAAA,YACV,SAAS;AAAA,YACT,aAAY;AAAA,YACZ,UAAQ;AAAA;AAAA,QACV,IAEA,UAEJ;AAAA,QAEC,SAAS,MAAM,SAAS,IACvB,4CAAC,SAAI,WAAW,mBAAmB,MAAM,WAAW,IAAI,gBAAgB,aAAa,IAClF,gBAAM,IAAI,CAAC,SACV,6CAAC,SAAqB,WAAU,+DAC9B;AAAA,sDAAC,OAAE,WAAU,uBAAuB,eAAK,OAAM;AAAA,UAC/C,6CAAC,SAAI,WAAU,6EACZ;AAAA,iBAAK;AAAA,YACL,KAAK;AAAA,aACR;AAAA,aALQ,KAAK,KAMf,CACD,GACH,IACE;AAAA;AAAA;AAAA,EACN;AAEJ;AAWO,SAAS,iBAAiB;AAAA,EAC/B;AAAA,EACA;AAAA,EACA;AAAA,EACA,wBAAwB;AAAA,EACxB;AAAA,EACA;AACF,GAA0B;AACxB,SACE,6CAAC,SACE;AAAA,cAAU,wBACT,6CAAC,SAAI,WAAU,0CACZ;AAAA,eAAS,4CAAC,oCAAY,QAAgB,MAAK,MAAK,IAAK;AAAA,MACrD,wBACC,6CAAC,UAAK,WAAU,kJACd;AAAA,oDAAC,8BAAO,WAAU,WAAU;AAAA,QAAE;AAAA,SAEhC,IACE;AAAA,OACN,IACE;AAAA,IACH,kBAAkB,gBACjB,4CAAC,SAAI,WAAU,QACb;AAAA,MAAC;AAAA;AAAA,QACC;AAAA,QACA;AAAA,QACA,YAAY,UAAU,aAAa;AAAA;AAAA,IACrC,GACF,IACE;AAAA,IACJ,4CAAC,QAAG,WAAU,kCAAkC,gBAAK;AAAA,IACpD,cACC,4CAAC,OAAE,WAAU,sDAAsD,uBAAY,IAC7E;AAAA,KACN;AAEJ;AAWO,SAAS,cAAc,EAAE,cAAc,UAAU,GAAuB;AAC7E,SACE,6CAAC,SAAI,WAAU,2DACb;AAAA,gDAAC,UAAK,mBAAK;AAAA,IACX,4CAAC,YAAAC,SAAA,EAAK,MAAM,WAAW,WAAU,oDAC/B,sDAAC,yCAAe,SAAS,cAAc,GACzC;AAAA,KACF;AAEJ;AAEO,SAAS,kBAAkB,eAAuB,cAAsB;AAC7E,SAAO;AAAA,IACL;AAAA,MACE,OAAO,cAAc,eAAe;AAAA,MACpC,OAAO;AAAA,MACP,MAAM,4CAAC,8BAAO,WAAU,WAAU;AAAA,IACpC;AAAA,IACA;AAAA,MACE,OAAO,aAAa,eAAe;AAAA,MACnC,OAAO;AAAA,MACP,MAAM,4CAAC,6BAAM,WAAU,WAAU;AAAA,IACnC;AAAA,EACF;AACF;","names":["Image","Link"]}
1
+ {"version":3,"sources":["../../src/components/asset-top-sections.tsx"],"sourcesContent":["\"use client\";\n\nimport Link from \"next/link\";\nimport { motion } from \"framer-motion\";\nimport { useIntersectionActive } from \"../utils/use-intersection-active.js\";\nimport { AddressDisplay } from \"./address-display.js\";\nimport { ParentAttributionBanner } from \"./parent-attribution-banner.js\";\nimport { IpTypeBadge } from \"./ip-type-badge.js\";\nimport { Layers, Users } from \"lucide-react\";\n\ninterface AssetMediaColumnProps {\n shouldReduce: boolean;\n image: string | null;\n imageAlt: string;\n imgError: boolean;\n onImageError: () => void;\n fallback: React.ReactNode;\n /** Opens the full-screen lightbox. Omit for a non-interactive (still borderless, real-aspect-ratio) render. */\n onZoom?: () => void;\n /** Resolved animation_url — the live on-chain renderer, if any. */\n animationUrl?: string | null;\n /** Caller-computed eligibility for the living-render treatment. */\n live?: boolean;\n stats?: Array<{\n value: string;\n label: string;\n icon: React.ReactNode;\n }>;\n}\n\n/**\n * The platform's one asset media column — borderless, respects the work's\n * real aspect ratio (never forced 1:1), capped to the viewport so it always\n * fits on screen, optional click-to-zoom (foundations §III: image leads, no\n * border clutter).\n *\n * `live`-eligible tokens (a small partner allowlist, see\n * living-render-collections) swap the static image for a sandboxed iframe of\n * the token's own on-chain animation_url once the media scrolls into view —\n * see medialane-core/docs/specs/2026-07-28-gol-starknet-living-render-design.md.\n */\nexport function AssetMediaColumn({\n shouldReduce,\n image,\n imageAlt,\n imgError,\n onImageError,\n fallback,\n onZoom,\n animationUrl,\n live = false,\n stats,\n}: AssetMediaColumnProps) {\n const [ref, isVisible] = useIntersectionActive<HTMLDivElement>();\n const showLive = live && !!animationUrl && isVisible;\n\n return (\n <div ref={ref} className=\"w-full lg:sticky lg:top-16\">\n {showLive ? (\n <div className=\"w-full overflow-hidden rounded-3xl aspect-square\">\n <iframe\n src={animationUrl!}\n title={imageAlt}\n sandbox=\"allow-scripts\"\n loading=\"lazy\"\n className=\"w-full h-full border-0\"\n />\n </div>\n ) : !image || imgError ? (\n <div className=\"w-full overflow-hidden rounded-3xl\">{fallback}</div>\n ) : onZoom ? (\n <motion.button\n type=\"button\"\n onClick={onZoom}\n aria-label=\"View full image\"\n initial={shouldReduce ? false : { opacity: 0, scale: 0.98 }}\n animate={{ opacity: 1, scale: 1 }}\n transition={{ duration: 0.5 }}\n className=\"group block w-full overflow-hidden rounded-3xl cursor-zoom-in focus:outline-none\"\n >\n {/* eslint-disable-next-line @next/next/no-img-element */}\n <img\n src={image}\n alt={imageAlt}\n crossOrigin=\"anonymous\"\n onError={onImageError}\n className=\"w-full h-auto max-h-[80vh] object-contain\n transition duration-300 group-hover:opacity-95 group-active:scale-[0.99]\"\n />\n </motion.button>\n ) : (\n <motion.div\n initial={shouldReduce ? false : { opacity: 0, scale: 0.98 }}\n animate={{ opacity: 1, scale: 1 }}\n transition={{ duration: 0.5 }}\n className=\"block w-full overflow-hidden rounded-3xl\"\n >\n {/* eslint-disable-next-line @next/next/no-img-element */}\n <img\n src={image}\n alt={imageAlt}\n crossOrigin=\"anonymous\"\n onError={onImageError}\n className=\"w-full h-auto max-h-[80vh] object-contain\"\n />\n </motion.div>\n )}\n\n {stats && stats.length > 0 ? (\n <div className={`grid gap-3 mt-4 ${stats.length === 2 ? \"grid-cols-2\" : \"grid-cols-1\"}`}>\n {stats.map((stat) => (\n <div key={stat.label} className=\"rounded-xl border border-border bg-muted/20 p-4 text-center\">\n <p className=\"text-2xl font-black\">{stat.value}</p>\n <div className=\"flex items-center justify-center gap-1 text-xs text-muted-foreground mt-1\">\n {stat.icon}\n {stat.label}\n </div>\n </div>\n ))}\n </div>\n ) : null}\n </div>\n );\n}\n\ninterface AssetHeaderBlockProps {\n name: string;\n description?: string | null;\n ipType?: string | null;\n showMultiEditionBadge?: boolean;\n parentContract?: string | null;\n parentTokenId?: string | null;\n}\n\nexport function AssetHeaderBlock({\n name,\n description,\n ipType,\n showMultiEditionBadge = false,\n parentContract,\n parentTokenId,\n}: AssetHeaderBlockProps) {\n return (\n <div>\n {ipType || showMultiEditionBadge ? (\n <div className=\"flex items-center gap-2 flex-wrap mb-2\">\n {ipType ? <IpTypeBadge ipType={ipType} size=\"md\" /> : null}\n {showMultiEditionBadge ? (\n <span className=\"inline-flex items-center gap-1 text-[11px] font-semibold px-2.5 py-1 rounded-full border border-violet-500/30 bg-violet-500/10 text-violet-500\">\n <Layers className=\"h-3 w-3\" />\n Multi-edition\n </span>\n ) : null}\n </div>\n ) : null}\n {parentContract && parentTokenId ? (\n <div className=\"mb-3\">\n <ParentAttributionBanner\n parentContract={parentContract}\n parentTokenId={parentTokenId}\n parentName={`Token #${parentTokenId}`}\n />\n </div>\n ) : null}\n <h1 className=\"text-3xl lg:text-5xl font-bold\">{name}</h1>\n {description ? (\n <p className=\"text-sm text-muted-foreground leading-relaxed mt-1\">{description}</p>\n ) : null}\n </div>\n );\n}\n\nexport interface AssetOwnerRowProps {\n ownerAddress: string;\n ownerHref: string;\n}\n\n/** Single-owner identity (ERC-721) — its own sibling row, not nested inside\n * the collection bar (that reads as if ownership were a collection\n * attribute, 2026-07-05 feedback). ERC-1155 editions use `AssetOwnersPanel`\n * instead (multiple owners). */\nexport function AssetOwnerRow({ ownerAddress, ownerHref }: AssetOwnerRowProps) {\n return (\n <div className=\"flex items-center gap-1.5 text-xs text-muted-foreground\">\n <span>Owner</span>\n <Link href={ownerHref} className=\"hover:text-primary transition-colors font-medium\">\n <AddressDisplay address={ownerAddress} />\n </Link>\n </div>\n );\n}\n\nexport function buildEditionStats(totalEditions: number, uniqueOwners: number) {\n return [\n {\n value: totalEditions.toLocaleString(),\n label: \"editions minted\",\n icon: <Layers className=\"h-3 w-3\" />,\n },\n {\n value: uniqueOwners.toLocaleString(),\n label: \"unique owners\",\n icon: <Users className=\"h-3 w-3\" />,\n },\n ];\n}\n"],"mappings":"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","names":["Link"]}
@@ -2,18 +2,35 @@ import * as react_jsx_runtime from 'react/jsx-runtime';
2
2
 
3
3
  interface AssetMediaColumnProps {
4
4
  shouldReduce: boolean;
5
- image: string;
5
+ image: string | null;
6
6
  imageAlt: string;
7
7
  imgError: boolean;
8
8
  onImageError: () => void;
9
9
  fallback: React.ReactNode;
10
+ /** Opens the full-screen lightbox. Omit for a non-interactive (still borderless, real-aspect-ratio) render. */
11
+ onZoom?: () => void;
12
+ /** Resolved animation_url — the live on-chain renderer, if any. */
13
+ animationUrl?: string | null;
14
+ /** Caller-computed eligibility for the living-render treatment. */
15
+ live?: boolean;
10
16
  stats?: Array<{
11
17
  value: string;
12
18
  label: string;
13
19
  icon: React.ReactNode;
14
20
  }>;
15
21
  }
16
- declare function AssetMediaColumn({ shouldReduce, image, imageAlt, imgError, onImageError, fallback, stats, }: AssetMediaColumnProps): react_jsx_runtime.JSX.Element;
22
+ /**
23
+ * The platform's one asset media column — borderless, respects the work's
24
+ * real aspect ratio (never forced 1:1), capped to the viewport so it always
25
+ * fits on screen, optional click-to-zoom (foundations §III: image leads, no
26
+ * border clutter).
27
+ *
28
+ * `live`-eligible tokens (a small partner allowlist, see
29
+ * living-render-collections) swap the static image for a sandboxed iframe of
30
+ * the token's own on-chain animation_url once the media scrolls into view —
31
+ * see medialane-core/docs/specs/2026-07-28-gol-starknet-living-render-design.md.
32
+ */
33
+ declare function AssetMediaColumn({ shouldReduce, image, imageAlt, imgError, onImageError, fallback, onZoom, animationUrl, live, stats, }: AssetMediaColumnProps): react_jsx_runtime.JSX.Element;
17
34
  interface AssetHeaderBlockProps {
18
35
  name: string;
19
36
  description?: string | null;
@@ -2,18 +2,35 @@ import * as react_jsx_runtime from 'react/jsx-runtime';
2
2
 
3
3
  interface AssetMediaColumnProps {
4
4
  shouldReduce: boolean;
5
- image: string;
5
+ image: string | null;
6
6
  imageAlt: string;
7
7
  imgError: boolean;
8
8
  onImageError: () => void;
9
9
  fallback: React.ReactNode;
10
+ /** Opens the full-screen lightbox. Omit for a non-interactive (still borderless, real-aspect-ratio) render. */
11
+ onZoom?: () => void;
12
+ /** Resolved animation_url — the live on-chain renderer, if any. */
13
+ animationUrl?: string | null;
14
+ /** Caller-computed eligibility for the living-render treatment. */
15
+ live?: boolean;
10
16
  stats?: Array<{
11
17
  value: string;
12
18
  label: string;
13
19
  icon: React.ReactNode;
14
20
  }>;
15
21
  }
16
- declare function AssetMediaColumn({ shouldReduce, image, imageAlt, imgError, onImageError, fallback, stats, }: AssetMediaColumnProps): react_jsx_runtime.JSX.Element;
22
+ /**
23
+ * The platform's one asset media column — borderless, respects the work's
24
+ * real aspect ratio (never forced 1:1), capped to the viewport so it always
25
+ * fits on screen, optional click-to-zoom (foundations §III: image leads, no
26
+ * border clutter).
27
+ *
28
+ * `live`-eligible tokens (a small partner allowlist, see
29
+ * living-render-collections) swap the static image for a sandboxed iframe of
30
+ * the token's own on-chain animation_url once the media scrolls into view —
31
+ * see medialane-core/docs/specs/2026-07-28-gol-starknet-living-render-design.md.
32
+ */
33
+ declare function AssetMediaColumn({ shouldReduce, image, imageAlt, imgError, onImageError, fallback, onZoom, animationUrl, live, stats, }: AssetMediaColumnProps): react_jsx_runtime.JSX.Element;
17
34
  interface AssetHeaderBlockProps {
18
35
  name: string;
19
36
  description?: string | null;
@@ -1,8 +1,8 @@
1
1
  "use client";
2
2
  import { jsx, jsxs } from "react/jsx-runtime";
3
- import Image from "next/image";
4
3
  import Link from "next/link";
5
4
  import { motion } from "framer-motion";
5
+ import { useIntersectionActive } from "../utils/use-intersection-active.js";
6
6
  import { AddressDisplay } from "./address-display.js";
7
7
  import { ParentAttributionBanner } from "./parent-attribution-banner.js";
8
8
  import { IpTypeBadge } from "./ip-type-badge.js";
@@ -14,40 +14,71 @@ function AssetMediaColumn({
14
14
  imgError,
15
15
  onImageError,
16
16
  fallback,
17
+ onZoom,
18
+ animationUrl,
19
+ live = false,
17
20
  stats
18
21
  }) {
19
- return /* @__PURE__ */ jsxs(
20
- motion.div,
21
- {
22
- initial: shouldReduce ? false : { scale: 1, opacity: 0 },
23
- animate: { scale: 1.02, opacity: 1 },
24
- transition: { duration: 0.6, ease: "easeOut" },
25
- className: "overflow-hidden rounded-xl lg:sticky lg:top-16",
26
- children: [
27
- /* @__PURE__ */ jsx("div", { className: "rounded-2xl overflow-hidden border border-border bg-muted", children: image && !imgError ? /* @__PURE__ */ jsx(
28
- Image,
22
+ const [ref, isVisible] = useIntersectionActive();
23
+ const showLive = live && !!animationUrl && isVisible;
24
+ return /* @__PURE__ */ jsxs("div", { ref, className: "w-full lg:sticky lg:top-16", children: [
25
+ showLive ? /* @__PURE__ */ jsx("div", { className: "w-full overflow-hidden rounded-3xl aspect-square", children: /* @__PURE__ */ jsx(
26
+ "iframe",
27
+ {
28
+ src: animationUrl,
29
+ title: imageAlt,
30
+ sandbox: "allow-scripts",
31
+ loading: "lazy",
32
+ className: "w-full h-full border-0"
33
+ }
34
+ ) }) : !image || imgError ? /* @__PURE__ */ jsx("div", { className: "w-full overflow-hidden rounded-3xl", children: fallback }) : onZoom ? /* @__PURE__ */ jsx(
35
+ motion.button,
36
+ {
37
+ type: "button",
38
+ onClick: onZoom,
39
+ "aria-label": "View full image",
40
+ initial: shouldReduce ? false : { opacity: 0, scale: 0.98 },
41
+ animate: { opacity: 1, scale: 1 },
42
+ transition: { duration: 0.5 },
43
+ className: "group block w-full overflow-hidden rounded-3xl cursor-zoom-in focus:outline-none",
44
+ children: /* @__PURE__ */ jsx(
45
+ "img",
29
46
  {
30
47
  src: image,
31
48
  alt: imageAlt,
32
- width: 0,
33
- height: 0,
34
- sizes: "(max-width: 1024px) 100vw, 66vw",
35
- className: "w-full h-auto",
49
+ crossOrigin: "anonymous",
36
50
  onError: onImageError,
51
+ className: "w-full h-auto max-h-[80vh] object-contain\n transition duration-300 group-hover:opacity-95 group-active:scale-[0.99]"
52
+ }
53
+ )
54
+ }
55
+ ) : /* @__PURE__ */ jsx(
56
+ motion.div,
57
+ {
58
+ initial: shouldReduce ? false : { opacity: 0, scale: 0.98 },
59
+ animate: { opacity: 1, scale: 1 },
60
+ transition: { duration: 0.5 },
61
+ className: "block w-full overflow-hidden rounded-3xl",
62
+ children: /* @__PURE__ */ jsx(
63
+ "img",
64
+ {
65
+ src: image,
66
+ alt: imageAlt,
37
67
  crossOrigin: "anonymous",
38
- priority: true
68
+ onError: onImageError,
69
+ className: "w-full h-auto max-h-[80vh] object-contain"
39
70
  }
40
- ) : fallback }),
41
- stats && stats.length > 0 ? /* @__PURE__ */ jsx("div", { className: `grid gap-3 mt-4 ${stats.length === 2 ? "grid-cols-2" : "grid-cols-1"}`, children: stats.map((stat) => /* @__PURE__ */ jsxs("div", { className: "rounded-xl border border-border bg-muted/20 p-4 text-center", children: [
42
- /* @__PURE__ */ jsx("p", { className: "text-2xl font-black", children: stat.value }),
43
- /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-center gap-1 text-xs text-muted-foreground mt-1", children: [
44
- stat.icon,
45
- stat.label
46
- ] })
47
- ] }, stat.label)) }) : null
48
- ]
49
- }
50
- );
71
+ )
72
+ }
73
+ ),
74
+ stats && stats.length > 0 ? /* @__PURE__ */ jsx("div", { className: `grid gap-3 mt-4 ${stats.length === 2 ? "grid-cols-2" : "grid-cols-1"}`, children: stats.map((stat) => /* @__PURE__ */ jsxs("div", { className: "rounded-xl border border-border bg-muted/20 p-4 text-center", children: [
75
+ /* @__PURE__ */ jsx("p", { className: "text-2xl font-black", children: stat.value }),
76
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-center gap-1 text-xs text-muted-foreground mt-1", children: [
77
+ stat.icon,
78
+ stat.label
79
+ ] })
80
+ ] }, stat.label)) }) : null
81
+ ] });
51
82
  }
52
83
  function AssetHeaderBlock({
53
84
  name,
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/components/asset-top-sections.tsx"],"sourcesContent":["\"use client\";\n\nimport Image from \"next/image\";\nimport Link from \"next/link\";\nimport { motion } from \"framer-motion\";\nimport { AddressDisplay } from \"./address-display.js\";\nimport { ParentAttributionBanner } from \"./parent-attribution-banner.js\";\nimport { IpTypeBadge } from \"./ip-type-badge.js\";\nimport { Layers, Users } from \"lucide-react\";\n\ninterface AssetMediaColumnProps {\n shouldReduce: boolean;\n image: string;\n imageAlt: string;\n imgError: boolean;\n onImageError: () => void;\n fallback: React.ReactNode;\n stats?: Array<{\n value: string;\n label: string;\n icon: React.ReactNode;\n }>;\n}\n\nexport function AssetMediaColumn({\n shouldReduce,\n image,\n imageAlt,\n imgError,\n onImageError,\n fallback,\n stats,\n}: AssetMediaColumnProps) {\n return (\n <motion.div\n initial={shouldReduce ? false : { scale: 1.0, opacity: 0 }}\n animate={{ scale: 1.02, opacity: 1 }}\n transition={{ duration: 0.6, ease: \"easeOut\" }}\n className=\"overflow-hidden rounded-xl lg:sticky lg:top-16\"\n >\n <div className=\"rounded-2xl overflow-hidden border border-border bg-muted\">\n {image && !imgError ? (\n <Image\n src={image}\n alt={imageAlt}\n width={0}\n height={0}\n sizes=\"(max-width: 1024px) 100vw, 66vw\"\n className=\"w-full h-auto\"\n onError={onImageError}\n crossOrigin=\"anonymous\"\n priority\n />\n ) : (\n fallback\n )}\n </div>\n\n {stats && stats.length > 0 ? (\n <div className={`grid gap-3 mt-4 ${stats.length === 2 ? \"grid-cols-2\" : \"grid-cols-1\"}`}>\n {stats.map((stat) => (\n <div key={stat.label} className=\"rounded-xl border border-border bg-muted/20 p-4 text-center\">\n <p className=\"text-2xl font-black\">{stat.value}</p>\n <div className=\"flex items-center justify-center gap-1 text-xs text-muted-foreground mt-1\">\n {stat.icon}\n {stat.label}\n </div>\n </div>\n ))}\n </div>\n ) : null}\n </motion.div>\n );\n}\n\ninterface AssetHeaderBlockProps {\n name: string;\n description?: string | null;\n ipType?: string | null;\n showMultiEditionBadge?: boolean;\n parentContract?: string | null;\n parentTokenId?: string | null;\n}\n\nexport function AssetHeaderBlock({\n name,\n description,\n ipType,\n showMultiEditionBadge = false,\n parentContract,\n parentTokenId,\n}: AssetHeaderBlockProps) {\n return (\n <div>\n {ipType || showMultiEditionBadge ? (\n <div className=\"flex items-center gap-2 flex-wrap mb-2\">\n {ipType ? <IpTypeBadge ipType={ipType} size=\"md\" /> : null}\n {showMultiEditionBadge ? (\n <span className=\"inline-flex items-center gap-1 text-[11px] font-semibold px-2.5 py-1 rounded-full border border-violet-500/30 bg-violet-500/10 text-violet-500\">\n <Layers className=\"h-3 w-3\" />\n Multi-edition\n </span>\n ) : null}\n </div>\n ) : null}\n {parentContract && parentTokenId ? (\n <div className=\"mb-3\">\n <ParentAttributionBanner\n parentContract={parentContract}\n parentTokenId={parentTokenId}\n parentName={`Token #${parentTokenId}`}\n />\n </div>\n ) : null}\n <h1 className=\"text-3xl lg:text-5xl font-bold\">{name}</h1>\n {description ? (\n <p className=\"text-sm text-muted-foreground leading-relaxed mt-1\">{description}</p>\n ) : null}\n </div>\n );\n}\n\nexport interface AssetOwnerRowProps {\n ownerAddress: string;\n ownerHref: string;\n}\n\n/** Single-owner identity (ERC-721) — its own sibling row, not nested inside\n * the collection bar (that reads as if ownership were a collection\n * attribute, 2026-07-05 feedback). ERC-1155 editions use `AssetOwnersPanel`\n * instead (multiple owners). */\nexport function AssetOwnerRow({ ownerAddress, ownerHref }: AssetOwnerRowProps) {\n return (\n <div className=\"flex items-center gap-1.5 text-xs text-muted-foreground\">\n <span>Owner</span>\n <Link href={ownerHref} className=\"hover:text-primary transition-colors font-medium\">\n <AddressDisplay address={ownerAddress} />\n </Link>\n </div>\n );\n}\n\nexport function buildEditionStats(totalEditions: number, uniqueOwners: number) {\n return [\n {\n value: totalEditions.toLocaleString(),\n label: \"editions minted\",\n icon: <Layers className=\"h-3 w-3\" />,\n },\n {\n value: uniqueOwners.toLocaleString(),\n label: \"unique owners\",\n icon: <Users className=\"h-3 w-3\" />,\n },\n ];\n}\n"],"mappings":";AA0CU,cAqBI,YArBJ;AAxCV,OAAO,WAAW;AAClB,OAAO,UAAU;AACjB,SAAS,cAAc;AACvB,SAAS,sBAAsB;AAC/B,SAAS,+BAA+B;AACxC,SAAS,mBAAmB;AAC5B,SAAS,QAAQ,aAAa;AAgBvB,SAAS,iBAAiB;AAAA,EAC/B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAA0B;AACxB,SACE;AAAA,IAAC,OAAO;AAAA,IAAP;AAAA,MACC,SAAS,eAAe,QAAQ,EAAE,OAAO,GAAK,SAAS,EAAE;AAAA,MACzD,SAAS,EAAE,OAAO,MAAM,SAAS,EAAE;AAAA,MACnC,YAAY,EAAE,UAAU,KAAK,MAAM,UAAU;AAAA,MAC7C,WAAU;AAAA,MAEV;AAAA,4BAAC,SAAI,WAAU,6DACZ,mBAAS,CAAC,WACT;AAAA,UAAC;AAAA;AAAA,YACC,KAAK;AAAA,YACL,KAAK;AAAA,YACL,OAAO;AAAA,YACP,QAAQ;AAAA,YACR,OAAM;AAAA,YACN,WAAU;AAAA,YACV,SAAS;AAAA,YACT,aAAY;AAAA,YACZ,UAAQ;AAAA;AAAA,QACV,IAEA,UAEJ;AAAA,QAEC,SAAS,MAAM,SAAS,IACvB,oBAAC,SAAI,WAAW,mBAAmB,MAAM,WAAW,IAAI,gBAAgB,aAAa,IAClF,gBAAM,IAAI,CAAC,SACV,qBAAC,SAAqB,WAAU,+DAC9B;AAAA,8BAAC,OAAE,WAAU,uBAAuB,eAAK,OAAM;AAAA,UAC/C,qBAAC,SAAI,WAAU,6EACZ;AAAA,iBAAK;AAAA,YACL,KAAK;AAAA,aACR;AAAA,aALQ,KAAK,KAMf,CACD,GACH,IACE;AAAA;AAAA;AAAA,EACN;AAEJ;AAWO,SAAS,iBAAiB;AAAA,EAC/B;AAAA,EACA;AAAA,EACA;AAAA,EACA,wBAAwB;AAAA,EACxB;AAAA,EACA;AACF,GAA0B;AACxB,SACE,qBAAC,SACE;AAAA,cAAU,wBACT,qBAAC,SAAI,WAAU,0CACZ;AAAA,eAAS,oBAAC,eAAY,QAAgB,MAAK,MAAK,IAAK;AAAA,MACrD,wBACC,qBAAC,UAAK,WAAU,kJACd;AAAA,4BAAC,UAAO,WAAU,WAAU;AAAA,QAAE;AAAA,SAEhC,IACE;AAAA,OACN,IACE;AAAA,IACH,kBAAkB,gBACjB,oBAAC,SAAI,WAAU,QACb;AAAA,MAAC;AAAA;AAAA,QACC;AAAA,QACA;AAAA,QACA,YAAY,UAAU,aAAa;AAAA;AAAA,IACrC,GACF,IACE;AAAA,IACJ,oBAAC,QAAG,WAAU,kCAAkC,gBAAK;AAAA,IACpD,cACC,oBAAC,OAAE,WAAU,sDAAsD,uBAAY,IAC7E;AAAA,KACN;AAEJ;AAWO,SAAS,cAAc,EAAE,cAAc,UAAU,GAAuB;AAC7E,SACE,qBAAC,SAAI,WAAU,2DACb;AAAA,wBAAC,UAAK,mBAAK;AAAA,IACX,oBAAC,QAAK,MAAM,WAAW,WAAU,oDAC/B,8BAAC,kBAAe,SAAS,cAAc,GACzC;AAAA,KACF;AAEJ;AAEO,SAAS,kBAAkB,eAAuB,cAAsB;AAC7E,SAAO;AAAA,IACL;AAAA,MACE,OAAO,cAAc,eAAe;AAAA,MACpC,OAAO;AAAA,MACP,MAAM,oBAAC,UAAO,WAAU,WAAU;AAAA,IACpC;AAAA,IACA;AAAA,MACE,OAAO,aAAa,eAAe;AAAA,MACnC,OAAO;AAAA,MACP,MAAM,oBAAC,SAAM,WAAU,WAAU;AAAA,IACnC;AAAA,EACF;AACF;","names":[]}
1
+ {"version":3,"sources":["../../src/components/asset-top-sections.tsx"],"sourcesContent":["\"use client\";\n\nimport Link from \"next/link\";\nimport { motion } from \"framer-motion\";\nimport { useIntersectionActive } from \"../utils/use-intersection-active.js\";\nimport { AddressDisplay } from \"./address-display.js\";\nimport { ParentAttributionBanner } from \"./parent-attribution-banner.js\";\nimport { IpTypeBadge } from \"./ip-type-badge.js\";\nimport { Layers, Users } from \"lucide-react\";\n\ninterface AssetMediaColumnProps {\n shouldReduce: boolean;\n image: string | null;\n imageAlt: string;\n imgError: boolean;\n onImageError: () => void;\n fallback: React.ReactNode;\n /** Opens the full-screen lightbox. Omit for a non-interactive (still borderless, real-aspect-ratio) render. */\n onZoom?: () => void;\n /** Resolved animation_url — the live on-chain renderer, if any. */\n animationUrl?: string | null;\n /** Caller-computed eligibility for the living-render treatment. */\n live?: boolean;\n stats?: Array<{\n value: string;\n label: string;\n icon: React.ReactNode;\n }>;\n}\n\n/**\n * The platform's one asset media column — borderless, respects the work's\n * real aspect ratio (never forced 1:1), capped to the viewport so it always\n * fits on screen, optional click-to-zoom (foundations §III: image leads, no\n * border clutter).\n *\n * `live`-eligible tokens (a small partner allowlist, see\n * living-render-collections) swap the static image for a sandboxed iframe of\n * the token's own on-chain animation_url once the media scrolls into view —\n * see medialane-core/docs/specs/2026-07-28-gol-starknet-living-render-design.md.\n */\nexport function AssetMediaColumn({\n shouldReduce,\n image,\n imageAlt,\n imgError,\n onImageError,\n fallback,\n onZoom,\n animationUrl,\n live = false,\n stats,\n}: AssetMediaColumnProps) {\n const [ref, isVisible] = useIntersectionActive<HTMLDivElement>();\n const showLive = live && !!animationUrl && isVisible;\n\n return (\n <div ref={ref} className=\"w-full lg:sticky lg:top-16\">\n {showLive ? (\n <div className=\"w-full overflow-hidden rounded-3xl aspect-square\">\n <iframe\n src={animationUrl!}\n title={imageAlt}\n sandbox=\"allow-scripts\"\n loading=\"lazy\"\n className=\"w-full h-full border-0\"\n />\n </div>\n ) : !image || imgError ? (\n <div className=\"w-full overflow-hidden rounded-3xl\">{fallback}</div>\n ) : onZoom ? (\n <motion.button\n type=\"button\"\n onClick={onZoom}\n aria-label=\"View full image\"\n initial={shouldReduce ? false : { opacity: 0, scale: 0.98 }}\n animate={{ opacity: 1, scale: 1 }}\n transition={{ duration: 0.5 }}\n className=\"group block w-full overflow-hidden rounded-3xl cursor-zoom-in focus:outline-none\"\n >\n {/* eslint-disable-next-line @next/next/no-img-element */}\n <img\n src={image}\n alt={imageAlt}\n crossOrigin=\"anonymous\"\n onError={onImageError}\n className=\"w-full h-auto max-h-[80vh] object-contain\n transition duration-300 group-hover:opacity-95 group-active:scale-[0.99]\"\n />\n </motion.button>\n ) : (\n <motion.div\n initial={shouldReduce ? false : { opacity: 0, scale: 0.98 }}\n animate={{ opacity: 1, scale: 1 }}\n transition={{ duration: 0.5 }}\n className=\"block w-full overflow-hidden rounded-3xl\"\n >\n {/* eslint-disable-next-line @next/next/no-img-element */}\n <img\n src={image}\n alt={imageAlt}\n crossOrigin=\"anonymous\"\n onError={onImageError}\n className=\"w-full h-auto max-h-[80vh] object-contain\"\n />\n </motion.div>\n )}\n\n {stats && stats.length > 0 ? (\n <div className={`grid gap-3 mt-4 ${stats.length === 2 ? \"grid-cols-2\" : \"grid-cols-1\"}`}>\n {stats.map((stat) => (\n <div key={stat.label} className=\"rounded-xl border border-border bg-muted/20 p-4 text-center\">\n <p className=\"text-2xl font-black\">{stat.value}</p>\n <div className=\"flex items-center justify-center gap-1 text-xs text-muted-foreground mt-1\">\n {stat.icon}\n {stat.label}\n </div>\n </div>\n ))}\n </div>\n ) : null}\n </div>\n );\n}\n\ninterface AssetHeaderBlockProps {\n name: string;\n description?: string | null;\n ipType?: string | null;\n showMultiEditionBadge?: boolean;\n parentContract?: string | null;\n parentTokenId?: string | null;\n}\n\nexport function AssetHeaderBlock({\n name,\n description,\n ipType,\n showMultiEditionBadge = false,\n parentContract,\n parentTokenId,\n}: AssetHeaderBlockProps) {\n return (\n <div>\n {ipType || showMultiEditionBadge ? (\n <div className=\"flex items-center gap-2 flex-wrap mb-2\">\n {ipType ? <IpTypeBadge ipType={ipType} size=\"md\" /> : null}\n {showMultiEditionBadge ? (\n <span className=\"inline-flex items-center gap-1 text-[11px] font-semibold px-2.5 py-1 rounded-full border border-violet-500/30 bg-violet-500/10 text-violet-500\">\n <Layers className=\"h-3 w-3\" />\n Multi-edition\n </span>\n ) : null}\n </div>\n ) : null}\n {parentContract && parentTokenId ? (\n <div className=\"mb-3\">\n <ParentAttributionBanner\n parentContract={parentContract}\n parentTokenId={parentTokenId}\n parentName={`Token #${parentTokenId}`}\n />\n </div>\n ) : null}\n <h1 className=\"text-3xl lg:text-5xl font-bold\">{name}</h1>\n {description ? (\n <p className=\"text-sm text-muted-foreground leading-relaxed mt-1\">{description}</p>\n ) : null}\n </div>\n );\n}\n\nexport interface AssetOwnerRowProps {\n ownerAddress: string;\n ownerHref: string;\n}\n\n/** Single-owner identity (ERC-721) — its own sibling row, not nested inside\n * the collection bar (that reads as if ownership were a collection\n * attribute, 2026-07-05 feedback). ERC-1155 editions use `AssetOwnersPanel`\n * instead (multiple owners). */\nexport function AssetOwnerRow({ ownerAddress, ownerHref }: AssetOwnerRowProps) {\n return (\n <div className=\"flex items-center gap-1.5 text-xs text-muted-foreground\">\n <span>Owner</span>\n <Link href={ownerHref} className=\"hover:text-primary transition-colors font-medium\">\n <AddressDisplay address={ownerAddress} />\n </Link>\n </div>\n );\n}\n\nexport function buildEditionStats(totalEditions: number, uniqueOwners: number) {\n return [\n {\n value: totalEditions.toLocaleString(),\n label: \"editions minted\",\n icon: <Layers className=\"h-3 w-3\" />,\n },\n {\n value: uniqueOwners.toLocaleString(),\n label: \"unique owners\",\n icon: <Users className=\"h-3 w-3\" />,\n },\n ];\n}\n"],"mappings":"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","names":[]}
@@ -120,7 +120,14 @@ const GROUP_HEADING_CLASSES = (0, import_cn.cn)(
120
120
  "[&_[cmdk-group-heading]]:tracking-[0.08em]",
121
121
  "[&_[cmdk-group-heading]]:text-muted-foreground/60"
122
122
  );
123
- function NavCommandMenu({ commands, trigger, accountSlot, footerSlot }) {
123
+ function NavCommandMenu({
124
+ commands,
125
+ trigger,
126
+ accountSlot,
127
+ footerSlot,
128
+ showKeyboardHints = true,
129
+ brandSlot
130
+ }) {
124
131
  const [open, setOpen] = React.useState(false);
125
132
  const [query, setQuery] = React.useState("");
126
133
  const router = (0, import_navigation.useRouter)();
@@ -244,7 +251,7 @@ function NavCommandMenu({ commands, trigger, accountSlot, footerSlot }) {
244
251
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex items-center justify-between gap-3 border-t border-border/40 bg-muted/20 px-3 py-2", children: [
245
252
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex items-center gap-3", children: [
246
253
  footerSlot,
247
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "hidden items-center gap-3 text-[10.5px] text-muted-foreground/70 sm:flex", children: [
254
+ showKeyboardHints && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "hidden items-center gap-3 text-[10.5px] text-muted-foreground/70 sm:flex", children: [
248
255
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "flex items-center gap-1", children: [
249
256
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Kbd, { children: "\u2191" }),
250
257
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Kbd, { children: "\u2193" }),
@@ -256,7 +263,7 @@ function NavCommandMenu({ commands, trigger, accountSlot, footerSlot }) {
256
263
  ] })
257
264
  ] })
258
265
  ] }),
259
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "flex shrink-0 items-center gap-2 text-[10px] text-muted-foreground/50", children: [
266
+ brandSlot ?? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "flex shrink-0 items-center gap-2 text-[10px] text-muted-foreground/50", children: [
260
267
  "medialane",
261
268
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Kbd, { className: "hidden sm:inline-flex", children: "\u2318K" })
262
269
  ] })
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/components/nav-command-menu.tsx"],"sourcesContent":["\"use client\";\n\nimport * as React from \"react\";\nimport { Command } from \"cmdk\";\nimport { useRouter } from \"next/navigation\";\nimport { Search, X, ArrowRight } from \"lucide-react\";\nimport { AnimatePresence, motion } from \"framer-motion\";\nimport { cn } from \"../utils/cn.js\";\n\n// ── Types ─────────────────────────────────────────────────────────────────────\n\nexport interface NavCommand {\n id: string;\n label: string;\n icon: React.ComponentType<{ className?: string }>;\n href?: string;\n action?: () => void;\n /** Extra search terms beyond the label */\n keywords?: string[];\n /** Optional one-line description shown under the label */\n description?: string;\n}\n\nexport interface NavCommandGroup {\n /**\n * Optional group heading. Omit it for the primary group so its items\n * read as the top-level menu — they render first, emphasized, with no\n * label, separated from the headed groups below.\n */\n heading?: string;\n items: NavCommand[];\n}\n\nexport interface NavCommandMenuProps {\n commands: NavCommandGroup[];\n /**\n * Optional trigger element rendered inline at the component's mount point.\n * For most apps, omit this and call `useNavCommandMenu().open()` from a\n * separate button — that keeps the trigger in the right place in the layout.\n */\n trigger?: React.ReactNode;\n /**\n * Optional pinned account/connect area rendered below command results.\n * Apps own the auth implementation here (Clerk, ChipiPay, wallet connectors,\n * Privy, Cartridge, etc.) so the shared nav stays framework-agnostic.\n */\n accountSlot?: React.ReactNode;\n /**\n * Optional control rendered in the footer row (e.g. a theme toggle).\n * Apps own theme state (next-themes etc.) so the shared nav stays\n * framework-agnostic — same pattern as `accountSlot`.\n */\n footerSlot?: React.ReactNode;\n}\n\n// ── Singleton hook ─────────────────────────────────────────────────────────────\n\nconst ML_NAV_OPEN = \"ml:nav-open\";\nconst ML_NAV_CLOSE = \"ml:nav-close\";\n\nexport function useNavCommandMenu() {\n return {\n open: () => document.dispatchEvent(new CustomEvent(ML_NAV_OPEN)),\n close: () => document.dispatchEvent(new CustomEvent(ML_NAV_CLOSE)),\n };\n}\n\n// ── Small primitives ──────────────────────────────────────────────────────────\n\nfunction Kbd({ children, className }: { children: React.ReactNode; className?: string }) {\n return (\n <kbd\n className={cn(\n \"inline-flex min-w-[18px] items-center justify-center rounded-md bg-muted/60 px-1.5 py-0.5\",\n \"font-sans text-[10px] leading-none text-muted-foreground\",\n className\n )}\n >\n {children}\n </kbd>\n );\n}\n\nfunction CommandRow({ item, primary, onSelect }: { item: NavCommand; primary: boolean; onSelect: () => void }) {\n return (\n <Command.Item\n value={[item.label, ...(item.keywords ?? [])].join(\" \")}\n onSelect={onSelect}\n className={cn(\n \"group/item flex cursor-pointer items-center gap-3 rounded-xl\",\n \"transition-colors duration-150 aria-selected:bg-primary/10\",\n primary ? \"px-2.5 py-2.5\" : \"px-2.5 py-2\"\n )}\n >\n <span\n className={cn(\n \"flex shrink-0 items-center justify-center rounded-lg transition-colors\",\n \"bg-muted/50 group-aria-selected/item:bg-primary/15\",\n primary ? \"h-9 w-9\" : \"h-8 w-8\"\n )}\n >\n <item.icon\n className={cn(\n \"text-foreground/80 transition-colors group-aria-selected/item:text-primary\",\n primary ? \"h-[18px] w-[18px]\" : \"h-4 w-4\"\n )}\n />\n </span>\n <span className=\"min-w-0 flex-1\">\n <span\n className={cn(\n \"block truncate leading-tight\",\n primary ? \"text-[15px] font-medium\" : \"text-sm\"\n )}\n >\n {item.label}\n </span>\n {item.description && (\n <span className=\"mt-0.5 block truncate text-[11.5px] leading-tight text-muted-foreground\">\n {item.description}\n </span>\n )}\n </span>\n <ArrowRight className=\"h-3.5 w-3.5 shrink-0 text-muted-foreground/30 transition-colors group-aria-selected/item:text-primary/70\" />\n </Command.Item>\n );\n}\n\nconst GROUP_HEADING_CLASSES = cn(\n \"[&_[cmdk-group-heading]]:px-2.5\",\n \"[&_[cmdk-group-heading]]:pt-2\",\n \"[&_[cmdk-group-heading]]:pb-1\",\n \"[&_[cmdk-group-heading]]:text-[10.5px]\",\n \"[&_[cmdk-group-heading]]:font-semibold\",\n \"[&_[cmdk-group-heading]]:uppercase\",\n \"[&_[cmdk-group-heading]]:tracking-[0.08em]\",\n \"[&_[cmdk-group-heading]]:text-muted-foreground/60\"\n);\n\n// ── Component ─────────────────────────────────────────────────────────────────\n\nexport function NavCommandMenu({ commands, trigger, accountSlot, footerSlot }: NavCommandMenuProps) {\n const [open, setOpen] = React.useState(false);\n const [query, setQuery] = React.useState(\"\");\n const router = useRouter();\n const inputRef = React.useRef<HTMLInputElement>(null);\n\n React.useEffect(() => {\n if (!open) return;\n setQuery(\"\");\n const t = setTimeout(() => inputRef.current?.focus(), 60);\n return () => clearTimeout(t);\n }, [open]);\n\n React.useEffect(() => {\n const onKey = (e: KeyboardEvent) => {\n if (e.key === \"k\" && (e.metaKey || e.ctrlKey)) {\n e.preventDefault();\n setOpen((prev) => !prev);\n }\n if (e.key === \"Escape\") setOpen(false);\n };\n const onOpen = () => setOpen(true);\n const onClose = () => setOpen(false);\n\n document.addEventListener(\"keydown\", onKey);\n document.addEventListener(ML_NAV_OPEN, onOpen);\n document.addEventListener(ML_NAV_CLOSE, onClose);\n return () => {\n document.removeEventListener(\"keydown\", onKey);\n document.removeEventListener(ML_NAV_OPEN, onOpen);\n document.removeEventListener(ML_NAV_CLOSE, onClose);\n };\n }, []);\n\n const runCommand = React.useCallback(\n (cmd: NavCommand) => {\n setOpen(false);\n if (cmd.href) router.push(cmd.href);\n else cmd.action?.();\n },\n [router]\n );\n\n return (\n <>\n {trigger}\n\n <AnimatePresence>\n {open && (\n <>\n {/* Backdrop blur */}\n <motion.div\n className=\"nav-canvas-overlay\"\n initial={{ opacity: 0 }}\n animate={{ opacity: 1 }}\n exit={{ opacity: 0 }}\n transition={{ duration: 0.15 }}\n onClick={() => setOpen(false)}\n />\n\n {/* Command panel — centered on desktop, bottom sheet on mobile */}\n <motion.div\n className=\"fixed inset-0 z-[101] flex items-end justify-center p-3 pb-4 sm:items-center sm:p-4\"\n initial={{ opacity: 0, y: 24, scale: 1 }}\n animate={{ opacity: 1, y: 0, scale: 1 }}\n exit={{ opacity: 0, y: 24 }}\n transition={{ duration: 0.18, ease: \"easeOut\" }}\n onClick={() => setOpen(false)}\n >\n <div\n className={cn(\n \"flex w-full max-h-[85dvh] flex-col overflow-hidden rounded-[20px] sm:max-h-[min(680px,88dvh)] sm:max-w-[620px]\",\n \"border border-border/40 bg-background/90 shadow-2xl backdrop-blur-2xl backdrop-saturate-150\"\n )}\n onClick={(e) => e.stopPropagation()}\n >\n <Command shouldFilter label=\"Medialane navigation\" className=\"flex min-h-0 flex-1 flex-col\">\n {/* Drag handle (mobile) */}\n <div className=\"flex justify-center pt-2.5 sm:hidden\" aria-hidden=\"true\">\n <span className=\"h-1 w-9 rounded-full bg-muted-foreground/30\" />\n </div>\n\n {/* Search bar */}\n <div className=\"flex items-center gap-3 border-b border-border/40 px-4 py-3.5\">\n <Search\n className={cn(\n \"h-[18px] w-[18px] shrink-0 transition-colors\",\n query ? \"text-primary\" : \"text-muted-foreground\"\n )}\n />\n <Command.Input\n ref={inputRef}\n value={query}\n onValueChange={setQuery}\n placeholder=\"Search or run a command…\"\n className=\"flex-1 bg-transparent text-[15px] outline-none placeholder:text-muted-foreground\"\n />\n <Kbd className=\"hidden sm:inline-flex\">esc</Kbd>\n <button\n onClick={() => setOpen(false)}\n className=\"rounded-md p-1 transition-colors hover:bg-muted/50 sm:hidden\"\n aria-label=\"Close\"\n >\n <X className=\"h-4 w-4 text-muted-foreground\" />\n </button>\n </div>\n\n {/* Results */}\n <Command.List className=\"min-h-0 flex-1 overflow-y-auto overscroll-contain p-2\">\n <Command.Empty className=\"py-8 text-center text-sm text-muted-foreground\">\n No results found.\n </Command.Empty>\n\n {commands.map((group, i) => {\n const primary = !group.heading;\n return (\n <React.Fragment key={group.heading ?? `__primary-${i}`}>\n {i > 0 && (\n <Command.Separator className=\"my-1.5 h-px bg-border/40\" />\n )}\n <Command.Group heading={group.heading} className={GROUP_HEADING_CLASSES}>\n {group.items.map((item) => (\n <CommandRow\n key={item.id}\n item={item}\n primary={primary}\n onSelect={() => runCommand(item)}\n />\n ))}\n </Command.Group>\n </React.Fragment>\n );\n })}\n </Command.List>\n\n {accountSlot && (\n <div className=\"border-t border-border/40 bg-muted/20 px-3 py-3\">\n {accountSlot}\n </div>\n )}\n\n {/* Footer */}\n <div className=\"flex items-center justify-between gap-3 border-t border-border/40 bg-muted/20 px-3 py-2\">\n <div className=\"flex items-center gap-3\">\n {footerSlot}\n <span className=\"hidden items-center gap-3 text-[10.5px] text-muted-foreground/70 sm:flex\">\n <span className=\"flex items-center gap-1\"><Kbd>↑</Kbd><Kbd>↓</Kbd> Navigate</span>\n <span className=\"flex items-center gap-1\"><Kbd>↵</Kbd> Open</span>\n </span>\n </div>\n <span className=\"flex shrink-0 items-center gap-2 text-[10px] text-muted-foreground/50\">\n medialane\n <Kbd className=\"hidden sm:inline-flex\">⌘K</Kbd>\n </span>\n </div>\n </Command>\n </div>\n </motion.div>\n </>\n )}\n </AnimatePresence>\n </>\n );\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../src/components/nav-command-menu.tsx"],"sourcesContent":["\"use client\";\n\nimport * as React from \"react\";\nimport { Command } from \"cmdk\";\nimport { useRouter } from \"next/navigation\";\nimport { Search, X, ArrowRight } from \"lucide-react\";\nimport { AnimatePresence, motion } from \"framer-motion\";\nimport { cn } from \"../utils/cn.js\";\n\n// ── Types ─────────────────────────────────────────────────────────────────────\n\nexport interface NavCommand {\n id: string;\n label: string;\n icon: React.ComponentType<{ className?: string }>;\n href?: string;\n action?: () => void;\n /** Extra search terms beyond the label */\n keywords?: string[];\n /** Optional one-line description shown under the label */\n description?: string;\n}\n\nexport interface NavCommandGroup {\n /**\n * Optional group heading. Omit it for the primary group so its items\n * read as the top-level menu — they render first, emphasized, with no\n * label, separated from the headed groups below.\n */\n heading?: string;\n items: NavCommand[];\n}\n\nexport interface NavCommandMenuProps {\n commands: NavCommandGroup[];\n /**\n * Optional trigger element rendered inline at the component's mount point.\n * For most apps, omit this and call `useNavCommandMenu().open()` from a\n * separate button — that keeps the trigger in the right place in the layout.\n */\n trigger?: React.ReactNode;\n /**\n * Optional pinned account/connect area rendered below command results.\n * Apps own the auth implementation here (Clerk, ChipiPay, wallet connectors,\n * Privy, Cartridge, etc.) so the shared nav stays framework-agnostic.\n */\n accountSlot?: React.ReactNode;\n /**\n * Optional control rendered in the footer row (e.g. a theme toggle).\n * Apps own theme state (next-themes etc.) so the shared nav stays\n * framework-agnostic — same pattern as `accountSlot`.\n */\n footerSlot?: React.ReactNode;\n /**\n * Show the \"↑↓ Navigate ↵ Open\" keyboard-hint text next to `footerSlot`.\n * Defaults to `true` (unchanged behavior).\n */\n showKeyboardHints?: boolean;\n /**\n * Optional override for the trailing \"medialane ⌘K\" brandmark at the\n * footer's right edge — replaces it entirely rather than adorning it, so\n * an app can put something more actionable there (e.g. a \"Connect\" button)\n * instead of a static label. Omit to keep the default brandmark.\n */\n brandSlot?: React.ReactNode;\n}\n\n// ── Singleton hook ─────────────────────────────────────────────────────────────\n\nconst ML_NAV_OPEN = \"ml:nav-open\";\nconst ML_NAV_CLOSE = \"ml:nav-close\";\n\nexport function useNavCommandMenu() {\n return {\n open: () => document.dispatchEvent(new CustomEvent(ML_NAV_OPEN)),\n close: () => document.dispatchEvent(new CustomEvent(ML_NAV_CLOSE)),\n };\n}\n\n// ── Small primitives ──────────────────────────────────────────────────────────\n\nfunction Kbd({ children, className }: { children: React.ReactNode; className?: string }) {\n return (\n <kbd\n className={cn(\n \"inline-flex min-w-[18px] items-center justify-center rounded-md bg-muted/60 px-1.5 py-0.5\",\n \"font-sans text-[10px] leading-none text-muted-foreground\",\n className\n )}\n >\n {children}\n </kbd>\n );\n}\n\nfunction CommandRow({ item, primary, onSelect }: { item: NavCommand; primary: boolean; onSelect: () => void }) {\n return (\n <Command.Item\n value={[item.label, ...(item.keywords ?? [])].join(\" \")}\n onSelect={onSelect}\n className={cn(\n \"group/item flex cursor-pointer items-center gap-3 rounded-xl\",\n \"transition-colors duration-150 aria-selected:bg-primary/10\",\n primary ? \"px-2.5 py-2.5\" : \"px-2.5 py-2\"\n )}\n >\n <span\n className={cn(\n \"flex shrink-0 items-center justify-center rounded-lg transition-colors\",\n \"bg-muted/50 group-aria-selected/item:bg-primary/15\",\n primary ? \"h-9 w-9\" : \"h-8 w-8\"\n )}\n >\n <item.icon\n className={cn(\n \"text-foreground/80 transition-colors group-aria-selected/item:text-primary\",\n primary ? \"h-[18px] w-[18px]\" : \"h-4 w-4\"\n )}\n />\n </span>\n <span className=\"min-w-0 flex-1\">\n <span\n className={cn(\n \"block truncate leading-tight\",\n primary ? \"text-[15px] font-medium\" : \"text-sm\"\n )}\n >\n {item.label}\n </span>\n {item.description && (\n <span className=\"mt-0.5 block truncate text-[11.5px] leading-tight text-muted-foreground\">\n {item.description}\n </span>\n )}\n </span>\n <ArrowRight className=\"h-3.5 w-3.5 shrink-0 text-muted-foreground/30 transition-colors group-aria-selected/item:text-primary/70\" />\n </Command.Item>\n );\n}\n\nconst GROUP_HEADING_CLASSES = cn(\n \"[&_[cmdk-group-heading]]:px-2.5\",\n \"[&_[cmdk-group-heading]]:pt-2\",\n \"[&_[cmdk-group-heading]]:pb-1\",\n \"[&_[cmdk-group-heading]]:text-[10.5px]\",\n \"[&_[cmdk-group-heading]]:font-semibold\",\n \"[&_[cmdk-group-heading]]:uppercase\",\n \"[&_[cmdk-group-heading]]:tracking-[0.08em]\",\n \"[&_[cmdk-group-heading]]:text-muted-foreground/60\"\n);\n\n// ── Component ─────────────────────────────────────────────────────────────────\n\nexport function NavCommandMenu({\n commands,\n trigger,\n accountSlot,\n footerSlot,\n showKeyboardHints = true,\n brandSlot,\n}: NavCommandMenuProps) {\n const [open, setOpen] = React.useState(false);\n const [query, setQuery] = React.useState(\"\");\n const router = useRouter();\n const inputRef = React.useRef<HTMLInputElement>(null);\n\n React.useEffect(() => {\n if (!open) return;\n setQuery(\"\");\n const t = setTimeout(() => inputRef.current?.focus(), 60);\n return () => clearTimeout(t);\n }, [open]);\n\n React.useEffect(() => {\n const onKey = (e: KeyboardEvent) => {\n if (e.key === \"k\" && (e.metaKey || e.ctrlKey)) {\n e.preventDefault();\n setOpen((prev) => !prev);\n }\n if (e.key === \"Escape\") setOpen(false);\n };\n const onOpen = () => setOpen(true);\n const onClose = () => setOpen(false);\n\n document.addEventListener(\"keydown\", onKey);\n document.addEventListener(ML_NAV_OPEN, onOpen);\n document.addEventListener(ML_NAV_CLOSE, onClose);\n return () => {\n document.removeEventListener(\"keydown\", onKey);\n document.removeEventListener(ML_NAV_OPEN, onOpen);\n document.removeEventListener(ML_NAV_CLOSE, onClose);\n };\n }, []);\n\n const runCommand = React.useCallback(\n (cmd: NavCommand) => {\n setOpen(false);\n if (cmd.href) router.push(cmd.href);\n else cmd.action?.();\n },\n [router]\n );\n\n return (\n <>\n {trigger}\n\n <AnimatePresence>\n {open && (\n <>\n {/* Backdrop blur */}\n <motion.div\n className=\"nav-canvas-overlay\"\n initial={{ opacity: 0 }}\n animate={{ opacity: 1 }}\n exit={{ opacity: 0 }}\n transition={{ duration: 0.15 }}\n onClick={() => setOpen(false)}\n />\n\n {/* Command panel — centered on desktop, bottom sheet on mobile */}\n <motion.div\n className=\"fixed inset-0 z-[101] flex items-end justify-center p-3 pb-4 sm:items-center sm:p-4\"\n initial={{ opacity: 0, y: 24, scale: 1 }}\n animate={{ opacity: 1, y: 0, scale: 1 }}\n exit={{ opacity: 0, y: 24 }}\n transition={{ duration: 0.18, ease: \"easeOut\" }}\n onClick={() => setOpen(false)}\n >\n <div\n className={cn(\n \"flex w-full max-h-[85dvh] flex-col overflow-hidden rounded-[20px] sm:max-h-[min(680px,88dvh)] sm:max-w-[620px]\",\n \"border border-border/40 bg-background/90 shadow-2xl backdrop-blur-2xl backdrop-saturate-150\"\n )}\n onClick={(e) => e.stopPropagation()}\n >\n <Command shouldFilter label=\"Medialane navigation\" className=\"flex min-h-0 flex-1 flex-col\">\n {/* Drag handle (mobile) */}\n <div className=\"flex justify-center pt-2.5 sm:hidden\" aria-hidden=\"true\">\n <span className=\"h-1 w-9 rounded-full bg-muted-foreground/30\" />\n </div>\n\n {/* Search bar */}\n <div className=\"flex items-center gap-3 border-b border-border/40 px-4 py-3.5\">\n <Search\n className={cn(\n \"h-[18px] w-[18px] shrink-0 transition-colors\",\n query ? \"text-primary\" : \"text-muted-foreground\"\n )}\n />\n <Command.Input\n ref={inputRef}\n value={query}\n onValueChange={setQuery}\n placeholder=\"Search or run a command…\"\n className=\"flex-1 bg-transparent text-[15px] outline-none placeholder:text-muted-foreground\"\n />\n <Kbd className=\"hidden sm:inline-flex\">esc</Kbd>\n <button\n onClick={() => setOpen(false)}\n className=\"rounded-md p-1 transition-colors hover:bg-muted/50 sm:hidden\"\n aria-label=\"Close\"\n >\n <X className=\"h-4 w-4 text-muted-foreground\" />\n </button>\n </div>\n\n {/* Results */}\n <Command.List className=\"min-h-0 flex-1 overflow-y-auto overscroll-contain p-2\">\n <Command.Empty className=\"py-8 text-center text-sm text-muted-foreground\">\n No results found.\n </Command.Empty>\n\n {commands.map((group, i) => {\n const primary = !group.heading;\n return (\n <React.Fragment key={group.heading ?? `__primary-${i}`}>\n {i > 0 && (\n <Command.Separator className=\"my-1.5 h-px bg-border/40\" />\n )}\n <Command.Group heading={group.heading} className={GROUP_HEADING_CLASSES}>\n {group.items.map((item) => (\n <CommandRow\n key={item.id}\n item={item}\n primary={primary}\n onSelect={() => runCommand(item)}\n />\n ))}\n </Command.Group>\n </React.Fragment>\n );\n })}\n </Command.List>\n\n {accountSlot && (\n <div className=\"border-t border-border/40 bg-muted/20 px-3 py-3\">\n {accountSlot}\n </div>\n )}\n\n {/* Footer */}\n <div className=\"flex items-center justify-between gap-3 border-t border-border/40 bg-muted/20 px-3 py-2\">\n <div className=\"flex items-center gap-3\">\n {footerSlot}\n {showKeyboardHints && (\n <span className=\"hidden items-center gap-3 text-[10.5px] text-muted-foreground/70 sm:flex\">\n <span className=\"flex items-center gap-1\"><Kbd>↑</Kbd><Kbd>↓</Kbd> Navigate</span>\n <span className=\"flex items-center gap-1\"><Kbd>↵</Kbd> Open</span>\n </span>\n )}\n </div>\n {brandSlot ?? (\n <span className=\"flex shrink-0 items-center gap-2 text-[10px] text-muted-foreground/50\">\n medialane\n <Kbd className=\"hidden sm:inline-flex\">⌘K</Kbd>\n </span>\n )}\n </div>\n </Command>\n </div>\n </motion.div>\n </>\n )}\n </AnimatePresence>\n </>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAmFI;AAjFJ,YAAuB;AACvB,kBAAwB;AACxB,wBAA0B;AAC1B,0BAAsC;AACtC,2BAAwC;AACxC,gBAAmB;AA8DnB,MAAM,cAAe;AACrB,MAAM,eAAe;AAEd,SAAS,oBAAoB;AAClC,SAAO;AAAA,IACL,MAAO,MAAM,SAAS,cAAc,IAAI,YAAY,WAAW,CAAC;AAAA,IAChE,OAAO,MAAM,SAAS,cAAc,IAAI,YAAY,YAAY,CAAC;AAAA,EACnE;AACF;AAIA,SAAS,IAAI,EAAE,UAAU,UAAU,GAAsD;AACvF,SACE;AAAA,IAAC;AAAA;AAAA,MACC,eAAW;AAAA,QACT;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAAA,MAEC;AAAA;AAAA,EACH;AAEJ;AAEA,SAAS,WAAW,EAAE,MAAM,SAAS,SAAS,GAAiE;AAC7G,SACE;AAAA,IAAC,oBAAQ;AAAA,IAAR;AAAA,MACC,OAAO,CAAC,KAAK,OAAO,GAAI,KAAK,YAAY,CAAC,CAAE,EAAE,KAAK,GAAG;AAAA,MACtD;AAAA,MACA,eAAW;AAAA,QACT;AAAA,QACA;AAAA,QACA,UAAU,kBAAkB;AAAA,MAC9B;AAAA,MAEA;AAAA;AAAA,UAAC;AAAA;AAAA,YACC,eAAW;AAAA,cACT;AAAA,cACA;AAAA,cACA,UAAU,YAAY;AAAA,YACxB;AAAA,YAEA;AAAA,cAAC,KAAK;AAAA,cAAL;AAAA,gBACC,eAAW;AAAA,kBACT;AAAA,kBACA,UAAU,sBAAsB;AAAA,gBAClC;AAAA;AAAA,YACF;AAAA;AAAA,QACF;AAAA,QACA,6CAAC,UAAK,WAAU,kBACd;AAAA;AAAA,YAAC;AAAA;AAAA,cACC,eAAW;AAAA,gBACT;AAAA,gBACA,UAAU,4BAA4B;AAAA,cACxC;AAAA,cAEC,eAAK;AAAA;AAAA,UACR;AAAA,UACC,KAAK,eACJ,4CAAC,UAAK,WAAU,2EACb,eAAK,aACR;AAAA,WAEJ;AAAA,QACA,4CAAC,kCAAW,WAAU,4GAA2G;AAAA;AAAA;AAAA,EACnI;AAEJ;AAEA,MAAM,4BAAwB;AAAA,EAC5B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAIO,SAAS,eAAe;AAAA,EAC7B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,oBAAoB;AAAA,EACpB;AACF,GAAwB;AACtB,QAAM,CAAC,MAAM,OAAO,IAAI,MAAM,SAAS,KAAK;AAC5C,QAAM,CAAC,OAAO,QAAQ,IAAI,MAAM,SAAS,EAAE;AAC3C,QAAM,aAAS,6BAAU;AACzB,QAAM,WAAW,MAAM,OAAyB,IAAI;AAEpD,QAAM,UAAU,MAAM;AACpB,QAAI,CAAC,KAAM;AACX,aAAS,EAAE;AACX,UAAM,IAAI,WAAW,MAAM,SAAS,SAAS,MAAM,GAAG,EAAE;AACxD,WAAO,MAAM,aAAa,CAAC;AAAA,EAC7B,GAAG,CAAC,IAAI,CAAC;AAET,QAAM,UAAU,MAAM;AACpB,UAAM,QAAQ,CAAC,MAAqB;AAClC,UAAI,EAAE,QAAQ,QAAQ,EAAE,WAAW,EAAE,UAAU;AAC7C,UAAE,eAAe;AACjB,gBAAQ,CAAC,SAAS,CAAC,IAAI;AAAA,MACzB;AACA,UAAI,EAAE,QAAQ,SAAU,SAAQ,KAAK;AAAA,IACvC;AACA,UAAM,SAAU,MAAM,QAAQ,IAAI;AAClC,UAAM,UAAU,MAAM,QAAQ,KAAK;AAEnC,aAAS,iBAAiB,WAAW,KAAK;AAC1C,aAAS,iBAAiB,aAAa,MAAM;AAC7C,aAAS,iBAAiB,cAAc,OAAO;AAC/C,WAAO,MAAM;AACX,eAAS,oBAAoB,WAAW,KAAK;AAC7C,eAAS,oBAAoB,aAAa,MAAM;AAChD,eAAS,oBAAoB,cAAc,OAAO;AAAA,IACpD;AAAA,EACF,GAAG,CAAC,CAAC;AAEL,QAAM,aAAa,MAAM;AAAA,IACvB,CAAC,QAAoB;AACnB,cAAQ,KAAK;AACb,UAAI,IAAI,KAAM,QAAO,KAAK,IAAI,IAAI;AAAA,UAC7B,KAAI,SAAS;AAAA,IACpB;AAAA,IACA,CAAC,MAAM;AAAA,EACT;AAEA,SACE,4EACG;AAAA;AAAA,IAED,4CAAC,wCACE,kBACC,4EAEE;AAAA;AAAA,QAAC,4BAAO;AAAA,QAAP;AAAA,UACC,WAAU;AAAA,UACV,SAAS,EAAE,SAAS,EAAE;AAAA,UACtB,SAAS,EAAE,SAAS,EAAE;AAAA,UACtB,MAAM,EAAE,SAAS,EAAE;AAAA,UACnB,YAAY,EAAE,UAAU,KAAK;AAAA,UAC7B,SAAS,MAAM,QAAQ,KAAK;AAAA;AAAA,MAC9B;AAAA,MAGA;AAAA,QAAC,4BAAO;AAAA,QAAP;AAAA,UACC,WAAU;AAAA,UACV,SAAS,EAAE,SAAS,GAAG,GAAG,IAAI,OAAO,EAAE;AAAA,UACvC,SAAS,EAAE,SAAS,GAAG,GAAG,GAAG,OAAO,EAAE;AAAA,UACtC,MAAM,EAAE,SAAS,GAAG,GAAG,GAAG;AAAA,UAC1B,YAAY,EAAE,UAAU,MAAM,MAAM,UAAU;AAAA,UAC9C,SAAS,MAAM,QAAQ,KAAK;AAAA,UAE5B;AAAA,YAAC;AAAA;AAAA,cACC,eAAW;AAAA,gBACT;AAAA,gBACA;AAAA,cACF;AAAA,cACA,SAAS,CAAC,MAAM,EAAE,gBAAgB;AAAA,cAElC,uDAAC,uBAAQ,cAAY,MAAC,OAAM,wBAAuB,WAAU,gCAE3D;AAAA,4DAAC,SAAI,WAAU,wCAAuC,eAAY,QAChE,sDAAC,UAAK,WAAU,+CAA8C,GAChE;AAAA,gBAGA,6CAAC,SAAI,WAAU,iEACb;AAAA;AAAA,oBAAC;AAAA;AAAA,sBACC,eAAW;AAAA,wBACT;AAAA,wBACA,QAAQ,iBAAiB;AAAA,sBAC3B;AAAA;AAAA,kBACF;AAAA,kBACA;AAAA,oBAAC,oBAAQ;AAAA,oBAAR;AAAA,sBACC,KAAK;AAAA,sBACL,OAAO;AAAA,sBACP,eAAe;AAAA,sBACf,aAAY;AAAA,sBACZ,WAAU;AAAA;AAAA,kBACZ;AAAA,kBACA,4CAAC,OAAI,WAAU,yBAAwB,iBAAG;AAAA,kBAC1C;AAAA,oBAAC;AAAA;AAAA,sBACC,SAAS,MAAM,QAAQ,KAAK;AAAA,sBAC5B,WAAU;AAAA,sBACV,cAAW;AAAA,sBAEX,sDAAC,yBAAE,WAAU,iCAAgC;AAAA;AAAA,kBAC/C;AAAA,mBACF;AAAA,gBAGA,6CAAC,oBAAQ,MAAR,EAAa,WAAU,yDACtB;AAAA,8DAAC,oBAAQ,OAAR,EAAc,WAAU,kDAAiD,+BAE1E;AAAA,kBAEC,SAAS,IAAI,CAAC,OAAO,MAAM;AAC1B,0BAAM,UAAU,CAAC,MAAM;AACvB,2BACA,6CAAC,MAAM,UAAN,EACE;AAAA,0BAAI,KACH,4CAAC,oBAAQ,WAAR,EAAkB,WAAU,4BAA2B;AAAA,sBAE1D,4CAAC,oBAAQ,OAAR,EAAc,SAAS,MAAM,SAAS,WAAW,uBAC/C,gBAAM,MAAM,IAAI,CAAC,SAChB;AAAA,wBAAC;AAAA;AAAA,0BAEC;AAAA,0BACA;AAAA,0BACA,UAAU,MAAM,WAAW,IAAI;AAAA;AAAA,wBAH1B,KAAK;AAAA,sBAIZ,CACD,GACH;AAAA,yBAbmB,MAAM,WAAW,aAAa,CAAC,EAcpD;AAAA,kBAEF,CAAC;AAAA,mBACH;AAAA,gBAEC,eACC,4CAAC,SAAI,WAAU,mDACZ,uBACH;AAAA,gBAIF,6CAAC,SAAI,WAAU,2FACb;AAAA,+DAAC,SAAI,WAAU,2BACZ;AAAA;AAAA,oBACA,qBACC,6CAAC,UAAK,WAAU,4EACd;AAAA,mEAAC,UAAK,WAAU,2BAA0B;AAAA,oEAAC,OAAI,oBAAC;AAAA,wBAAM,4CAAC,OAAI,oBAAC;AAAA,wBAAM;AAAA,yBAAS;AAAA,sBAC3E,6CAAC,UAAK,WAAU,2BAA0B;AAAA,oEAAC,OAAI,oBAAC;AAAA,wBAAM;AAAA,yBAAK;AAAA,uBAC7D;AAAA,qBAEJ;AAAA,kBACC,aACC,6CAAC,UAAK,WAAU,yEAAwE;AAAA;AAAA,oBAEtF,4CAAC,OAAI,WAAU,yBAAwB,qBAAE;AAAA,qBAC3C;AAAA,mBAEJ;AAAA,iBACF;AAAA;AAAA,UACF;AAAA;AAAA,MACF;AAAA,OACF,GAEJ;AAAA,KACF;AAEJ;","names":[]}
@@ -43,11 +43,23 @@ interface NavCommandMenuProps {
43
43
  * framework-agnostic — same pattern as `accountSlot`.
44
44
  */
45
45
  footerSlot?: React.ReactNode;
46
+ /**
47
+ * Show the "↑↓ Navigate ↵ Open" keyboard-hint text next to `footerSlot`.
48
+ * Defaults to `true` (unchanged behavior).
49
+ */
50
+ showKeyboardHints?: boolean;
51
+ /**
52
+ * Optional override for the trailing "medialane ⌘K" brandmark at the
53
+ * footer's right edge — replaces it entirely rather than adorning it, so
54
+ * an app can put something more actionable there (e.g. a "Connect" button)
55
+ * instead of a static label. Omit to keep the default brandmark.
56
+ */
57
+ brandSlot?: React.ReactNode;
46
58
  }
47
59
  declare function useNavCommandMenu(): {
48
60
  open: () => boolean;
49
61
  close: () => boolean;
50
62
  };
51
- declare function NavCommandMenu({ commands, trigger, accountSlot, footerSlot }: NavCommandMenuProps): react_jsx_runtime.JSX.Element;
63
+ declare function NavCommandMenu({ commands, trigger, accountSlot, footerSlot, showKeyboardHints, brandSlot, }: NavCommandMenuProps): react_jsx_runtime.JSX.Element;
52
64
 
53
65
  export { type NavCommand, type NavCommandGroup, NavCommandMenu, type NavCommandMenuProps, useNavCommandMenu };
@@ -43,11 +43,23 @@ interface NavCommandMenuProps {
43
43
  * framework-agnostic — same pattern as `accountSlot`.
44
44
  */
45
45
  footerSlot?: React.ReactNode;
46
+ /**
47
+ * Show the "↑↓ Navigate ↵ Open" keyboard-hint text next to `footerSlot`.
48
+ * Defaults to `true` (unchanged behavior).
49
+ */
50
+ showKeyboardHints?: boolean;
51
+ /**
52
+ * Optional override for the trailing "medialane ⌘K" brandmark at the
53
+ * footer's right edge — replaces it entirely rather than adorning it, so
54
+ * an app can put something more actionable there (e.g. a "Connect" button)
55
+ * instead of a static label. Omit to keep the default brandmark.
56
+ */
57
+ brandSlot?: React.ReactNode;
46
58
  }
47
59
  declare function useNavCommandMenu(): {
48
60
  open: () => boolean;
49
61
  close: () => boolean;
50
62
  };
51
- declare function NavCommandMenu({ commands, trigger, accountSlot, footerSlot }: NavCommandMenuProps): react_jsx_runtime.JSX.Element;
63
+ declare function NavCommandMenu({ commands, trigger, accountSlot, footerSlot, showKeyboardHints, brandSlot, }: NavCommandMenuProps): react_jsx_runtime.JSX.Element;
52
64
 
53
65
  export { type NavCommand, type NavCommandGroup, NavCommandMenu, type NavCommandMenuProps, useNavCommandMenu };
@@ -86,7 +86,14 @@ const GROUP_HEADING_CLASSES = cn(
86
86
  "[&_[cmdk-group-heading]]:tracking-[0.08em]",
87
87
  "[&_[cmdk-group-heading]]:text-muted-foreground/60"
88
88
  );
89
- function NavCommandMenu({ commands, trigger, accountSlot, footerSlot }) {
89
+ function NavCommandMenu({
90
+ commands,
91
+ trigger,
92
+ accountSlot,
93
+ footerSlot,
94
+ showKeyboardHints = true,
95
+ brandSlot
96
+ }) {
90
97
  const [open, setOpen] = React.useState(false);
91
98
  const [query, setQuery] = React.useState("");
92
99
  const router = useRouter();
@@ -210,7 +217,7 @@ function NavCommandMenu({ commands, trigger, accountSlot, footerSlot }) {
210
217
  /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-3 border-t border-border/40 bg-muted/20 px-3 py-2", children: [
211
218
  /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
212
219
  footerSlot,
213
- /* @__PURE__ */ jsxs("span", { className: "hidden items-center gap-3 text-[10.5px] text-muted-foreground/70 sm:flex", children: [
220
+ showKeyboardHints && /* @__PURE__ */ jsxs("span", { className: "hidden items-center gap-3 text-[10.5px] text-muted-foreground/70 sm:flex", children: [
214
221
  /* @__PURE__ */ jsxs("span", { className: "flex items-center gap-1", children: [
215
222
  /* @__PURE__ */ jsx(Kbd, { children: "\u2191" }),
216
223
  /* @__PURE__ */ jsx(Kbd, { children: "\u2193" }),
@@ -222,7 +229,7 @@ function NavCommandMenu({ commands, trigger, accountSlot, footerSlot }) {
222
229
  ] })
223
230
  ] })
224
231
  ] }),
225
- /* @__PURE__ */ jsxs("span", { className: "flex shrink-0 items-center gap-2 text-[10px] text-muted-foreground/50", children: [
232
+ brandSlot ?? /* @__PURE__ */ jsxs("span", { className: "flex shrink-0 items-center gap-2 text-[10px] text-muted-foreground/50", children: [
226
233
  "medialane",
227
234
  /* @__PURE__ */ jsx(Kbd, { className: "hidden sm:inline-flex", children: "\u2318K" })
228
235
  ] })
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/components/nav-command-menu.tsx"],"sourcesContent":["\"use client\";\n\nimport * as React from \"react\";\nimport { Command } from \"cmdk\";\nimport { useRouter } from \"next/navigation\";\nimport { Search, X, ArrowRight } from \"lucide-react\";\nimport { AnimatePresence, motion } from \"framer-motion\";\nimport { cn } from \"../utils/cn.js\";\n\n// ── Types ─────────────────────────────────────────────────────────────────────\n\nexport interface NavCommand {\n id: string;\n label: string;\n icon: React.ComponentType<{ className?: string }>;\n href?: string;\n action?: () => void;\n /** Extra search terms beyond the label */\n keywords?: string[];\n /** Optional one-line description shown under the label */\n description?: string;\n}\n\nexport interface NavCommandGroup {\n /**\n * Optional group heading. Omit it for the primary group so its items\n * read as the top-level menu — they render first, emphasized, with no\n * label, separated from the headed groups below.\n */\n heading?: string;\n items: NavCommand[];\n}\n\nexport interface NavCommandMenuProps {\n commands: NavCommandGroup[];\n /**\n * Optional trigger element rendered inline at the component's mount point.\n * For most apps, omit this and call `useNavCommandMenu().open()` from a\n * separate button — that keeps the trigger in the right place in the layout.\n */\n trigger?: React.ReactNode;\n /**\n * Optional pinned account/connect area rendered below command results.\n * Apps own the auth implementation here (Clerk, ChipiPay, wallet connectors,\n * Privy, Cartridge, etc.) so the shared nav stays framework-agnostic.\n */\n accountSlot?: React.ReactNode;\n /**\n * Optional control rendered in the footer row (e.g. a theme toggle).\n * Apps own theme state (next-themes etc.) so the shared nav stays\n * framework-agnostic — same pattern as `accountSlot`.\n */\n footerSlot?: React.ReactNode;\n}\n\n// ── Singleton hook ─────────────────────────────────────────────────────────────\n\nconst ML_NAV_OPEN = \"ml:nav-open\";\nconst ML_NAV_CLOSE = \"ml:nav-close\";\n\nexport function useNavCommandMenu() {\n return {\n open: () => document.dispatchEvent(new CustomEvent(ML_NAV_OPEN)),\n close: () => document.dispatchEvent(new CustomEvent(ML_NAV_CLOSE)),\n };\n}\n\n// ── Small primitives ──────────────────────────────────────────────────────────\n\nfunction Kbd({ children, className }: { children: React.ReactNode; className?: string }) {\n return (\n <kbd\n className={cn(\n \"inline-flex min-w-[18px] items-center justify-center rounded-md bg-muted/60 px-1.5 py-0.5\",\n \"font-sans text-[10px] leading-none text-muted-foreground\",\n className\n )}\n >\n {children}\n </kbd>\n );\n}\n\nfunction CommandRow({ item, primary, onSelect }: { item: NavCommand; primary: boolean; onSelect: () => void }) {\n return (\n <Command.Item\n value={[item.label, ...(item.keywords ?? [])].join(\" \")}\n onSelect={onSelect}\n className={cn(\n \"group/item flex cursor-pointer items-center gap-3 rounded-xl\",\n \"transition-colors duration-150 aria-selected:bg-primary/10\",\n primary ? \"px-2.5 py-2.5\" : \"px-2.5 py-2\"\n )}\n >\n <span\n className={cn(\n \"flex shrink-0 items-center justify-center rounded-lg transition-colors\",\n \"bg-muted/50 group-aria-selected/item:bg-primary/15\",\n primary ? \"h-9 w-9\" : \"h-8 w-8\"\n )}\n >\n <item.icon\n className={cn(\n \"text-foreground/80 transition-colors group-aria-selected/item:text-primary\",\n primary ? \"h-[18px] w-[18px]\" : \"h-4 w-4\"\n )}\n />\n </span>\n <span className=\"min-w-0 flex-1\">\n <span\n className={cn(\n \"block truncate leading-tight\",\n primary ? \"text-[15px] font-medium\" : \"text-sm\"\n )}\n >\n {item.label}\n </span>\n {item.description && (\n <span className=\"mt-0.5 block truncate text-[11.5px] leading-tight text-muted-foreground\">\n {item.description}\n </span>\n )}\n </span>\n <ArrowRight className=\"h-3.5 w-3.5 shrink-0 text-muted-foreground/30 transition-colors group-aria-selected/item:text-primary/70\" />\n </Command.Item>\n );\n}\n\nconst GROUP_HEADING_CLASSES = cn(\n \"[&_[cmdk-group-heading]]:px-2.5\",\n \"[&_[cmdk-group-heading]]:pt-2\",\n \"[&_[cmdk-group-heading]]:pb-1\",\n \"[&_[cmdk-group-heading]]:text-[10.5px]\",\n \"[&_[cmdk-group-heading]]:font-semibold\",\n \"[&_[cmdk-group-heading]]:uppercase\",\n \"[&_[cmdk-group-heading]]:tracking-[0.08em]\",\n \"[&_[cmdk-group-heading]]:text-muted-foreground/60\"\n);\n\n// ── Component ─────────────────────────────────────────────────────────────────\n\nexport function NavCommandMenu({ commands, trigger, accountSlot, footerSlot }: NavCommandMenuProps) {\n const [open, setOpen] = React.useState(false);\n const [query, setQuery] = React.useState(\"\");\n const router = useRouter();\n const inputRef = React.useRef<HTMLInputElement>(null);\n\n React.useEffect(() => {\n if (!open) return;\n setQuery(\"\");\n const t = setTimeout(() => inputRef.current?.focus(), 60);\n return () => clearTimeout(t);\n }, [open]);\n\n React.useEffect(() => {\n const onKey = (e: KeyboardEvent) => {\n if (e.key === \"k\" && (e.metaKey || e.ctrlKey)) {\n e.preventDefault();\n setOpen((prev) => !prev);\n }\n if (e.key === \"Escape\") setOpen(false);\n };\n const onOpen = () => setOpen(true);\n const onClose = () => setOpen(false);\n\n document.addEventListener(\"keydown\", onKey);\n document.addEventListener(ML_NAV_OPEN, onOpen);\n document.addEventListener(ML_NAV_CLOSE, onClose);\n return () => {\n document.removeEventListener(\"keydown\", onKey);\n document.removeEventListener(ML_NAV_OPEN, onOpen);\n document.removeEventListener(ML_NAV_CLOSE, onClose);\n };\n }, []);\n\n const runCommand = React.useCallback(\n (cmd: NavCommand) => {\n setOpen(false);\n if (cmd.href) router.push(cmd.href);\n else cmd.action?.();\n },\n [router]\n );\n\n return (\n <>\n {trigger}\n\n <AnimatePresence>\n {open && (\n <>\n {/* Backdrop blur */}\n <motion.div\n className=\"nav-canvas-overlay\"\n initial={{ opacity: 0 }}\n animate={{ opacity: 1 }}\n exit={{ opacity: 0 }}\n transition={{ duration: 0.15 }}\n onClick={() => setOpen(false)}\n />\n\n {/* Command panel — centered on desktop, bottom sheet on mobile */}\n <motion.div\n className=\"fixed inset-0 z-[101] flex items-end justify-center p-3 pb-4 sm:items-center sm:p-4\"\n initial={{ opacity: 0, y: 24, scale: 1 }}\n animate={{ opacity: 1, y: 0, scale: 1 }}\n exit={{ opacity: 0, y: 24 }}\n transition={{ duration: 0.18, ease: \"easeOut\" }}\n onClick={() => setOpen(false)}\n >\n <div\n className={cn(\n \"flex w-full max-h-[85dvh] flex-col overflow-hidden rounded-[20px] sm:max-h-[min(680px,88dvh)] sm:max-w-[620px]\",\n \"border border-border/40 bg-background/90 shadow-2xl backdrop-blur-2xl backdrop-saturate-150\"\n )}\n onClick={(e) => e.stopPropagation()}\n >\n <Command shouldFilter label=\"Medialane navigation\" className=\"flex min-h-0 flex-1 flex-col\">\n {/* Drag handle (mobile) */}\n <div className=\"flex justify-center pt-2.5 sm:hidden\" aria-hidden=\"true\">\n <span className=\"h-1 w-9 rounded-full bg-muted-foreground/30\" />\n </div>\n\n {/* Search bar */}\n <div className=\"flex items-center gap-3 border-b border-border/40 px-4 py-3.5\">\n <Search\n className={cn(\n \"h-[18px] w-[18px] shrink-0 transition-colors\",\n query ? \"text-primary\" : \"text-muted-foreground\"\n )}\n />\n <Command.Input\n ref={inputRef}\n value={query}\n onValueChange={setQuery}\n placeholder=\"Search or run a command…\"\n className=\"flex-1 bg-transparent text-[15px] outline-none placeholder:text-muted-foreground\"\n />\n <Kbd className=\"hidden sm:inline-flex\">esc</Kbd>\n <button\n onClick={() => setOpen(false)}\n className=\"rounded-md p-1 transition-colors hover:bg-muted/50 sm:hidden\"\n aria-label=\"Close\"\n >\n <X className=\"h-4 w-4 text-muted-foreground\" />\n </button>\n </div>\n\n {/* Results */}\n <Command.List className=\"min-h-0 flex-1 overflow-y-auto overscroll-contain p-2\">\n <Command.Empty className=\"py-8 text-center text-sm text-muted-foreground\">\n No results found.\n </Command.Empty>\n\n {commands.map((group, i) => {\n const primary = !group.heading;\n return (\n <React.Fragment key={group.heading ?? `__primary-${i}`}>\n {i > 0 && (\n <Command.Separator className=\"my-1.5 h-px bg-border/40\" />\n )}\n <Command.Group heading={group.heading} className={GROUP_HEADING_CLASSES}>\n {group.items.map((item) => (\n <CommandRow\n key={item.id}\n item={item}\n primary={primary}\n onSelect={() => runCommand(item)}\n />\n ))}\n </Command.Group>\n </React.Fragment>\n );\n })}\n </Command.List>\n\n {accountSlot && (\n <div className=\"border-t border-border/40 bg-muted/20 px-3 py-3\">\n {accountSlot}\n </div>\n )}\n\n {/* Footer */}\n <div className=\"flex items-center justify-between gap-3 border-t border-border/40 bg-muted/20 px-3 py-2\">\n <div className=\"flex items-center gap-3\">\n {footerSlot}\n <span className=\"hidden items-center gap-3 text-[10.5px] text-muted-foreground/70 sm:flex\">\n <span className=\"flex items-center gap-1\"><Kbd>↑</Kbd><Kbd>↓</Kbd> Navigate</span>\n <span className=\"flex items-center gap-1\"><Kbd>↵</Kbd> Open</span>\n </span>\n </div>\n <span className=\"flex shrink-0 items-center gap-2 text-[10px] text-muted-foreground/50\">\n medialane\n <Kbd className=\"hidden sm:inline-flex\">⌘K</Kbd>\n </span>\n </div>\n </Command>\n </div>\n </motion.div>\n </>\n )}\n </AnimatePresence>\n </>\n );\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../src/components/nav-command-menu.tsx"],"sourcesContent":["\"use client\";\n\nimport * as React from \"react\";\nimport { Command } from \"cmdk\";\nimport { useRouter } from \"next/navigation\";\nimport { Search, X, ArrowRight } from \"lucide-react\";\nimport { AnimatePresence, motion } from \"framer-motion\";\nimport { cn } from \"../utils/cn.js\";\n\n// ── Types ─────────────────────────────────────────────────────────────────────\n\nexport interface NavCommand {\n id: string;\n label: string;\n icon: React.ComponentType<{ className?: string }>;\n href?: string;\n action?: () => void;\n /** Extra search terms beyond the label */\n keywords?: string[];\n /** Optional one-line description shown under the label */\n description?: string;\n}\n\nexport interface NavCommandGroup {\n /**\n * Optional group heading. Omit it for the primary group so its items\n * read as the top-level menu — they render first, emphasized, with no\n * label, separated from the headed groups below.\n */\n heading?: string;\n items: NavCommand[];\n}\n\nexport interface NavCommandMenuProps {\n commands: NavCommandGroup[];\n /**\n * Optional trigger element rendered inline at the component's mount point.\n * For most apps, omit this and call `useNavCommandMenu().open()` from a\n * separate button — that keeps the trigger in the right place in the layout.\n */\n trigger?: React.ReactNode;\n /**\n * Optional pinned account/connect area rendered below command results.\n * Apps own the auth implementation here (Clerk, ChipiPay, wallet connectors,\n * Privy, Cartridge, etc.) so the shared nav stays framework-agnostic.\n */\n accountSlot?: React.ReactNode;\n /**\n * Optional control rendered in the footer row (e.g. a theme toggle).\n * Apps own theme state (next-themes etc.) so the shared nav stays\n * framework-agnostic — same pattern as `accountSlot`.\n */\n footerSlot?: React.ReactNode;\n /**\n * Show the \"↑↓ Navigate ↵ Open\" keyboard-hint text next to `footerSlot`.\n * Defaults to `true` (unchanged behavior).\n */\n showKeyboardHints?: boolean;\n /**\n * Optional override for the trailing \"medialane ⌘K\" brandmark at the\n * footer's right edge — replaces it entirely rather than adorning it, so\n * an app can put something more actionable there (e.g. a \"Connect\" button)\n * instead of a static label. Omit to keep the default brandmark.\n */\n brandSlot?: React.ReactNode;\n}\n\n// ── Singleton hook ─────────────────────────────────────────────────────────────\n\nconst ML_NAV_OPEN = \"ml:nav-open\";\nconst ML_NAV_CLOSE = \"ml:nav-close\";\n\nexport function useNavCommandMenu() {\n return {\n open: () => document.dispatchEvent(new CustomEvent(ML_NAV_OPEN)),\n close: () => document.dispatchEvent(new CustomEvent(ML_NAV_CLOSE)),\n };\n}\n\n// ── Small primitives ──────────────────────────────────────────────────────────\n\nfunction Kbd({ children, className }: { children: React.ReactNode; className?: string }) {\n return (\n <kbd\n className={cn(\n \"inline-flex min-w-[18px] items-center justify-center rounded-md bg-muted/60 px-1.5 py-0.5\",\n \"font-sans text-[10px] leading-none text-muted-foreground\",\n className\n )}\n >\n {children}\n </kbd>\n );\n}\n\nfunction CommandRow({ item, primary, onSelect }: { item: NavCommand; primary: boolean; onSelect: () => void }) {\n return (\n <Command.Item\n value={[item.label, ...(item.keywords ?? [])].join(\" \")}\n onSelect={onSelect}\n className={cn(\n \"group/item flex cursor-pointer items-center gap-3 rounded-xl\",\n \"transition-colors duration-150 aria-selected:bg-primary/10\",\n primary ? \"px-2.5 py-2.5\" : \"px-2.5 py-2\"\n )}\n >\n <span\n className={cn(\n \"flex shrink-0 items-center justify-center rounded-lg transition-colors\",\n \"bg-muted/50 group-aria-selected/item:bg-primary/15\",\n primary ? \"h-9 w-9\" : \"h-8 w-8\"\n )}\n >\n <item.icon\n className={cn(\n \"text-foreground/80 transition-colors group-aria-selected/item:text-primary\",\n primary ? \"h-[18px] w-[18px]\" : \"h-4 w-4\"\n )}\n />\n </span>\n <span className=\"min-w-0 flex-1\">\n <span\n className={cn(\n \"block truncate leading-tight\",\n primary ? \"text-[15px] font-medium\" : \"text-sm\"\n )}\n >\n {item.label}\n </span>\n {item.description && (\n <span className=\"mt-0.5 block truncate text-[11.5px] leading-tight text-muted-foreground\">\n {item.description}\n </span>\n )}\n </span>\n <ArrowRight className=\"h-3.5 w-3.5 shrink-0 text-muted-foreground/30 transition-colors group-aria-selected/item:text-primary/70\" />\n </Command.Item>\n );\n}\n\nconst GROUP_HEADING_CLASSES = cn(\n \"[&_[cmdk-group-heading]]:px-2.5\",\n \"[&_[cmdk-group-heading]]:pt-2\",\n \"[&_[cmdk-group-heading]]:pb-1\",\n \"[&_[cmdk-group-heading]]:text-[10.5px]\",\n \"[&_[cmdk-group-heading]]:font-semibold\",\n \"[&_[cmdk-group-heading]]:uppercase\",\n \"[&_[cmdk-group-heading]]:tracking-[0.08em]\",\n \"[&_[cmdk-group-heading]]:text-muted-foreground/60\"\n);\n\n// ── Component ─────────────────────────────────────────────────────────────────\n\nexport function NavCommandMenu({\n commands,\n trigger,\n accountSlot,\n footerSlot,\n showKeyboardHints = true,\n brandSlot,\n}: NavCommandMenuProps) {\n const [open, setOpen] = React.useState(false);\n const [query, setQuery] = React.useState(\"\");\n const router = useRouter();\n const inputRef = React.useRef<HTMLInputElement>(null);\n\n React.useEffect(() => {\n if (!open) return;\n setQuery(\"\");\n const t = setTimeout(() => inputRef.current?.focus(), 60);\n return () => clearTimeout(t);\n }, [open]);\n\n React.useEffect(() => {\n const onKey = (e: KeyboardEvent) => {\n if (e.key === \"k\" && (e.metaKey || e.ctrlKey)) {\n e.preventDefault();\n setOpen((prev) => !prev);\n }\n if (e.key === \"Escape\") setOpen(false);\n };\n const onOpen = () => setOpen(true);\n const onClose = () => setOpen(false);\n\n document.addEventListener(\"keydown\", onKey);\n document.addEventListener(ML_NAV_OPEN, onOpen);\n document.addEventListener(ML_NAV_CLOSE, onClose);\n return () => {\n document.removeEventListener(\"keydown\", onKey);\n document.removeEventListener(ML_NAV_OPEN, onOpen);\n document.removeEventListener(ML_NAV_CLOSE, onClose);\n };\n }, []);\n\n const runCommand = React.useCallback(\n (cmd: NavCommand) => {\n setOpen(false);\n if (cmd.href) router.push(cmd.href);\n else cmd.action?.();\n },\n [router]\n );\n\n return (\n <>\n {trigger}\n\n <AnimatePresence>\n {open && (\n <>\n {/* Backdrop blur */}\n <motion.div\n className=\"nav-canvas-overlay\"\n initial={{ opacity: 0 }}\n animate={{ opacity: 1 }}\n exit={{ opacity: 0 }}\n transition={{ duration: 0.15 }}\n onClick={() => setOpen(false)}\n />\n\n {/* Command panel — centered on desktop, bottom sheet on mobile */}\n <motion.div\n className=\"fixed inset-0 z-[101] flex items-end justify-center p-3 pb-4 sm:items-center sm:p-4\"\n initial={{ opacity: 0, y: 24, scale: 1 }}\n animate={{ opacity: 1, y: 0, scale: 1 }}\n exit={{ opacity: 0, y: 24 }}\n transition={{ duration: 0.18, ease: \"easeOut\" }}\n onClick={() => setOpen(false)}\n >\n <div\n className={cn(\n \"flex w-full max-h-[85dvh] flex-col overflow-hidden rounded-[20px] sm:max-h-[min(680px,88dvh)] sm:max-w-[620px]\",\n \"border border-border/40 bg-background/90 shadow-2xl backdrop-blur-2xl backdrop-saturate-150\"\n )}\n onClick={(e) => e.stopPropagation()}\n >\n <Command shouldFilter label=\"Medialane navigation\" className=\"flex min-h-0 flex-1 flex-col\">\n {/* Drag handle (mobile) */}\n <div className=\"flex justify-center pt-2.5 sm:hidden\" aria-hidden=\"true\">\n <span className=\"h-1 w-9 rounded-full bg-muted-foreground/30\" />\n </div>\n\n {/* Search bar */}\n <div className=\"flex items-center gap-3 border-b border-border/40 px-4 py-3.5\">\n <Search\n className={cn(\n \"h-[18px] w-[18px] shrink-0 transition-colors\",\n query ? \"text-primary\" : \"text-muted-foreground\"\n )}\n />\n <Command.Input\n ref={inputRef}\n value={query}\n onValueChange={setQuery}\n placeholder=\"Search or run a command…\"\n className=\"flex-1 bg-transparent text-[15px] outline-none placeholder:text-muted-foreground\"\n />\n <Kbd className=\"hidden sm:inline-flex\">esc</Kbd>\n <button\n onClick={() => setOpen(false)}\n className=\"rounded-md p-1 transition-colors hover:bg-muted/50 sm:hidden\"\n aria-label=\"Close\"\n >\n <X className=\"h-4 w-4 text-muted-foreground\" />\n </button>\n </div>\n\n {/* Results */}\n <Command.List className=\"min-h-0 flex-1 overflow-y-auto overscroll-contain p-2\">\n <Command.Empty className=\"py-8 text-center text-sm text-muted-foreground\">\n No results found.\n </Command.Empty>\n\n {commands.map((group, i) => {\n const primary = !group.heading;\n return (\n <React.Fragment key={group.heading ?? `__primary-${i}`}>\n {i > 0 && (\n <Command.Separator className=\"my-1.5 h-px bg-border/40\" />\n )}\n <Command.Group heading={group.heading} className={GROUP_HEADING_CLASSES}>\n {group.items.map((item) => (\n <CommandRow\n key={item.id}\n item={item}\n primary={primary}\n onSelect={() => runCommand(item)}\n />\n ))}\n </Command.Group>\n </React.Fragment>\n );\n })}\n </Command.List>\n\n {accountSlot && (\n <div className=\"border-t border-border/40 bg-muted/20 px-3 py-3\">\n {accountSlot}\n </div>\n )}\n\n {/* Footer */}\n <div className=\"flex items-center justify-between gap-3 border-t border-border/40 bg-muted/20 px-3 py-2\">\n <div className=\"flex items-center gap-3\">\n {footerSlot}\n {showKeyboardHints && (\n <span className=\"hidden items-center gap-3 text-[10.5px] text-muted-foreground/70 sm:flex\">\n <span className=\"flex items-center gap-1\"><Kbd>↑</Kbd><Kbd>↓</Kbd> Navigate</span>\n <span className=\"flex items-center gap-1\"><Kbd>↵</Kbd> Open</span>\n </span>\n )}\n </div>\n {brandSlot ?? (\n <span className=\"flex shrink-0 items-center gap-2 text-[10px] text-muted-foreground/50\">\n medialane\n <Kbd className=\"hidden sm:inline-flex\">⌘K</Kbd>\n </span>\n )}\n </div>\n </Command>\n </div>\n </motion.div>\n </>\n )}\n </AnimatePresence>\n </>\n );\n}\n"],"mappings":"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","names":[]}