@medialane/ui 0.12.0 → 0.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. package/dist/components/asset-markets-tab.cjs +111 -0
  2. package/dist/components/asset-markets-tab.cjs.map +1 -0
  3. package/dist/components/asset-markets-tab.d.cts +16 -0
  4. package/dist/components/asset-markets-tab.d.ts +16 -0
  5. package/dist/components/asset-markets-tab.js +87 -0
  6. package/dist/components/asset-markets-tab.js.map +1 -0
  7. package/dist/components/asset-overview-content.cjs +102 -0
  8. package/dist/components/asset-overview-content.cjs.map +1 -0
  9. package/dist/components/asset-overview-content.d.cts +14 -0
  10. package/dist/components/asset-overview-content.d.ts +14 -0
  11. package/dist/components/asset-overview-content.js +78 -0
  12. package/dist/components/asset-overview-content.js.map +1 -0
  13. package/dist/components/ip-type-display.cjs +201 -0
  14. package/dist/components/ip-type-display.cjs.map +1 -0
  15. package/dist/components/ip-type-display.d.cts +12 -0
  16. package/dist/components/ip-type-display.d.ts +12 -0
  17. package/dist/components/ip-type-display.js +181 -0
  18. package/dist/components/ip-type-display.js.map +1 -0
  19. package/dist/components/launchpad-strip.cjs +150 -0
  20. package/dist/components/launchpad-strip.cjs.map +1 -0
  21. package/dist/components/launchpad-strip.d.cts +16 -0
  22. package/dist/components/launchpad-strip.d.ts +16 -0
  23. package/dist/components/launchpad-strip.js +116 -0
  24. package/dist/components/launchpad-strip.js.map +1 -0
  25. package/dist/data/ip-templates.cjs +206 -0
  26. package/dist/data/ip-templates.cjs.map +1 -0
  27. package/dist/data/ip-templates.d.cts +45 -0
  28. package/dist/data/ip-templates.d.ts +45 -0
  29. package/dist/data/ip-templates.js +200 -0
  30. package/dist/data/ip-templates.js.map +1 -0
  31. package/dist/data/ip.cjs +163 -0
  32. package/dist/data/ip.cjs.map +1 -0
  33. package/dist/data/ip.d.cts +18 -0
  34. package/dist/data/ip.d.ts +18 -0
  35. package/dist/data/ip.js +134 -0
  36. package/dist/data/ip.js.map +1 -0
  37. package/dist/index.cjs +36 -0
  38. package/dist/index.cjs.map +1 -1
  39. package/dist/index.d.cts +9 -3
  40. package/dist/index.d.ts +9 -3
  41. package/dist/index.js +34 -1
  42. package/dist/index.js.map +1 -1
  43. package/dist/utils/time.cjs +15 -2
  44. package/dist/utils/time.cjs.map +1 -1
  45. package/dist/utils/time.d.cts +3 -1
  46. package/dist/utils/time.d.ts +3 -1
  47. package/dist/utils/time.js +13 -1
  48. package/dist/utils/time.js.map +1 -1
  49. package/package.json +1 -1
@@ -0,0 +1,201 @@
1
+ "use strict";
2
+ "use client";
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
7
+ var __export = (target, all) => {
8
+ for (var name in all)
9
+ __defProp(target, name, { get: all[name], enumerable: true });
10
+ };
11
+ var __copyProps = (to, from, except, desc) => {
12
+ if (from && typeof from === "object" || typeof from === "function") {
13
+ for (let key of __getOwnPropNames(from))
14
+ if (!__hasOwnProp.call(to, key) && key !== except)
15
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
16
+ }
17
+ return to;
18
+ };
19
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
20
+ var ip_type_display_exports = {};
21
+ __export(ip_type_display_exports, {
22
+ IPTypeDisplay: () => IPTypeDisplay
23
+ });
24
+ module.exports = __toCommonJS(ip_type_display_exports);
25
+ var import_jsx_runtime = require("react/jsx-runtime");
26
+ var import_ip_templates = require("../data/ip-templates.js");
27
+ var import_lucide_react = require("lucide-react");
28
+ function parseYouTubeEmbed(url) {
29
+ try {
30
+ const u = new URL(url);
31
+ let id = null;
32
+ if (u.hostname.includes("youtu.be")) {
33
+ id = u.pathname.slice(1);
34
+ } else if (u.hostname.includes("youtube.com")) {
35
+ id = u.searchParams.get("v");
36
+ }
37
+ if (!id) return null;
38
+ return `https://www.youtube.com/embed/${id}`;
39
+ } catch {
40
+ return null;
41
+ }
42
+ }
43
+ function parseSpotifyEmbed(url) {
44
+ try {
45
+ const u = new URL(url);
46
+ if (!u.hostname.includes("spotify.com")) return null;
47
+ const match = u.pathname.match(
48
+ /(track|album|playlist|episode|show|artist)\/([A-Za-z0-9]+)/
49
+ );
50
+ if (!match) return null;
51
+ return `https://open.spotify.com/embed/${match[1]}/${match[2]}`;
52
+ } catch {
53
+ return null;
54
+ }
55
+ }
56
+ function parseSoundCloudEmbed(url) {
57
+ try {
58
+ new URL(url);
59
+ if (!url.includes("soundcloud.com")) return null;
60
+ const encoded = encodeURIComponent(url);
61
+ return `https://w.soundcloud.com/player/?url=${encoded}&color=%23ff5500&auto_play=false&hide_related=true&show_comments=false&show_user=true&show_reposts=false`;
62
+ } catch {
63
+ return null;
64
+ }
65
+ }
66
+ function parseTikTokEmbed(url) {
67
+ try {
68
+ const u = new URL(url);
69
+ if (!u.hostname.includes("tiktok.com")) return null;
70
+ const match = u.pathname.match(/\/video\/(\d+)/);
71
+ if (!match) return null;
72
+ return `https://www.tiktok.com/embed/v2/${match[1]}`;
73
+ } catch {
74
+ return null;
75
+ }
76
+ }
77
+ function parseVimeoEmbed(url) {
78
+ try {
79
+ const u = new URL(url);
80
+ if (!u.hostname.includes("vimeo.com")) return null;
81
+ const match = u.pathname.match(/(\d+)/);
82
+ if (!match) return null;
83
+ return `https://player.vimeo.com/video/${match[1]}`;
84
+ } catch {
85
+ return null;
86
+ }
87
+ }
88
+ function getEmbedSrc(platform, value) {
89
+ switch (platform) {
90
+ case "youtube":
91
+ return parseYouTubeEmbed(value);
92
+ case "spotify":
93
+ return parseSpotifyEmbed(value);
94
+ case "soundcloud":
95
+ return parseSoundCloudEmbed(value);
96
+ case "tiktok":
97
+ return parseTikTokEmbed(value);
98
+ case "vimeo":
99
+ return parseVimeoEmbed(value);
100
+ }
101
+ }
102
+ const COMPACT = {
103
+ spotify: true,
104
+ soundcloud: true,
105
+ youtube: false,
106
+ tiktok: false,
107
+ vimeo: false
108
+ };
109
+ function IPTypeDisplay({ attributes }) {
110
+ const attrs = attributes ?? [];
111
+ const ipType = attrs.find(
112
+ (a) => a.trait_type?.toLowerCase() === "ip type"
113
+ )?.value;
114
+ if (!ipType) return null;
115
+ const template = import_ip_templates.IP_TEMPLATES[ipType];
116
+ if (!template) return null;
117
+ const getAttr = (key) => attrs.find((a) => a.trait_type === key)?.value ?? null;
118
+ const embeds = (template.embeds ?? []).flatMap((platform) => {
119
+ const meta = import_ip_templates.EMBED_PLATFORM_META[platform];
120
+ const value = getAttr(meta.traitKey);
121
+ return value ? [{ platform, meta, value }] : [];
122
+ });
123
+ const socials = (template.socials ?? []).flatMap((platform) => {
124
+ const meta = import_ip_templates.SOCIAL_PLATFORM_META[platform];
125
+ const value = getAttr(meta.traitKey);
126
+ return value ? [{ platform, meta, value }] : [];
127
+ });
128
+ if (embeds.length === 0 && socials.length === 0) return null;
129
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "space-y-5", children: [
130
+ embeds.map(({ platform, meta, value }) => {
131
+ const src = getEmbedSrc(platform, value);
132
+ if (src) {
133
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "space-y-1.5", children: [
134
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "text-xs font-semibold uppercase tracking-wider text-muted-foreground", children: meta.label }),
135
+ COMPACT[platform] ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
136
+ "iframe",
137
+ {
138
+ src,
139
+ className: "w-full rounded-xl border-0",
140
+ height: 166,
141
+ allow: "autoplay",
142
+ loading: "lazy",
143
+ title: meta.label
144
+ }
145
+ ) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "relative w-full aspect-video rounded-xl overflow-hidden bg-muted/20", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
146
+ "iframe",
147
+ {
148
+ src,
149
+ className: "absolute inset-0 w-full h-full",
150
+ allow: "accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture",
151
+ allowFullScreen: true,
152
+ loading: "lazy",
153
+ title: meta.label
154
+ }
155
+ ) })
156
+ ] }, platform);
157
+ }
158
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
159
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "text-xs font-semibold uppercase tracking-wider text-muted-foreground mb-1", children: meta.label }),
160
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
161
+ "a",
162
+ {
163
+ href: value,
164
+ target: "_blank",
165
+ rel: "noopener noreferrer",
166
+ className: "inline-flex items-center gap-1.5 text-sm text-primary hover:underline",
167
+ children: [
168
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_lucide_react.ExternalLink, { className: "h-3.5 w-3.5" }),
169
+ "Open link"
170
+ ]
171
+ }
172
+ )
173
+ ] }, platform);
174
+ }),
175
+ socials.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "space-y-1.5", children: [
176
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "text-xs font-semibold uppercase tracking-wider text-muted-foreground", children: "Links" }),
177
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "flex flex-wrap gap-2", children: socials.map(({ platform, meta, value }) => {
178
+ const SIcon = meta.icon;
179
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
180
+ "a",
181
+ {
182
+ href: value,
183
+ target: "_blank",
184
+ rel: "noopener noreferrer",
185
+ className: "inline-flex items-center gap-1.5 rounded-full border border-border bg-muted/30 px-3 py-1.5 text-xs font-medium text-muted-foreground transition-colors hover:border-primary/40 hover:text-foreground",
186
+ children: [
187
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SIcon, { className: "h-3.5 w-3.5" }),
188
+ meta.label
189
+ ]
190
+ },
191
+ platform
192
+ );
193
+ }) })
194
+ ] })
195
+ ] });
196
+ }
197
+ // Annotate the CommonJS export names for ESM import in node:
198
+ 0 && (module.exports = {
199
+ IPTypeDisplay
200
+ });
201
+ //# sourceMappingURL=ip-type-display.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/components/ip-type-display.tsx"],"sourcesContent":["\"use client\";\n\nimport type { IPType } from \"../data/ip.js\";\nimport {\n IP_TEMPLATES,\n EMBED_PLATFORM_META,\n SOCIAL_PLATFORM_META,\n type EmbedPlatform,\n} from \"../data/ip-templates.js\";\nimport { ExternalLink } from \"lucide-react\";\n\ninterface Attr {\n trait_type?: string | null;\n value?: string | null;\n}\n\ninterface IPTypeDisplayProps {\n attributes: Attr[] | null | undefined;\n}\n\n// ── Embed URL parsers ────────────────────────────────────────────────────────\n\nfunction parseYouTubeEmbed(url: string): string | null {\n try {\n const u = new URL(url);\n let id: string | null = null;\n if (u.hostname.includes(\"youtu.be\")) {\n id = u.pathname.slice(1);\n } else if (u.hostname.includes(\"youtube.com\")) {\n id = u.searchParams.get(\"v\");\n }\n if (!id) return null;\n return `https://www.youtube.com/embed/${id}`;\n } catch {\n return null;\n }\n}\n\nfunction parseSpotifyEmbed(url: string): string | null {\n try {\n const u = new URL(url);\n if (!u.hostname.includes(\"spotify.com\")) return null;\n // Spotify's embed endpoint only accepts /embed/{type}/{id}. Isolate the\n // resource type + id so locale prefixes (e.g. /intl-pt) and trailing query\n // params don't leak through — `/embed/intl-pt/album/…` 404s on Spotify.\n const match = u.pathname.match(\n /(track|album|playlist|episode|show|artist)\\/([A-Za-z0-9]+)/\n );\n if (!match) return null;\n return `https://open.spotify.com/embed/${match[1]}/${match[2]}`;\n } catch {\n return null;\n }\n}\n\nfunction parseSoundCloudEmbed(url: string): string | null {\n try {\n new URL(url); // validate\n if (!url.includes(\"soundcloud.com\")) return null;\n const encoded = encodeURIComponent(url);\n return `https://w.soundcloud.com/player/?url=${encoded}&color=%23ff5500&auto_play=false&hide_related=true&show_comments=false&show_user=true&show_reposts=false`;\n } catch {\n return null;\n }\n}\n\nfunction parseTikTokEmbed(url: string): string | null {\n try {\n const u = new URL(url);\n if (!u.hostname.includes(\"tiktok.com\")) return null;\n const match = u.pathname.match(/\\/video\\/(\\d+)/);\n if (!match) return null;\n return `https://www.tiktok.com/embed/v2/${match[1]}`;\n } catch {\n return null;\n }\n}\n\nfunction parseVimeoEmbed(url: string): string | null {\n try {\n const u = new URL(url);\n if (!u.hostname.includes(\"vimeo.com\")) return null;\n const match = u.pathname.match(/(\\d+)/);\n if (!match) return null;\n return `https://player.vimeo.com/video/${match[1]}`;\n } catch {\n return null;\n }\n}\n\nfunction getEmbedSrc(platform: EmbedPlatform, value: string): string | null {\n switch (platform) {\n case \"youtube\": return parseYouTubeEmbed(value);\n case \"spotify\": return parseSpotifyEmbed(value);\n case \"soundcloud\": return parseSoundCloudEmbed(value);\n case \"tiktok\": return parseTikTokEmbed(value);\n case \"vimeo\": return parseVimeoEmbed(value);\n }\n}\n\n// Compact iframe (fixed height) vs 16:9 video frame.\nconst COMPACT: Record<EmbedPlatform, boolean> = {\n spotify: true,\n soundcloud: true,\n youtube: false,\n tiktok: false,\n vimeo: false,\n};\n\n// ── Component ─────────────────────────────────────────────────────────────────\n\nexport function IPTypeDisplay({ attributes }: IPTypeDisplayProps) {\n const attrs = attributes ?? [];\n\n const ipType = attrs.find(\n (a) => a.trait_type?.toLowerCase() === \"ip type\"\n )?.value as IPType | undefined;\n if (!ipType) return null;\n\n const template = IP_TEMPLATES[ipType];\n if (!template) return null;\n\n const getAttr = (key: string) =>\n attrs.find((a) => a.trait_type === key)?.value ?? null;\n\n const embeds = (template.embeds ?? []).flatMap((platform) => {\n const meta = EMBED_PLATFORM_META[platform];\n const value = getAttr(meta.traitKey);\n return value ? [{ platform, meta, value }] : [];\n });\n\n const socials = (template.socials ?? []).flatMap((platform) => {\n const meta = SOCIAL_PLATFORM_META[platform];\n const value = getAttr(meta.traitKey);\n return value ? [{ platform, meta, value }] : [];\n });\n\n if (embeds.length === 0 && socials.length === 0) return null;\n\n return (\n <div className=\"space-y-5\">\n {embeds.map(({ platform, meta, value }) => {\n const src = getEmbedSrc(platform, value);\n if (src) {\n return (\n <div key={platform} className=\"space-y-1.5\">\n <p className=\"text-xs font-semibold uppercase tracking-wider text-muted-foreground\">\n {meta.label}\n </p>\n {COMPACT[platform] ? (\n <iframe\n src={src}\n className=\"w-full rounded-xl border-0\"\n height={166}\n allow=\"autoplay\"\n loading=\"lazy\"\n title={meta.label}\n />\n ) : (\n <div className=\"relative w-full aspect-video rounded-xl overflow-hidden bg-muted/20\">\n <iframe\n src={src}\n className=\"absolute inset-0 w-full h-full\"\n allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture\"\n allowFullScreen\n loading=\"lazy\"\n title={meta.label}\n />\n </div>\n )}\n </div>\n );\n }\n // Fallback: plain external link if URL parsing failed\n return (\n <div key={platform}>\n <p className=\"text-xs font-semibold uppercase tracking-wider text-muted-foreground mb-1\">\n {meta.label}\n </p>\n <a\n href={value}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n className=\"inline-flex items-center gap-1.5 text-sm text-primary hover:underline\"\n >\n <ExternalLink className=\"h-3.5 w-3.5\" />\n Open link\n </a>\n </div>\n );\n })}\n\n {socials.length > 0 && (\n <div className=\"space-y-1.5\">\n <p className=\"text-xs font-semibold uppercase tracking-wider text-muted-foreground\">\n Links\n </p>\n <div className=\"flex flex-wrap gap-2\">\n {socials.map(({ platform, meta, value }) => {\n const SIcon = meta.icon;\n return (\n <a\n key={platform}\n href={value}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n className=\"inline-flex items-center gap-1.5 rounded-full border border-border bg-muted/30 px-3 py-1.5 text-xs font-medium text-muted-foreground transition-colors hover:border-primary/40 hover:text-foreground\"\n >\n <SIcon className=\"h-3.5 w-3.5\" />\n {meta.label}\n </a>\n );\n })}\n </div>\n </div>\n )}\n </div>\n );\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,12 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+
3
+ interface Attr {
4
+ trait_type?: string | null;
5
+ value?: string | null;
6
+ }
7
+ interface IPTypeDisplayProps {
8
+ attributes: Attr[] | null | undefined;
9
+ }
10
+ declare function IPTypeDisplay({ attributes }: IPTypeDisplayProps): react_jsx_runtime.JSX.Element | null;
11
+
12
+ export { IPTypeDisplay };
@@ -0,0 +1,12 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+
3
+ interface Attr {
4
+ trait_type?: string | null;
5
+ value?: string | null;
6
+ }
7
+ interface IPTypeDisplayProps {
8
+ attributes: Attr[] | null | undefined;
9
+ }
10
+ declare function IPTypeDisplay({ attributes }: IPTypeDisplayProps): react_jsx_runtime.JSX.Element | null;
11
+
12
+ export { IPTypeDisplay };
@@ -0,0 +1,181 @@
1
+ "use client";
2
+ import { jsx, jsxs } from "react/jsx-runtime";
3
+ import {
4
+ IP_TEMPLATES,
5
+ EMBED_PLATFORM_META,
6
+ SOCIAL_PLATFORM_META
7
+ } from "../data/ip-templates.js";
8
+ import { ExternalLink } from "lucide-react";
9
+ function parseYouTubeEmbed(url) {
10
+ try {
11
+ const u = new URL(url);
12
+ let id = null;
13
+ if (u.hostname.includes("youtu.be")) {
14
+ id = u.pathname.slice(1);
15
+ } else if (u.hostname.includes("youtube.com")) {
16
+ id = u.searchParams.get("v");
17
+ }
18
+ if (!id) return null;
19
+ return `https://www.youtube.com/embed/${id}`;
20
+ } catch {
21
+ return null;
22
+ }
23
+ }
24
+ function parseSpotifyEmbed(url) {
25
+ try {
26
+ const u = new URL(url);
27
+ if (!u.hostname.includes("spotify.com")) return null;
28
+ const match = u.pathname.match(
29
+ /(track|album|playlist|episode|show|artist)\/([A-Za-z0-9]+)/
30
+ );
31
+ if (!match) return null;
32
+ return `https://open.spotify.com/embed/${match[1]}/${match[2]}`;
33
+ } catch {
34
+ return null;
35
+ }
36
+ }
37
+ function parseSoundCloudEmbed(url) {
38
+ try {
39
+ new URL(url);
40
+ if (!url.includes("soundcloud.com")) return null;
41
+ const encoded = encodeURIComponent(url);
42
+ return `https://w.soundcloud.com/player/?url=${encoded}&color=%23ff5500&auto_play=false&hide_related=true&show_comments=false&show_user=true&show_reposts=false`;
43
+ } catch {
44
+ return null;
45
+ }
46
+ }
47
+ function parseTikTokEmbed(url) {
48
+ try {
49
+ const u = new URL(url);
50
+ if (!u.hostname.includes("tiktok.com")) return null;
51
+ const match = u.pathname.match(/\/video\/(\d+)/);
52
+ if (!match) return null;
53
+ return `https://www.tiktok.com/embed/v2/${match[1]}`;
54
+ } catch {
55
+ return null;
56
+ }
57
+ }
58
+ function parseVimeoEmbed(url) {
59
+ try {
60
+ const u = new URL(url);
61
+ if (!u.hostname.includes("vimeo.com")) return null;
62
+ const match = u.pathname.match(/(\d+)/);
63
+ if (!match) return null;
64
+ return `https://player.vimeo.com/video/${match[1]}`;
65
+ } catch {
66
+ return null;
67
+ }
68
+ }
69
+ function getEmbedSrc(platform, value) {
70
+ switch (platform) {
71
+ case "youtube":
72
+ return parseYouTubeEmbed(value);
73
+ case "spotify":
74
+ return parseSpotifyEmbed(value);
75
+ case "soundcloud":
76
+ return parseSoundCloudEmbed(value);
77
+ case "tiktok":
78
+ return parseTikTokEmbed(value);
79
+ case "vimeo":
80
+ return parseVimeoEmbed(value);
81
+ }
82
+ }
83
+ const COMPACT = {
84
+ spotify: true,
85
+ soundcloud: true,
86
+ youtube: false,
87
+ tiktok: false,
88
+ vimeo: false
89
+ };
90
+ function IPTypeDisplay({ attributes }) {
91
+ const attrs = attributes ?? [];
92
+ const ipType = attrs.find(
93
+ (a) => a.trait_type?.toLowerCase() === "ip type"
94
+ )?.value;
95
+ if (!ipType) return null;
96
+ const template = IP_TEMPLATES[ipType];
97
+ if (!template) return null;
98
+ const getAttr = (key) => attrs.find((a) => a.trait_type === key)?.value ?? null;
99
+ const embeds = (template.embeds ?? []).flatMap((platform) => {
100
+ const meta = EMBED_PLATFORM_META[platform];
101
+ const value = getAttr(meta.traitKey);
102
+ return value ? [{ platform, meta, value }] : [];
103
+ });
104
+ const socials = (template.socials ?? []).flatMap((platform) => {
105
+ const meta = SOCIAL_PLATFORM_META[platform];
106
+ const value = getAttr(meta.traitKey);
107
+ return value ? [{ platform, meta, value }] : [];
108
+ });
109
+ if (embeds.length === 0 && socials.length === 0) return null;
110
+ return /* @__PURE__ */ jsxs("div", { className: "space-y-5", children: [
111
+ embeds.map(({ platform, meta, value }) => {
112
+ const src = getEmbedSrc(platform, value);
113
+ if (src) {
114
+ return /* @__PURE__ */ jsxs("div", { className: "space-y-1.5", children: [
115
+ /* @__PURE__ */ jsx("p", { className: "text-xs font-semibold uppercase tracking-wider text-muted-foreground", children: meta.label }),
116
+ COMPACT[platform] ? /* @__PURE__ */ jsx(
117
+ "iframe",
118
+ {
119
+ src,
120
+ className: "w-full rounded-xl border-0",
121
+ height: 166,
122
+ allow: "autoplay",
123
+ loading: "lazy",
124
+ title: meta.label
125
+ }
126
+ ) : /* @__PURE__ */ jsx("div", { className: "relative w-full aspect-video rounded-xl overflow-hidden bg-muted/20", children: /* @__PURE__ */ jsx(
127
+ "iframe",
128
+ {
129
+ src,
130
+ className: "absolute inset-0 w-full h-full",
131
+ allow: "accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture",
132
+ allowFullScreen: true,
133
+ loading: "lazy",
134
+ title: meta.label
135
+ }
136
+ ) })
137
+ ] }, platform);
138
+ }
139
+ return /* @__PURE__ */ jsxs("div", { children: [
140
+ /* @__PURE__ */ jsx("p", { className: "text-xs font-semibold uppercase tracking-wider text-muted-foreground mb-1", children: meta.label }),
141
+ /* @__PURE__ */ jsxs(
142
+ "a",
143
+ {
144
+ href: value,
145
+ target: "_blank",
146
+ rel: "noopener noreferrer",
147
+ className: "inline-flex items-center gap-1.5 text-sm text-primary hover:underline",
148
+ children: [
149
+ /* @__PURE__ */ jsx(ExternalLink, { className: "h-3.5 w-3.5" }),
150
+ "Open link"
151
+ ]
152
+ }
153
+ )
154
+ ] }, platform);
155
+ }),
156
+ socials.length > 0 && /* @__PURE__ */ jsxs("div", { className: "space-y-1.5", children: [
157
+ /* @__PURE__ */ jsx("p", { className: "text-xs font-semibold uppercase tracking-wider text-muted-foreground", children: "Links" }),
158
+ /* @__PURE__ */ jsx("div", { className: "flex flex-wrap gap-2", children: socials.map(({ platform, meta, value }) => {
159
+ const SIcon = meta.icon;
160
+ return /* @__PURE__ */ jsxs(
161
+ "a",
162
+ {
163
+ href: value,
164
+ target: "_blank",
165
+ rel: "noopener noreferrer",
166
+ className: "inline-flex items-center gap-1.5 rounded-full border border-border bg-muted/30 px-3 py-1.5 text-xs font-medium text-muted-foreground transition-colors hover:border-primary/40 hover:text-foreground",
167
+ children: [
168
+ /* @__PURE__ */ jsx(SIcon, { className: "h-3.5 w-3.5" }),
169
+ meta.label
170
+ ]
171
+ },
172
+ platform
173
+ );
174
+ }) })
175
+ ] })
176
+ ] });
177
+ }
178
+ export {
179
+ IPTypeDisplay
180
+ };
181
+ //# sourceMappingURL=ip-type-display.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/components/ip-type-display.tsx"],"sourcesContent":["\"use client\";\n\nimport type { IPType } from \"../data/ip.js\";\nimport {\n IP_TEMPLATES,\n EMBED_PLATFORM_META,\n SOCIAL_PLATFORM_META,\n type EmbedPlatform,\n} from \"../data/ip-templates.js\";\nimport { ExternalLink } from \"lucide-react\";\n\ninterface Attr {\n trait_type?: string | null;\n value?: string | null;\n}\n\ninterface IPTypeDisplayProps {\n attributes: Attr[] | null | undefined;\n}\n\n// ── Embed URL parsers ────────────────────────────────────────────────────────\n\nfunction parseYouTubeEmbed(url: string): string | null {\n try {\n const u = new URL(url);\n let id: string | null = null;\n if (u.hostname.includes(\"youtu.be\")) {\n id = u.pathname.slice(1);\n } else if (u.hostname.includes(\"youtube.com\")) {\n id = u.searchParams.get(\"v\");\n }\n if (!id) return null;\n return `https://www.youtube.com/embed/${id}`;\n } catch {\n return null;\n }\n}\n\nfunction parseSpotifyEmbed(url: string): string | null {\n try {\n const u = new URL(url);\n if (!u.hostname.includes(\"spotify.com\")) return null;\n // Spotify's embed endpoint only accepts /embed/{type}/{id}. Isolate the\n // resource type + id so locale prefixes (e.g. /intl-pt) and trailing query\n // params don't leak through — `/embed/intl-pt/album/…` 404s on Spotify.\n const match = u.pathname.match(\n /(track|album|playlist|episode|show|artist)\\/([A-Za-z0-9]+)/\n );\n if (!match) return null;\n return `https://open.spotify.com/embed/${match[1]}/${match[2]}`;\n } catch {\n return null;\n }\n}\n\nfunction parseSoundCloudEmbed(url: string): string | null {\n try {\n new URL(url); // validate\n if (!url.includes(\"soundcloud.com\")) return null;\n const encoded = encodeURIComponent(url);\n return `https://w.soundcloud.com/player/?url=${encoded}&color=%23ff5500&auto_play=false&hide_related=true&show_comments=false&show_user=true&show_reposts=false`;\n } catch {\n return null;\n }\n}\n\nfunction parseTikTokEmbed(url: string): string | null {\n try {\n const u = new URL(url);\n if (!u.hostname.includes(\"tiktok.com\")) return null;\n const match = u.pathname.match(/\\/video\\/(\\d+)/);\n if (!match) return null;\n return `https://www.tiktok.com/embed/v2/${match[1]}`;\n } catch {\n return null;\n }\n}\n\nfunction parseVimeoEmbed(url: string): string | null {\n try {\n const u = new URL(url);\n if (!u.hostname.includes(\"vimeo.com\")) return null;\n const match = u.pathname.match(/(\\d+)/);\n if (!match) return null;\n return `https://player.vimeo.com/video/${match[1]}`;\n } catch {\n return null;\n }\n}\n\nfunction getEmbedSrc(platform: EmbedPlatform, value: string): string | null {\n switch (platform) {\n case \"youtube\": return parseYouTubeEmbed(value);\n case \"spotify\": return parseSpotifyEmbed(value);\n case \"soundcloud\": return parseSoundCloudEmbed(value);\n case \"tiktok\": return parseTikTokEmbed(value);\n case \"vimeo\": return parseVimeoEmbed(value);\n }\n}\n\n// Compact iframe (fixed height) vs 16:9 video frame.\nconst COMPACT: Record<EmbedPlatform, boolean> = {\n spotify: true,\n soundcloud: true,\n youtube: false,\n tiktok: false,\n vimeo: false,\n};\n\n// ── Component ─────────────────────────────────────────────────────────────────\n\nexport function IPTypeDisplay({ attributes }: IPTypeDisplayProps) {\n const attrs = attributes ?? [];\n\n const ipType = attrs.find(\n (a) => a.trait_type?.toLowerCase() === \"ip type\"\n )?.value as IPType | undefined;\n if (!ipType) return null;\n\n const template = IP_TEMPLATES[ipType];\n if (!template) return null;\n\n const getAttr = (key: string) =>\n attrs.find((a) => a.trait_type === key)?.value ?? null;\n\n const embeds = (template.embeds ?? []).flatMap((platform) => {\n const meta = EMBED_PLATFORM_META[platform];\n const value = getAttr(meta.traitKey);\n return value ? [{ platform, meta, value }] : [];\n });\n\n const socials = (template.socials ?? []).flatMap((platform) => {\n const meta = SOCIAL_PLATFORM_META[platform];\n const value = getAttr(meta.traitKey);\n return value ? [{ platform, meta, value }] : [];\n });\n\n if (embeds.length === 0 && socials.length === 0) return null;\n\n return (\n <div className=\"space-y-5\">\n {embeds.map(({ platform, meta, value }) => {\n const src = getEmbedSrc(platform, value);\n if (src) {\n return (\n <div key={platform} className=\"space-y-1.5\">\n <p className=\"text-xs font-semibold uppercase tracking-wider text-muted-foreground\">\n {meta.label}\n </p>\n {COMPACT[platform] ? (\n <iframe\n src={src}\n className=\"w-full rounded-xl border-0\"\n height={166}\n allow=\"autoplay\"\n loading=\"lazy\"\n title={meta.label}\n />\n ) : (\n <div className=\"relative w-full aspect-video rounded-xl overflow-hidden bg-muted/20\">\n <iframe\n src={src}\n className=\"absolute inset-0 w-full h-full\"\n allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture\"\n allowFullScreen\n loading=\"lazy\"\n title={meta.label}\n />\n </div>\n )}\n </div>\n );\n }\n // Fallback: plain external link if URL parsing failed\n return (\n <div key={platform}>\n <p className=\"text-xs font-semibold uppercase tracking-wider text-muted-foreground mb-1\">\n {meta.label}\n </p>\n <a\n href={value}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n className=\"inline-flex items-center gap-1.5 text-sm text-primary hover:underline\"\n >\n <ExternalLink className=\"h-3.5 w-3.5\" />\n Open link\n </a>\n </div>\n );\n })}\n\n {socials.length > 0 && (\n <div className=\"space-y-1.5\">\n <p className=\"text-xs font-semibold uppercase tracking-wider text-muted-foreground\">\n Links\n </p>\n <div className=\"flex flex-wrap gap-2\">\n {socials.map(({ platform, meta, value }) => {\n const SIcon = meta.icon;\n return (\n <a\n key={platform}\n href={value}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n className=\"inline-flex items-center gap-1.5 rounded-full border border-border bg-muted/30 px-3 py-1.5 text-xs font-medium text-muted-foreground transition-colors hover:border-primary/40 hover:text-foreground\"\n >\n <SIcon className=\"h-3.5 w-3.5\" />\n {meta.label}\n </a>\n );\n })}\n </div>\n </div>\n )}\n </div>\n );\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,150 @@
1
+ "use strict";
2
+ "use client";
3
+ var __create = Object.create;
4
+ var __defProp = Object.defineProperty;
5
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
6
+ var __getOwnPropNames = Object.getOwnPropertyNames;
7
+ var __getProtoOf = Object.getPrototypeOf;
8
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
9
+ var __export = (target, all) => {
10
+ for (var name in all)
11
+ __defProp(target, name, { get: all[name], enumerable: true });
12
+ };
13
+ var __copyProps = (to, from, except, desc) => {
14
+ if (from && typeof from === "object" || typeof from === "function") {
15
+ for (let key of __getOwnPropNames(from))
16
+ if (!__hasOwnProp.call(to, key) && key !== except)
17
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
18
+ }
19
+ return to;
20
+ };
21
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
22
+ // If the importer is in node compatibility mode or this is not an ESM
23
+ // file that has been converted to a CommonJS file using a Babel-
24
+ // compatible transform (i.e. "__esModule" has not been set), then set
25
+ // "default" to the CommonJS "module.exports" for node compatibility.
26
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
27
+ mod
28
+ ));
29
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
30
+ var launchpad_strip_exports = {};
31
+ __export(launchpad_strip_exports, {
32
+ LaunchpadStrip: () => LaunchpadStrip
33
+ });
34
+ module.exports = __toCommonJS(launchpad_strip_exports);
35
+ var import_jsx_runtime = require("react/jsx-runtime");
36
+ var import_link = __toESM(require("next/link"), 1);
37
+ var import_lucide_react = require("lucide-react");
38
+ var import_scroll_section = require("./scroll-section.js");
39
+ var import_launchpad_services = require("./launchpad-services.js");
40
+ var import_launchpad_services2 = require("../data/launchpad-services.js");
41
+ var import_cn = require("../utils/cn.js");
42
+ const DEF_BY_KEY = Object.fromEntries(import_launchpad_services2.LAUNCHPAD_SERVICE_DEFINITIONS.map((d) => [d.key, d]));
43
+ const STRIP_ORDER = [
44
+ "mint-ip-asset",
45
+ "create-collection",
46
+ "ip-collection-1155",
47
+ "mint-editions",
48
+ "collection-drop",
49
+ "pop-protocol"
50
+ ];
51
+ const MARKETPLACE_HUE = {
52
+ text: "text-indigo-600 dark:text-indigo-400",
53
+ solid: "bg-indigo-500",
54
+ border: "border-indigo-500/25",
55
+ pill: "bg-gradient-to-r from-indigo-500 to-blue-600"
56
+ };
57
+ function ServiceCard({ card }) {
58
+ const { icon: Icon, title, blurb, example, cta, hue, href } = card;
59
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
60
+ import_link.default,
61
+ {
62
+ href,
63
+ className: (0, import_cn.cn)(
64
+ "relative rounded-2xl border bg-card overflow-hidden flex flex-col h-full min-h-[280px]",
65
+ "transition-transform active:scale-[0.99]",
66
+ hue.border
67
+ ),
68
+ children: [
69
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { "aria-hidden": true, className: "absolute -right-7 -bottom-9 opacity-[0.04] select-none pointer-events-none", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Icon, { className: "h-36 w-36" }) }),
70
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "relative flex flex-col flex-1 p-6 gap-3.5", children: [
71
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "relative w-fit", children: [
72
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { "aria-hidden": true, className: (0, import_cn.cn)("absolute -inset-3 rounded-full blur-2xl opacity-30", hue.solid) }),
73
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Icon, { className: (0, import_cn.cn)("relative h-8 w-8", hue.text) })
74
+ ] }),
75
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "space-y-1.5", children: [
76
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h3", { className: "text-xl font-black tracking-tight leading-snug", children: title }),
77
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "text-sm text-muted-foreground leading-relaxed", children: blurb }),
78
+ example && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("p", { className: "text-xs text-muted-foreground/70 italic leading-relaxed", children: [
79
+ "e.g. ",
80
+ example
81
+ ] })
82
+ ] }),
83
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "mt-auto pt-1 flex justify-end", children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
84
+ "span",
85
+ {
86
+ className: (0, import_cn.cn)(
87
+ "inline-flex items-center gap-2 h-9 px-4 rounded-full",
88
+ "text-sm font-semibold text-white shadow-lg shadow-black/25",
89
+ hue.pill
90
+ ),
91
+ children: [
92
+ cta,
93
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_lucide_react.ArrowRight, { className: "h-3.5 w-3.5" })
94
+ ]
95
+ }
96
+ ) })
97
+ ] })
98
+ ]
99
+ }
100
+ );
101
+ }
102
+ function LaunchpadStrip({
103
+ hrefs,
104
+ marketplaceHref,
105
+ launchpadHref = "/launchpad"
106
+ }) {
107
+ const cards = [
108
+ ...STRIP_ORDER.flatMap((key) => {
109
+ const def = DEF_BY_KEY[key];
110
+ const href = hrefs[key];
111
+ if (!def || !href) return [];
112
+ return [{
113
+ key,
114
+ href,
115
+ icon: def.icon,
116
+ title: def.title,
117
+ blurb: def.blurb,
118
+ example: def.example,
119
+ cta: def.cta,
120
+ hue: import_launchpad_services.SERVICE_HUES[key] ?? MARKETPLACE_HUE
121
+ }];
122
+ }),
123
+ ...marketplaceHref ? [{
124
+ key: "marketplace",
125
+ href: marketplaceHref,
126
+ icon: import_lucide_react.ShoppingBag,
127
+ title: "Marketplace",
128
+ blurb: "Discover and trade works from creators \u2014 gasless, instantly settled.",
129
+ example: "Buy an art print, make an offer on a music track",
130
+ cta: "Browse",
131
+ hue: MARKETPLACE_HUE
132
+ }] : []
133
+ ];
134
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
135
+ import_scroll_section.ScrollSection,
136
+ {
137
+ icon: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_lucide_react.Rocket, { className: "h-3.5 w-3.5 text-white" }),
138
+ iconBg: "bg-gradient-to-br from-primary to-blue-600 shadow-md shadow-primary/20",
139
+ title: "Launchpad",
140
+ href: launchpadHref,
141
+ linkLabel: "Explore",
142
+ children: cards.map((card) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "w-64 sm:w-72 snap-start shrink-0 flex", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ServiceCard, { card }) }, card.key))
143
+ }
144
+ );
145
+ }
146
+ // Annotate the CommonJS export names for ESM import in node:
147
+ 0 && (module.exports = {
148
+ LaunchpadStrip
149
+ });
150
+ //# sourceMappingURL=launchpad-strip.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/components/launchpad-strip.tsx"],"sourcesContent":["\"use client\";\n\nimport Link from \"next/link\";\nimport { ShoppingBag, ArrowRight, Rocket } from \"lucide-react\";\nimport type { LucideIcon } from \"lucide-react\";\nimport { ScrollSection } from \"./scroll-section.js\";\nimport { SERVICE_HUES } from \"./launchpad-services.js\";\nimport { LAUNCHPAD_SERVICE_DEFINITIONS } from \"../data/launchpad-services.js\";\nimport { cn } from \"../utils/cn.js\";\n\n/** Homepage launchpad strip — cards derive from the shared launchpad service\n * definitions: creator language, one blurb, one example, one vivid verb pill.\n * No tech chips, no long descriptions. Apps inject only hrefs. */\n\nexport interface LaunchpadStripProps {\n /** Per-service destinations, keyed by service key. Services without an href are skipped. */\n hrefs: Record<string, string>;\n /** Marketplace card destination (omit to hide the marketplace card) */\n marketplaceHref?: string;\n /** \"Explore\" header link */\n launchpadHref?: string;\n}\n\ninterface StripCard {\n key: string;\n href: string;\n icon: LucideIcon;\n title: string;\n blurb: string;\n example?: string;\n cta: string;\n hue: { text: string; solid: string; border: string; pill: string };\n}\n\nconst DEF_BY_KEY = Object.fromEntries(LAUNCHPAD_SERVICE_DEFINITIONS.map((d) => [d.key, d]));\n\n/** Display order on the homepage strip */\nconst STRIP_ORDER = [\n \"mint-ip-asset\",\n \"create-collection\",\n \"ip-collection-1155\",\n \"mint-editions\",\n \"collection-drop\",\n \"pop-protocol\",\n];\n\nconst MARKETPLACE_HUE = {\n text: \"text-indigo-600 dark:text-indigo-400\",\n solid: \"bg-indigo-500\",\n border: \"border-indigo-500/25\",\n pill: \"bg-gradient-to-r from-indigo-500 to-blue-600\",\n};\n\nfunction ServiceCard({ card }: { card: StripCard }) {\n const { icon: Icon, title, blurb, example, cta, hue, href } = card;\n return (\n <Link\n href={href}\n className={cn(\n \"relative rounded-2xl border bg-card overflow-hidden flex flex-col h-full min-h-[280px]\",\n \"transition-transform active:scale-[0.99]\",\n hue.border,\n )}\n >\n {/* Ghosted watermark icon (launchpad card language) */}\n <div aria-hidden className=\"absolute -right-7 -bottom-9 opacity-[0.04] select-none pointer-events-none\">\n <Icon className=\"h-36 w-36\" />\n </div>\n\n <div className=\"relative flex flex-col flex-1 p-6 gap-3.5\">\n <div className=\"relative w-fit\">\n <div aria-hidden className={cn(\"absolute -inset-3 rounded-full blur-2xl opacity-30\", hue.solid)} />\n <Icon className={cn(\"relative h-8 w-8\", hue.text)} />\n </div>\n\n <div className=\"space-y-1.5\">\n <h3 className=\"text-xl font-black tracking-tight leading-snug\">{title}</h3>\n <p className=\"text-sm text-muted-foreground leading-relaxed\">{blurb}</p>\n {example && (\n <p className=\"text-xs text-muted-foreground/70 italic leading-relaxed\">e.g. {example}</p>\n )}\n </div>\n\n <div className=\"mt-auto pt-1 flex justify-end\">\n <span\n className={cn(\n \"inline-flex items-center gap-2 h-9 px-4 rounded-full\",\n \"text-sm font-semibold text-white shadow-lg shadow-black/25\",\n hue.pill,\n )}\n >\n {cta}\n <ArrowRight className=\"h-3.5 w-3.5\" />\n </span>\n </div>\n </div>\n </Link>\n );\n}\n\nexport function LaunchpadStrip({\n hrefs,\n marketplaceHref,\n launchpadHref = \"/launchpad\",\n}: LaunchpadStripProps) {\n const cards: StripCard[] = [\n ...STRIP_ORDER.flatMap((key) => {\n const def = DEF_BY_KEY[key];\n const href = hrefs[key];\n if (!def || !href) return [];\n return [{\n key,\n href,\n icon: def.icon,\n title: def.title,\n blurb: def.blurb,\n example: def.example,\n cta: def.cta,\n hue: SERVICE_HUES[key] ?? MARKETPLACE_HUE,\n }];\n }),\n ...(marketplaceHref\n ? [{\n key: \"marketplace\",\n href: marketplaceHref,\n icon: ShoppingBag,\n title: \"Marketplace\",\n blurb: \"Discover and trade works from creators — gasless, instantly settled.\",\n example: \"Buy an art print, make an offer on a music track\",\n cta: \"Browse\",\n hue: MARKETPLACE_HUE,\n }]\n : []),\n ];\n\n return (\n <ScrollSection\n icon={<Rocket className=\"h-3.5 w-3.5 text-white\" />}\n iconBg=\"bg-gradient-to-br from-primary to-blue-600 shadow-md shadow-primary/20\"\n title=\"Launchpad\"\n href={launchpadHref}\n linkLabel=\"Explore\"\n >\n {cards.map((card) => (\n <div key={card.key} className=\"w-64 sm:w-72 snap-start shrink-0 flex\">\n <ServiceCard card={card} />\n </div>\n ))}\n </ScrollSection>\n );\n}\n"],"mappings":"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","names":["import_launchpad_services","Link"]}
@@ -0,0 +1,16 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+
3
+ /** Homepage launchpad strip — cards derive from the shared launchpad service
4
+ * definitions: creator language, one blurb, one example, one vivid verb pill.
5
+ * No tech chips, no long descriptions. Apps inject only hrefs. */
6
+ interface LaunchpadStripProps {
7
+ /** Per-service destinations, keyed by service key. Services without an href are skipped. */
8
+ hrefs: Record<string, string>;
9
+ /** Marketplace card destination (omit to hide the marketplace card) */
10
+ marketplaceHref?: string;
11
+ /** "Explore" header link */
12
+ launchpadHref?: string;
13
+ }
14
+ declare function LaunchpadStrip({ hrefs, marketplaceHref, launchpadHref, }: LaunchpadStripProps): react_jsx_runtime.JSX.Element;
15
+
16
+ export { LaunchpadStrip, type LaunchpadStripProps };