@medialane/ui 0.153.0 → 0.155.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/collection-hero-banner.cjs.map +1 -1
- package/dist/components/collection-hero-banner.d.cts +0 -2
- package/dist/components/collection-hero-banner.d.ts +0 -2
- package/dist/components/collection-hero-banner.js.map +1 -1
- package/dist/components/drop-preview-card.test.d.cts +3 -2
- package/dist/components/drop-preview-card.test.d.ts +3 -2
- package/dist/components/dual-price.cjs +0 -2
- package/dist/components/dual-price.cjs.map +1 -1
- package/dist/components/dual-price.d.cts +0 -2
- package/dist/components/dual-price.d.ts +0 -2
- package/dist/components/dual-price.js +0 -2
- package/dist/components/dual-price.js.map +1 -1
- package/dist/components/fast-mint/fast-mint.cjs +4 -4
- package/dist/components/fast-mint/fast-mint.cjs.map +1 -1
- package/dist/components/fast-mint/fast-mint.d.cts +2 -2
- package/dist/components/fast-mint/fast-mint.d.ts +2 -2
- package/dist/components/fast-mint/fast-mint.js +4 -4
- package/dist/components/fast-mint/fast-mint.js.map +1 -1
- package/dist/components/image.cjs.map +1 -1
- package/dist/components/image.js.map +1 -1
- package/dist/components/ip-type-fields.cjs.map +1 -1
- package/dist/components/ip-type-fields.d.cts +0 -6
- package/dist/components/ip-type-fields.d.ts +0 -6
- package/dist/components/ip-type-fields.js.map +1 -1
- package/dist/components/launchpad-cta-banner.cjs.map +1 -1
- package/dist/components/launchpad-cta-banner.d.cts +0 -4
- package/dist/components/launchpad-cta-banner.d.ts +0 -4
- package/dist/components/launchpad-cta-banner.js.map +1 -1
- package/dist/components/launchpad-services.cjs.map +1 -1
- package/dist/components/launchpad-services.d.cts +0 -3
- package/dist/components/launchpad-services.d.ts +0 -3
- package/dist/components/launchpad-services.js.map +1 -1
- package/dist/components/link.cjs.map +1 -1
- package/dist/components/link.d.cts +0 -2
- package/dist/components/link.d.ts +0 -2
- package/dist/components/link.js.map +1 -1
- package/dist/components/medialane-collection-card.cjs.map +1 -1
- package/dist/components/medialane-collection-card.d.cts +0 -1
- package/dist/components/medialane-collection-card.d.ts +0 -1
- package/dist/components/medialane-collection-card.js.map +1 -1
- package/dist/components/portfolio-section.cjs.map +1 -1
- package/dist/components/portfolio-section.d.cts +0 -4
- package/dist/components/portfolio-section.d.ts +0 -4
- package/dist/components/portfolio-section.js.map +1 -1
- package/dist/components/router.cjs.map +1 -1
- package/dist/components/router.js.map +1 -1
- package/dist/components/sheet.d.cts +1 -1
- package/dist/components/sheet.d.ts +1 -1
- package/dist/data/coins.cjs.map +1 -1
- package/dist/data/coins.d.cts +0 -2
- package/dist/data/coins.d.ts +0 -2
- package/dist/data/coins.js.map +1 -1
- package/dist/data/drop-create-schema.d.cts +8 -8
- package/dist/data/drop-create-schema.d.ts +8 -8
- package/dist/index.cjs +4 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -1
- package/dist/medialane.css +1 -47
- package/dist/utils/ipfs-upload.cjs +46 -19
- package/dist/utils/ipfs-upload.cjs.map +1 -1
- package/dist/utils/ipfs-upload.d.cts +21 -3
- package/dist/utils/ipfs-upload.d.ts +21 -3
- package/dist/utils/ipfs-upload.js +44 -19
- package/dist/utils/ipfs-upload.js.map +1 -1
- package/dist/utils/use-usd-prices.cjs.map +1 -1
- package/dist/utils/use-usd-prices.d.cts +0 -2
- package/dist/utils/use-usd-prices.d.ts +0 -2
- package/dist/utils/use-usd-prices.js.map +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/components/ip-type-fields.tsx"],"sourcesContent":["\"use client\";\n\nimport { useEffect, useMemo, useRef, useState } from \"react\";\nimport type { IPType } from \"../data/ip.js\";\nimport {\n IP_TEMPLATES,\n EMBED_PLATFORM_META,\n SOCIAL_PLATFORM_META,\n type TraitSuggestion,\n} from \"../data/ip-templates.js\";\nimport { Label } from \"./label.js\";\nimport { Input } from \"./input.js\";\nimport { Button } from \"./button.js\";\nimport {\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n} from \"./select.js\";\nimport { Check, FileText, FileUp, Loader2, Plus, Trash2, X } from \"lucide-react\";\n\nexport type MetadataField = {\n traitType: string;\n value: string;\n};\n\ntype TraitRow = {\n id: string;\n key: string;\n value: string;\n options?: string[];\n};\n\nconst createId = () =>\n globalThis.crypto?.randomUUID?.() ?? `trait-${Date.now()}-${Math.random().toString(36).slice(2)}`;\n\nconst looksLikeUrl = (v: string) => /^https?:\\/\\/\\S+/i.test(v.trim());\n\nexport interface IPTypeFieldsProps {\n ipType: IPType | null;\n onChange: (fields: MetadataField[]) => void;\n\n uploadDocument?: (file: File) => Promise<string>;\n\n /**\n * When the primary asset being minted is already the document this IP\n * type would otherwise ask to upload (e.g. a Documents/Publications type\n * whose main file is a PDF), pass it here to skip the redundant picker\n * and show it as already attached instead.\n */\n existingDocument?: { uri: string; name: string } | null;\n}\n\nexport function IPTypeFields({ ipType, onChange, uploadDocument, existingDocument }: IPTypeFieldsProps) {\n const template = ipType ? IP_TEMPLATES[ipType] : null;\n\n const [embedValues, setEmbedValues] = useState<Record<string, string>>({});\n const [socialValues, setSocialValues] = useState<Record<string, string>>({});\n\n const [traits, setTraits] = useState<TraitRow[]>([]);\n\n const [docUri, setDocUri] = useState<string | null>(null);\n const [docName, setDocName] = useState<string | null>(null);\n const [docUploading, setDocUploading] = useState(false);\n const [docError, setDocError] = useState<string | null>(null);\n const docInputRef = useRef<HTMLInputElement>(null);\n\n useEffect(() => {\n setEmbedValues({});\n setSocialValues({});\n setDocUri(null);\n setDocName(null);\n setDocError(null);\n }, [ipType]);\n\n const effectiveDocUri = existingDocument?.uri ?? docUri;\n const effectiveDocName = existingDocument?.name ?? docName;\n\n const docTraitType = template?.docUpload?.traitType ?? null;\n\n const metadataFields = useMemo(() => {\n const fields: MetadataField[] = [];\n const seen = new Set<string>();\n const add = (traitType: string, value: string) => {\n const t = traitType.trim();\n const v = value.trim();\n const k = t.toLowerCase();\n if (!t || !v || seen.has(k)) return;\n seen.add(k);\n fields.push({ traitType: t, value: v });\n };\n if (docTraitType && effectiveDocUri) add(docTraitType, effectiveDocUri);\n Object.entries(embedValues).forEach(([key, value]) => add(key, value));\n Object.entries(socialValues).forEach(([key, value]) => add(key, value));\n traits.forEach((row) => add(row.key, row.value));\n return fields;\n }, [embedValues, socialValues, traits, docTraitType, effectiveDocUri]);\n\n useEffect(() => {\n onChange(metadataFields);\n }, [metadataFields, onChange]);\n\n const addedKeys = useMemo(\n () => new Set(traits.map((t) => t.key.trim().toLowerCase())),\n [traits]\n );\n\n const addSuggestion = (s: TraitSuggestion) => {\n setTraits((cur) => [\n ...cur,\n { id: createId(), key: s.key, value: \"\", options: s.options },\n ]);\n };\n const addCustomTrait = () =>\n setTraits((cur) => [...cur, { id: createId(), key: \"\", value: \"\" }]);\n const updateTrait = (id: string, patch: Partial<TraitRow>) =>\n setTraits((cur) => cur.map((t) => (t.id === id ? { ...t, ...patch } : t)));\n const removeTrait = (id: string) =>\n setTraits((cur) => cur.filter((t) => t.id !== id));\n\n if (!template) return null;\n\n const Icon = template.icon;\n const docUpload = template.docUpload;\n\n const handleDocPick = async (file: File) => {\n if (!uploadDocument || !docUpload) return;\n if (file.size > docUpload.maxMb * 1024 * 1024) {\n setDocError(`File must be under ${docUpload.maxMb} MB`);\n return;\n }\n setDocError(null);\n setDocUploading(true);\n try {\n const uri = await uploadDocument(file);\n setDocUri(uri);\n setDocName(file.name);\n } catch (err) {\n setDocError(err instanceof Error ? err.message : \"Document upload failed\");\n } finally {\n setDocUploading(false);\n if (docInputRef.current) docInputRef.current.value = \"\";\n }\n };\n\n const clearDoc = () => {\n setDocUri(null);\n setDocName(null);\n setDocError(null);\n };\n\n return (\n <div className=\"space-y-5\">\n <div className=\"flex items-center gap-2\">\n <Icon className={`h-4 w-4 ${template.color.text}`} />\n <p className=\"text-sm font-semibold\">{template.label} Details</p>\n </div>\n\n {docUpload && (uploadDocument || existingDocument) && (\n <section className=\"space-y-2\">\n <p className=\"text-[11px] font-semibold text-muted-foreground\">\n Document\n </p>\n {existingDocument ? (\n <div className=\"flex items-center gap-3 rounded-xl border border-emerald-500/30 bg-emerald-500/5 px-4 py-3\">\n <FileText className=\"h-5 w-5 text-emerald-500 shrink-0\" />\n <div className=\"min-w-0 flex-1\">\n <p className=\"text-sm font-medium truncate\">{effectiveDocName}</p>\n <p className=\"text-xs text-muted-foreground\">Already attached as your uploaded file</p>\n </div>\n </div>\n ) : docUri ? (\n <div className=\"flex items-center gap-3 rounded-xl border border-emerald-500/30 bg-emerald-500/5 px-4 py-3\">\n <FileText className=\"h-5 w-5 text-emerald-500 shrink-0\" />\n <div className=\"min-w-0 flex-1\">\n <p className=\"text-sm font-medium truncate\">{docName}</p>\n <p className=\"text-xs text-muted-foreground truncate\">{docUri}</p>\n </div>\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"icon\"\n className=\"h-8 w-8 shrink-0 text-muted-foreground hover:text-destructive\"\n onClick={clearDoc}\n aria-label=\"Remove document\"\n >\n <X className=\"h-4 w-4\" />\n </Button>\n </div>\n ) : (\n <div className=\"space-y-1.5\">\n <input\n ref={docInputRef}\n type=\"file\"\n accept={docUpload.accept}\n className=\"hidden\"\n onChange={(e) => {\n const f = e.target.files?.[0];\n if (f) void handleDocPick(f);\n }}\n />\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n disabled={docUploading}\n onClick={() => docInputRef.current?.click()}\n >\n {docUploading ? (\n <>\n <Loader2 className=\"mr-1.5 h-3.5 w-3.5 animate-spin\" />\n Uploading…\n </>\n ) : (\n <>\n <FileUp className=\"mr-1.5 h-3.5 w-3.5\" />\n Upload document\n </>\n )}\n </Button>\n {docError && <p className=\"text-xs text-destructive\">{docError}</p>}\n <p className=\"text-xs text-muted-foreground leading-relaxed\">{docUpload.hint}</p>\n </div>\n )}\n </section>\n )}\n\n {template.embeds && template.embeds.length > 0 && (\n <section className=\"space-y-3\">\n <p className=\"text-[11px] font-semibold text-muted-foreground\">Embeds</p>\n {template.embeds.map((platform) => {\n const meta = EMBED_PLATFORM_META[platform];\n const PIcon = meta.icon;\n const value = embedValues[meta.traitKey] ?? \"\";\n return (\n <div key={platform} className=\"space-y-1.5\">\n <Label className=\"flex items-center gap-1.5 text-xs text-muted-foreground\">\n <PIcon className=\"h-3.5 w-3.5\" />\n {meta.label}\n {looksLikeUrl(value) && (\n <span className=\"ml-1 inline-flex items-center gap-0.5 text-[10px] font-medium text-emerald-400\">\n <Check className=\"h-3 w-3\" /> connected\n </span>\n )}\n </Label>\n <Input\n type=\"url\"\n inputMode=\"url\"\n placeholder={meta.placeholder}\n value={value}\n onChange={(e) =>\n setEmbedValues((cur) => ({ ...cur, [meta.traitKey]: e.target.value }))\n }\n className=\"h-10 text-sm\"\n />\n </div>\n );\n })}\n </section>\n )}\n\n {template.socials && template.socials.length > 0 && (\n <section className=\"space-y-3\">\n <p className=\"text-[11px] font-semibold text-muted-foreground\">Social links</p>\n {template.socials.map((platform) => {\n const meta = SOCIAL_PLATFORM_META[platform];\n const PIcon = meta.icon;\n const value = socialValues[meta.traitKey] ?? \"\";\n return (\n <div key={platform} className=\"space-y-1.5\">\n <Label className=\"flex items-center gap-1.5 text-xs text-muted-foreground\">\n <PIcon className=\"h-3.5 w-3.5\" />\n {meta.label}\n </Label>\n <Input\n type=\"url\"\n inputMode=\"url\"\n placeholder={meta.placeholder}\n value={value}\n onChange={(e) =>\n setSocialValues((cur) => ({ ...cur, [meta.traitKey]: e.target.value }))\n }\n className=\"h-10 text-sm\"\n />\n </div>\n );\n })}\n </section>\n )}\n\n <section className=\"space-y-3 border-t border-border/60 pt-4\">\n <p className=\"text-sm font-semibold\">Traits</p>\n\n {template.traitSuggestions && template.traitSuggestions.length > 0 && (\n <div className=\"flex flex-wrap gap-2\">\n {template.traitSuggestions\n .filter((s) => !addedKeys.has(s.key.toLowerCase()))\n .map((s) => (\n <button\n key={s.key}\n type=\"button\"\n onClick={() => addSuggestion(s)}\n className=\"inline-flex items-center gap-1 rounded-full border border-border bg-muted/40 px-3 py-1 text-xs font-medium text-muted-foreground transition-colors hover:border-primary/40 hover:text-foreground\"\n >\n <Plus className=\"h-3 w-3\" />\n {s.key}\n </button>\n ))}\n </div>\n )}\n\n {traits.length > 0 && (\n <div className=\"space-y-2\">\n {traits.map((row) => (\n <div\n key={row.id}\n className=\"grid grid-cols-[1fr_1fr_auto] items-center gap-2\"\n >\n <Input\n value={row.key}\n onChange={(e) => updateTrait(row.id, { key: e.target.value })}\n placeholder=\"Trait name\"\n className=\"h-9 text-sm\"\n maxLength={64}\n />\n {row.options ? (\n <Select\n value={row.value}\n onValueChange={(v) => updateTrait(row.id, { value: v })}\n >\n <SelectTrigger className=\"h-9 text-sm\">\n <SelectValue placeholder=\"Value\" />\n </SelectTrigger>\n <SelectContent>\n {row.options.map((opt) => (\n <SelectItem key={opt} value={opt}>{opt}</SelectItem>\n ))}\n </SelectContent>\n </Select>\n ) : (\n <Input\n value={row.value}\n onChange={(e) => updateTrait(row.id, { value: e.target.value })}\n placeholder=\"Value\"\n className=\"h-9 text-sm\"\n maxLength={512}\n />\n )}\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"icon\"\n className=\"h-9 w-9 shrink-0 text-muted-foreground hover:text-destructive\"\n onClick={() => removeTrait(row.id)}\n aria-label=\"Remove trait\"\n >\n <Trash2 className=\"h-4 w-4\" />\n </Button>\n </div>\n ))}\n </div>\n )}\n\n <Button type=\"button\" variant=\"outline\" size=\"sm\" onClick={addCustomTrait}>\n <Plus className=\"mr-1.5 h-3.5 w-3.5\" />\n Add custom trait\n </Button>\n </section>\n </div>\n );\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../src/components/ip-type-fields.tsx"],"sourcesContent":["\"use client\";\n\nimport { useEffect, useMemo, useRef, useState } from \"react\";\nimport type { IPType } from \"../data/ip.js\";\nimport {\n IP_TEMPLATES,\n EMBED_PLATFORM_META,\n SOCIAL_PLATFORM_META,\n type TraitSuggestion,\n} from \"../data/ip-templates.js\";\nimport { Label } from \"./label.js\";\nimport { Input } from \"./input.js\";\nimport { Button } from \"./button.js\";\nimport {\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n} from \"./select.js\";\nimport { Check, FileText, FileUp, Loader2, Plus, Trash2, X } from \"lucide-react\";\n\nexport type MetadataField = {\n traitType: string;\n value: string;\n};\n\ntype TraitRow = {\n id: string;\n key: string;\n value: string;\n options?: string[];\n};\n\nconst createId = () =>\n globalThis.crypto?.randomUUID?.() ?? `trait-${Date.now()}-${Math.random().toString(36).slice(2)}`;\n\nconst looksLikeUrl = (v: string) => /^https?:\\/\\/\\S+/i.test(v.trim());\n\nexport interface IPTypeFieldsProps {\n ipType: IPType | null;\n onChange: (fields: MetadataField[]) => void;\n\n uploadDocument?: (file: File) => Promise<string>;\n\n existingDocument?: { uri: string; name: string } | null;\n}\n\nexport function IPTypeFields({ ipType, onChange, uploadDocument, existingDocument }: IPTypeFieldsProps) {\n const template = ipType ? IP_TEMPLATES[ipType] : null;\n\n const [embedValues, setEmbedValues] = useState<Record<string, string>>({});\n const [socialValues, setSocialValues] = useState<Record<string, string>>({});\n\n const [traits, setTraits] = useState<TraitRow[]>([]);\n\n const [docUri, setDocUri] = useState<string | null>(null);\n const [docName, setDocName] = useState<string | null>(null);\n const [docUploading, setDocUploading] = useState(false);\n const [docError, setDocError] = useState<string | null>(null);\n const docInputRef = useRef<HTMLInputElement>(null);\n\n useEffect(() => {\n setEmbedValues({});\n setSocialValues({});\n setDocUri(null);\n setDocName(null);\n setDocError(null);\n }, [ipType]);\n\n const effectiveDocUri = existingDocument?.uri ?? docUri;\n const effectiveDocName = existingDocument?.name ?? docName;\n\n const docTraitType = template?.docUpload?.traitType ?? null;\n\n const metadataFields = useMemo(() => {\n const fields: MetadataField[] = [];\n const seen = new Set<string>();\n const add = (traitType: string, value: string) => {\n const t = traitType.trim();\n const v = value.trim();\n const k = t.toLowerCase();\n if (!t || !v || seen.has(k)) return;\n seen.add(k);\n fields.push({ traitType: t, value: v });\n };\n if (docTraitType && effectiveDocUri) add(docTraitType, effectiveDocUri);\n Object.entries(embedValues).forEach(([key, value]) => add(key, value));\n Object.entries(socialValues).forEach(([key, value]) => add(key, value));\n traits.forEach((row) => add(row.key, row.value));\n return fields;\n }, [embedValues, socialValues, traits, docTraitType, effectiveDocUri]);\n\n useEffect(() => {\n onChange(metadataFields);\n }, [metadataFields, onChange]);\n\n const addedKeys = useMemo(\n () => new Set(traits.map((t) => t.key.trim().toLowerCase())),\n [traits]\n );\n\n const addSuggestion = (s: TraitSuggestion) => {\n setTraits((cur) => [\n ...cur,\n { id: createId(), key: s.key, value: \"\", options: s.options },\n ]);\n };\n const addCustomTrait = () =>\n setTraits((cur) => [...cur, { id: createId(), key: \"\", value: \"\" }]);\n const updateTrait = (id: string, patch: Partial<TraitRow>) =>\n setTraits((cur) => cur.map((t) => (t.id === id ? { ...t, ...patch } : t)));\n const removeTrait = (id: string) =>\n setTraits((cur) => cur.filter((t) => t.id !== id));\n\n if (!template) return null;\n\n const Icon = template.icon;\n const docUpload = template.docUpload;\n\n const handleDocPick = async (file: File) => {\n if (!uploadDocument || !docUpload) return;\n if (file.size > docUpload.maxMb * 1024 * 1024) {\n setDocError(`File must be under ${docUpload.maxMb} MB`);\n return;\n }\n setDocError(null);\n setDocUploading(true);\n try {\n const uri = await uploadDocument(file);\n setDocUri(uri);\n setDocName(file.name);\n } catch (err) {\n setDocError(err instanceof Error ? err.message : \"Document upload failed\");\n } finally {\n setDocUploading(false);\n if (docInputRef.current) docInputRef.current.value = \"\";\n }\n };\n\n const clearDoc = () => {\n setDocUri(null);\n setDocName(null);\n setDocError(null);\n };\n\n return (\n <div className=\"space-y-5\">\n <div className=\"flex items-center gap-2\">\n <Icon className={`h-4 w-4 ${template.color.text}`} />\n <p className=\"text-sm font-semibold\">{template.label} Details</p>\n </div>\n\n {docUpload && (uploadDocument || existingDocument) && (\n <section className=\"space-y-2\">\n <p className=\"text-[11px] font-semibold text-muted-foreground\">\n Document\n </p>\n {existingDocument ? (\n <div className=\"flex items-center gap-3 rounded-xl border border-emerald-500/30 bg-emerald-500/5 px-4 py-3\">\n <FileText className=\"h-5 w-5 text-emerald-500 shrink-0\" />\n <div className=\"min-w-0 flex-1\">\n <p className=\"text-sm font-medium truncate\">{effectiveDocName}</p>\n <p className=\"text-xs text-muted-foreground\">Already attached as your uploaded file</p>\n </div>\n </div>\n ) : docUri ? (\n <div className=\"flex items-center gap-3 rounded-xl border border-emerald-500/30 bg-emerald-500/5 px-4 py-3\">\n <FileText className=\"h-5 w-5 text-emerald-500 shrink-0\" />\n <div className=\"min-w-0 flex-1\">\n <p className=\"text-sm font-medium truncate\">{docName}</p>\n <p className=\"text-xs text-muted-foreground truncate\">{docUri}</p>\n </div>\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"icon\"\n className=\"h-8 w-8 shrink-0 text-muted-foreground hover:text-destructive\"\n onClick={clearDoc}\n aria-label=\"Remove document\"\n >\n <X className=\"h-4 w-4\" />\n </Button>\n </div>\n ) : (\n <div className=\"space-y-1.5\">\n <input\n ref={docInputRef}\n type=\"file\"\n accept={docUpload.accept}\n className=\"hidden\"\n onChange={(e) => {\n const f = e.target.files?.[0];\n if (f) void handleDocPick(f);\n }}\n />\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n disabled={docUploading}\n onClick={() => docInputRef.current?.click()}\n >\n {docUploading ? (\n <>\n <Loader2 className=\"mr-1.5 h-3.5 w-3.5 animate-spin\" />\n Uploading…\n </>\n ) : (\n <>\n <FileUp className=\"mr-1.5 h-3.5 w-3.5\" />\n Upload document\n </>\n )}\n </Button>\n {docError && <p className=\"text-xs text-destructive\">{docError}</p>}\n <p className=\"text-xs text-muted-foreground leading-relaxed\">{docUpload.hint}</p>\n </div>\n )}\n </section>\n )}\n\n {template.embeds && template.embeds.length > 0 && (\n <section className=\"space-y-3\">\n <p className=\"text-[11px] font-semibold text-muted-foreground\">Embeds</p>\n {template.embeds.map((platform) => {\n const meta = EMBED_PLATFORM_META[platform];\n const PIcon = meta.icon;\n const value = embedValues[meta.traitKey] ?? \"\";\n return (\n <div key={platform} className=\"space-y-1.5\">\n <Label className=\"flex items-center gap-1.5 text-xs text-muted-foreground\">\n <PIcon className=\"h-3.5 w-3.5\" />\n {meta.label}\n {looksLikeUrl(value) && (\n <span className=\"ml-1 inline-flex items-center gap-0.5 text-[10px] font-medium text-emerald-400\">\n <Check className=\"h-3 w-3\" /> connected\n </span>\n )}\n </Label>\n <Input\n type=\"url\"\n inputMode=\"url\"\n placeholder={meta.placeholder}\n value={value}\n onChange={(e) =>\n setEmbedValues((cur) => ({ ...cur, [meta.traitKey]: e.target.value }))\n }\n className=\"h-10 text-sm\"\n />\n </div>\n );\n })}\n </section>\n )}\n\n {template.socials && template.socials.length > 0 && (\n <section className=\"space-y-3\">\n <p className=\"text-[11px] font-semibold text-muted-foreground\">Social links</p>\n {template.socials.map((platform) => {\n const meta = SOCIAL_PLATFORM_META[platform];\n const PIcon = meta.icon;\n const value = socialValues[meta.traitKey] ?? \"\";\n return (\n <div key={platform} className=\"space-y-1.5\">\n <Label className=\"flex items-center gap-1.5 text-xs text-muted-foreground\">\n <PIcon className=\"h-3.5 w-3.5\" />\n {meta.label}\n </Label>\n <Input\n type=\"url\"\n inputMode=\"url\"\n placeholder={meta.placeholder}\n value={value}\n onChange={(e) =>\n setSocialValues((cur) => ({ ...cur, [meta.traitKey]: e.target.value }))\n }\n className=\"h-10 text-sm\"\n />\n </div>\n );\n })}\n </section>\n )}\n\n <section className=\"space-y-3 border-t border-border/60 pt-4\">\n <p className=\"text-sm font-semibold\">Traits</p>\n\n {template.traitSuggestions && template.traitSuggestions.length > 0 && (\n <div className=\"flex flex-wrap gap-2\">\n {template.traitSuggestions\n .filter((s) => !addedKeys.has(s.key.toLowerCase()))\n .map((s) => (\n <button\n key={s.key}\n type=\"button\"\n onClick={() => addSuggestion(s)}\n className=\"inline-flex items-center gap-1 rounded-full border border-border bg-muted/40 px-3 py-1 text-xs font-medium text-muted-foreground transition-colors hover:border-primary/40 hover:text-foreground\"\n >\n <Plus className=\"h-3 w-3\" />\n {s.key}\n </button>\n ))}\n </div>\n )}\n\n {traits.length > 0 && (\n <div className=\"space-y-2\">\n {traits.map((row) => (\n <div\n key={row.id}\n className=\"grid grid-cols-[1fr_1fr_auto] items-center gap-2\"\n >\n <Input\n value={row.key}\n onChange={(e) => updateTrait(row.id, { key: e.target.value })}\n placeholder=\"Trait name\"\n className=\"h-9 text-sm\"\n maxLength={64}\n />\n {row.options ? (\n <Select\n value={row.value}\n onValueChange={(v) => updateTrait(row.id, { value: v })}\n >\n <SelectTrigger className=\"h-9 text-sm\">\n <SelectValue placeholder=\"Value\" />\n </SelectTrigger>\n <SelectContent>\n {row.options.map((opt) => (\n <SelectItem key={opt} value={opt}>{opt}</SelectItem>\n ))}\n </SelectContent>\n </Select>\n ) : (\n <Input\n value={row.value}\n onChange={(e) => updateTrait(row.id, { value: e.target.value })}\n placeholder=\"Value\"\n className=\"h-9 text-sm\"\n maxLength={512}\n />\n )}\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"icon\"\n className=\"h-9 w-9 shrink-0 text-muted-foreground hover:text-destructive\"\n onClick={() => removeTrait(row.id)}\n aria-label=\"Remove trait\"\n >\n <Trash2 className=\"h-4 w-4\" />\n </Button>\n </div>\n ))}\n </div>\n )}\n\n <Button type=\"button\" variant=\"outline\" size=\"sm\" onClick={addCustomTrait}>\n <Plus className=\"mr-1.5 h-3.5 w-3.5\" />\n Add custom trait\n </Button>\n </section>\n </div>\n );\n}\n"],"mappings":"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","names":[]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/components/launchpad-cta-banner.tsx"],"sourcesContent":["import type { ReactNode } from \"react\";\nimport { ArrowRight, ExternalLink } from \"lucide-react\";\nimport Link from \"./link.js\";\nimport { Button } from \"./button.js\";\nimport { cn } from \"../utils/cn.js\";\n\nexport interface LaunchpadCtaBannerProps {\n eyebrow: string;\n title: string;\n description: string;\n href: string;\n ctaLabel: string;\n
|
|
1
|
+
{"version":3,"sources":["../../src/components/launchpad-cta-banner.tsx"],"sourcesContent":["import type { ReactNode } from \"react\";\nimport { ArrowRight, ExternalLink } from \"lucide-react\";\nimport Link from \"./link.js\";\nimport { Button } from \"./button.js\";\nimport { cn } from \"../utils/cn.js\";\n\nexport interface LaunchpadCtaBannerProps {\n eyebrow: string;\n title: string;\n description: string;\n href: string;\n ctaLabel: string;\n\n external?: boolean;\n\n tone?: \"primary\" | \"manage\";\n className?: string;\n}\n\nconst TONE_GRADIENT: Record<NonNullable<LaunchpadCtaBannerProps[\"tone\"]>, string> = {\n primary: \"bg-gradient-to-r from-brand-blue/10 to-brand-purple/10\",\n manage: \"bg-gradient-to-r from-brand-navy/10 to-brand-purple/10\",\n};\n\nexport function LaunchpadCtaBanner({\n eyebrow,\n title,\n description,\n href,\n ctaLabel,\n external = false,\n tone = \"primary\",\n className,\n}: LaunchpadCtaBannerProps) {\n const icon: ReactNode = external\n ? <ExternalLink className=\"h-3.5 w-3.5 ml-1.5\" />\n : <ArrowRight className=\"h-3.5 w-3.5 ml-1.5\" />;\n\n return (\n <div\n className={cn(\n \"rounded-2xl border border-border/40 p-5 flex flex-col sm:flex-row items-start sm:items-center justify-between gap-4\",\n TONE_GRADIENT[tone],\n className\n )}\n >\n <div>\n <p className=\"section-label\">{eyebrow}</p>\n <p className=\"font-bold text-base mt-0.5\">{title}</p>\n <p className=\"text-sm text-muted-foreground mt-1\">{description}</p>\n </div>\n <Button variant=\"outline\" asChild className=\"shrink-0\">\n {external ? (\n <a href={href} target=\"_blank\" rel=\"noopener noreferrer\">\n {ctaLabel} {icon}\n </a>\n ) : (\n <Link href={href}>\n {ctaLabel} {icon}\n </Link>\n )}\n </Button>\n </div>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAmCM;AAlCN,0BAAyC;AACzC,kBAAiB;AACjB,oBAAuB;AACvB,gBAAmB;AAenB,MAAM,gBAA8E;AAAA,EAClF,SAAS;AAAA,EACT,QAAQ;AACV;AAEO,SAAS,mBAAmB;AAAA,EACjC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,WAAW;AAAA,EACX,OAAO;AAAA,EACP;AACF,GAA4B;AAC1B,QAAM,OAAkB,WACpB,4CAAC,oCAAa,WAAU,sBAAqB,IAC7C,4CAAC,kCAAW,WAAU,sBAAqB;AAE/C,SACE;AAAA,IAAC;AAAA;AAAA,MACC,eAAW;AAAA,QACT;AAAA,QACA,cAAc,IAAI;AAAA,QAClB;AAAA,MACF;AAAA,MAEA;AAAA,qDAAC,SACC;AAAA,sDAAC,OAAE,WAAU,iBAAiB,mBAAQ;AAAA,UACtC,4CAAC,OAAE,WAAU,8BAA8B,iBAAM;AAAA,UACjD,4CAAC,OAAE,WAAU,sCAAsC,uBAAY;AAAA,WACjE;AAAA,QACA,4CAAC,wBAAO,SAAQ,WAAU,SAAO,MAAC,WAAU,YACzC,qBACC,6CAAC,OAAE,MAAY,QAAO,UAAS,KAAI,uBAChC;AAAA;AAAA,UAAS;AAAA,UAAE;AAAA,WACd,IAEA,6CAAC,YAAAA,SAAA,EAAK,MACH;AAAA;AAAA,UAAS;AAAA,UAAE;AAAA,WACd,GAEJ;AAAA;AAAA;AAAA,EACF;AAEJ;","names":["Link"]}
|
|
@@ -6,11 +6,7 @@ interface LaunchpadCtaBannerProps {
|
|
|
6
6
|
description: string;
|
|
7
7
|
href: string;
|
|
8
8
|
ctaLabel: string;
|
|
9
|
-
/** External links render a plain <a target="_blank"> with an ExternalLink
|
|
10
|
-
* icon; internal links route through next/link with an ArrowRight icon. */
|
|
11
9
|
external?: boolean;
|
|
12
|
-
/** Swaps the background gradient tint — "primary" for cross-app CTAs,
|
|
13
|
-
* "manage" for account/portfolio-management CTAs. */
|
|
14
10
|
tone?: "primary" | "manage";
|
|
15
11
|
className?: string;
|
|
16
12
|
}
|
|
@@ -6,11 +6,7 @@ interface LaunchpadCtaBannerProps {
|
|
|
6
6
|
description: string;
|
|
7
7
|
href: string;
|
|
8
8
|
ctaLabel: string;
|
|
9
|
-
/** External links render a plain <a target="_blank"> with an ExternalLink
|
|
10
|
-
* icon; internal links route through next/link with an ArrowRight icon. */
|
|
11
9
|
external?: boolean;
|
|
12
|
-
/** Swaps the background gradient tint — "primary" for cross-app CTAs,
|
|
13
|
-
* "manage" for account/portfolio-management CTAs. */
|
|
14
10
|
tone?: "primary" | "manage";
|
|
15
11
|
className?: string;
|
|
16
12
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/components/launchpad-cta-banner.tsx"],"sourcesContent":["import type { ReactNode } from \"react\";\nimport { ArrowRight, ExternalLink } from \"lucide-react\";\nimport Link from \"./link.js\";\nimport { Button } from \"./button.js\";\nimport { cn } from \"../utils/cn.js\";\n\nexport interface LaunchpadCtaBannerProps {\n eyebrow: string;\n title: string;\n description: string;\n href: string;\n ctaLabel: string;\n
|
|
1
|
+
{"version":3,"sources":["../../src/components/launchpad-cta-banner.tsx"],"sourcesContent":["import type { ReactNode } from \"react\";\nimport { ArrowRight, ExternalLink } from \"lucide-react\";\nimport Link from \"./link.js\";\nimport { Button } from \"./button.js\";\nimport { cn } from \"../utils/cn.js\";\n\nexport interface LaunchpadCtaBannerProps {\n eyebrow: string;\n title: string;\n description: string;\n href: string;\n ctaLabel: string;\n\n external?: boolean;\n\n tone?: \"primary\" | \"manage\";\n className?: string;\n}\n\nconst TONE_GRADIENT: Record<NonNullable<LaunchpadCtaBannerProps[\"tone\"]>, string> = {\n primary: \"bg-gradient-to-r from-brand-blue/10 to-brand-purple/10\",\n manage: \"bg-gradient-to-r from-brand-navy/10 to-brand-purple/10\",\n};\n\nexport function LaunchpadCtaBanner({\n eyebrow,\n title,\n description,\n href,\n ctaLabel,\n external = false,\n tone = \"primary\",\n className,\n}: LaunchpadCtaBannerProps) {\n const icon: ReactNode = external\n ? <ExternalLink className=\"h-3.5 w-3.5 ml-1.5\" />\n : <ArrowRight className=\"h-3.5 w-3.5 ml-1.5\" />;\n\n return (\n <div\n className={cn(\n \"rounded-2xl border border-border/40 p-5 flex flex-col sm:flex-row items-start sm:items-center justify-between gap-4\",\n TONE_GRADIENT[tone],\n className\n )}\n >\n <div>\n <p className=\"section-label\">{eyebrow}</p>\n <p className=\"font-bold text-base mt-0.5\">{title}</p>\n <p className=\"text-sm text-muted-foreground mt-1\">{description}</p>\n </div>\n <Button variant=\"outline\" asChild className=\"shrink-0\">\n {external ? (\n <a href={href} target=\"_blank\" rel=\"noopener noreferrer\">\n {ctaLabel} {icon}\n </a>\n ) : (\n <Link href={href}>\n {ctaLabel} {icon}\n </Link>\n )}\n </Button>\n </div>\n );\n}\n"],"mappings":"AAmCM,cAWA,YAXA;AAlCN,SAAS,YAAY,oBAAoB;AACzC,OAAO,UAAU;AACjB,SAAS,cAAc;AACvB,SAAS,UAAU;AAenB,MAAM,gBAA8E;AAAA,EAClF,SAAS;AAAA,EACT,QAAQ;AACV;AAEO,SAAS,mBAAmB;AAAA,EACjC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,WAAW;AAAA,EACX,OAAO;AAAA,EACP;AACF,GAA4B;AAC1B,QAAM,OAAkB,WACpB,oBAAC,gBAAa,WAAU,sBAAqB,IAC7C,oBAAC,cAAW,WAAU,sBAAqB;AAE/C,SACE;AAAA,IAAC;AAAA;AAAA,MACC,WAAW;AAAA,QACT;AAAA,QACA,cAAc,IAAI;AAAA,QAClB;AAAA,MACF;AAAA,MAEA;AAAA,6BAAC,SACC;AAAA,8BAAC,OAAE,WAAU,iBAAiB,mBAAQ;AAAA,UACtC,oBAAC,OAAE,WAAU,8BAA8B,iBAAM;AAAA,UACjD,oBAAC,OAAE,WAAU,sCAAsC,uBAAY;AAAA,WACjE;AAAA,QACA,oBAAC,UAAO,SAAQ,WAAU,SAAO,MAAC,WAAU,YACzC,qBACC,qBAAC,OAAE,MAAY,QAAO,UAAS,KAAI,uBAChC;AAAA;AAAA,UAAS;AAAA,UAAE;AAAA,WACd,IAEA,qBAAC,QAAK,MACH;AAAA;AAAA,UAAS;AAAA,UAAE;AAAA,WACd,GAEJ;AAAA;AAAA;AAAA,EACF;AAEJ;","names":[]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/components/launchpad-services.tsx"],"sourcesContent":["\"use client\";\n\nimport { useEffect, useMemo, useState } from \"react\";\nimport Link from \"./link.js\";\nimport { AnimatePresence, motion, useReducedMotion } from \"framer-motion\";\nimport { Lock, ArrowRight, Check } from \"lucide-react\";\nimport { cn } from \"../utils/cn.js\";\nimport {\n LAUNCHPAD_SERVICE_DEFINITIONS,\n LAUNCHPAD_SERVICE_GROUPS,\n type ServiceDefinition,\n type ServiceGroup,\n type ServiceGroupDefinition,\n type ServiceStatus,\n} from \"../data/launchpad-services.js\";\n\nexport interface ServiceOverride {\n\n href?: string;\n\n browseHref?: string;\n\n status?: ServiceStatus;\n\n blurb?: string;\n\n meta?: string;\n}\n\nexport type ServiceOverrides = Record<string, ServiceOverride>;\n\n/** The launchpad route structure both apps share (`/launchpad/...`,\n * `/claim/...`) — spread this into an app's own overrides and add\n * `meta` counts or any app-specific extras on top. */\nexport const LAUNCHPAD_ROUTE_OVERRIDES: ServiceOverrides = {\n \"nfts\": { href: \"/launchpad/single-editions\" },\n \"create-collection\": { href: \"/launchpad/single-editions/collection\" },\n \"limited-editions\": { href: \"/launchpad/nfteditions\" },\n \"create-1155\": { href: \"/launchpad/nfteditions/create\" },\n \"remix-asset\": { href: \"/launchpad/remix\" },\n \"pop-protocol\": { href: \"/launchpad/pop\" },\n \"collection-drop\": { href: \"/launchpad/drop\" },\n \"creator-coins\": { href: \"/launchpad/coin/create\" },\n \"claim-memecoin\": { href: \"/launchpad/memecoin\" },\n \"claim-username\": { href: \"/claim/username\" },\n \"claim-collection\": { href: \"/claim/collection\" },\n \"claim-collection-name\": { href: \"/claim/collection-name\" },\n \"ip-tickets\": { href: \"/launchpad/tickets\" },\n \"ip-club\": { href: \"/launchpad/club\" },\n \"ip-sponsorship\": { href: \"/launchpad/sponsorship\" },\n};\n\ninterface GroupSlice {\n\n tint: string;\n\n text: string;\n\n border: string;\n}\n\nconst DEFAULT_SLICE: GroupSlice = {\n tint: \"bg-muted/60\",\n text: \"text-muted-foreground/60\",\n border: \"border-border/60\",\n};\n\nexport const GROUP_SLICES: Record<ServiceGroup, GroupSlice> = {\n \"nfts\": { tint: \"bg-brand-blue/10\", text: \"text-brand-blue\", border: \"border-brand-blue/30\" },\n \"limited-editions\": { tint: \"bg-brand-maeve/10\", text: \"text-brand-maeve\", border: \"border-brand-maeve/30\" },\n \"coins\": { tint: \"bg-brand-purple/10\", text: \"text-brand-purple\", border: \"border-brand-purple/30\" },\n \"community\": { tint: \"bg-brand-rose/10\", text: \"text-brand-rose\", border: \"border-brand-rose/30\" },\n \"claims\": { tint: \"bg-brand-orange/10\", text: \"text-brand-orange\", border: \"border-brand-orange/30\" },\n \"coming-soon\": DEFAULT_SLICE,\n};\n\ninterface ServiceHue {\n text: string;\n solid: string;\n border: string;\n tint: string;\n}\n\nexport const SERVICE_HUES: Record<string, ServiceHue> = {\n \"nfts\": { text: \"text-brand-blue\", solid: \"bg-brand-blue\", border: \"border-brand-blue/30\", tint: \"bg-brand-blue/10\" },\n \"limited-editions\": { text: \"text-brand-maeve\", solid: \"bg-brand-maeve\", border: \"border-brand-maeve/30\", tint: \"bg-brand-maeve/10\" },\n \"creator-coins\": { text: \"text-brand-maeve\", solid: \"bg-brand-maeve\", border: \"border-brand-maeve/30\", tint: \"bg-brand-maeve/10\" },\n \"claim-memecoin\": { text: \"text-brand-orange\", solid: \"bg-brand-orange\", border: \"border-brand-orange/30\", tint: \"bg-brand-orange/10\" },\n \"collection-drop\": { text: \"text-brand-purple\", solid: \"bg-brand-purple\", border: \"border-brand-purple/30\", tint: \"bg-brand-purple/10\" },\n \"pop-protocol\": { text: \"text-brand-rose\", solid: \"bg-brand-rose\", border: \"border-brand-rose/30\", tint: \"bg-brand-rose/10\" },\n \"ip-tickets\": { text: \"text-brand-orange\", solid: \"bg-brand-orange\", border: \"border-brand-orange/30\", tint: \"bg-brand-orange/10\" },\n \"remix-asset\": { text: \"text-brand-maeve\", solid: \"bg-brand-maeve\", border: \"border-brand-maeve/30\", tint: \"bg-brand-maeve/10\" },\n \"claim-username\": { text: \"text-brand-purple\", solid: \"bg-brand-purple\", border: \"border-brand-purple/30\", tint: \"bg-brand-purple/10\" },\n \"claim-collection\": { text: \"text-brand-blue\", solid: \"bg-brand-blue\", border: \"border-brand-blue/30\", tint: \"bg-brand-blue/10\" },\n \"claim-collection-name\": { text: \"text-brand-rose\", solid: \"bg-brand-rose\", border: \"border-brand-rose/30\", tint: \"bg-brand-rose/10\" },\n};\n\nconst GROUP_TITLE_BY_KEY = Object.fromEntries(\n LAUNCHPAD_SERVICE_GROUPS.map((g) => [g.key, g.title]),\n) as Record<ServiceGroup, string>;\n\nconst GROUP_KEYS = new Set(LAUNCHPAD_SERVICE_GROUPS.map((g) => g.key));\n\nexport interface LaunchpadServiceCardProps {\n def: ServiceDefinition;\n override?: ServiceOverride;\n\n index?: number;\n}\n\nexport function LaunchpadServiceCard({ def, override = {}, index = 0 }: LaunchpadServiceCardProps) {\n const { icon: Icon, title, group, features, cta } = def;\n const status = override.status ?? def.status;\n const blurb = override.blurb ?? def.blurb;\n const { href } = override;\n const reduceMotion = useReducedMotion();\n\n const live = status === \"live\";\n const slice = GROUP_SLICES[group] ?? DEFAULT_SLICE;\n\n const body = (\n <>\n\n <div className=\"flex items-start justify-between gap-3\">\n <span className={cn(\"flex h-14 w-14 items-center justify-center rounded-2xl\", live ? slice.tint : \"bg-muted/40\")}>\n <Icon className={cn(\"h-7 w-7\", live ? slice.text : \"text-muted-foreground/50\")} />\n </span>\n {!live ? (\n <span className=\"flex items-center gap-1 text-2xs font-medium text-muted-foreground/60 pt-1\">\n <Lock className=\"h-3 w-3\" />\n Coming soon\n </span>\n ) : override.meta ? (\n <span className=\"text-xs font-semibold tabular-nums px-2.5 py-1 rounded-full bg-muted/50 text-foreground/80\">\n {override.meta}\n </span>\n ) : null}\n </div>\n\n <div className=\"mt-auto space-y-2 pt-6\">\n <h3 className=\"text-2xl font-bold tracking-tight leading-tight\">{title}</h3>\n <p className={cn(\"text-sm leading-relaxed\", live ? \"text-muted-foreground\" : \"text-muted-foreground/60\")}>\n {blurb}\n </p>\n </div>\n\n {live && features.length > 0 && (\n <ul className=\"space-y-1.5 pt-4\">\n {features.slice(0, 3).map((feature) => (\n <li key={feature} className=\"flex items-start gap-2 text-xs text-muted-foreground leading-relaxed\">\n <Check className={cn(\"h-3.5 w-3.5 shrink-0 mt-px\", slice.text)} />\n {feature}\n </li>\n ))}\n </ul>\n )}\n\n {live && (\n <div className=\"pt-5\">\n <span className=\"inline-flex items-center gap-1.5 text-sm font-semibold\">\n {cta}\n <ArrowRight className=\"h-4 w-4 transition-transform duration-200 sm:group-hover:translate-x-0.5 motion-reduce:transform-none\" />\n </span>\n </div>\n )}\n </>\n );\n\n return (\n <motion.div\n layout={!reduceMotion}\n initial={{ opacity: 0, y: reduceMotion ? 0 : 16 }}\n animate={{ opacity: 1, y: 0 }}\n exit={{ opacity: 0 }}\n whileTap={live ? { scale: 0.97 } : undefined}\n transition={{ duration: 0.35, delay: index * 0.04, ease: [0.25, 0.46, 0.45, 0.94] }}\n className=\"flex\"\n >\n {live && href ? (\n <Link\n href={href}\n className={cn(\n \"group flex flex-1 flex-col rounded-3xl border bg-card p-6 sm:aspect-[4/5]\",\n \"ml-grad-hover-border\",\n slice.border,\n )}\n >\n {body}\n </Link>\n ) : (\n <div className=\"flex flex-1 flex-col rounded-3xl border border-border/30 bg-card p-6 sm:aspect-[4/5] opacity-75\">\n {body}\n </div>\n )}\n </motion.div>\n );\n}\n\nfunction ComingSoonStrip({ group, defs }: { group: ServiceGroupDefinition; defs: ServiceDefinition[] }) {\n return (\n <div className=\"rounded-2xl border border-border/40 p-5\">\n <p className=\"font-semibold text-sm\">{group.title}</p>\n <p className=\"text-sm text-muted-foreground mt-0.5\">{group.tagline}</p>\n <div className=\"flex flex-wrap gap-2 mt-4\">\n {defs.map(({ key, icon: Icon, title }) => (\n <div key={key} className=\"flex items-center gap-2 px-3 py-2 rounded-full bg-muted/30 border border-border/25\">\n <Icon className=\"h-3.5 w-3.5 text-muted-foreground/60\" />\n <span className=\"text-xs font-medium text-muted-foreground\">{title}</span>\n </div>\n ))}\n </div>\n </div>\n );\n}\n\nexport function serviceMatchesQuery(def: ServiceDefinition, query: string): boolean {\n if (query.trim() === \"\") return true;\n const haystack = `${def.title} ${def.blurb} ${def.subtitle} ${GROUP_TITLE_BY_KEY[def.group] ?? \"\"}`.toLowerCase();\n return haystack.includes(query.trim().toLowerCase());\n}\n\nfunction syncFilterUrl(query: string, groups: Set<ServiceGroup>) {\n if (typeof window === \"undefined\") return;\n const params = new URLSearchParams(window.location.search);\n if (query.trim()) params.set(\"q\", query.trim());\n else params.delete(\"q\");\n if (groups.size > 0) params.set(\"groups\", [...groups].join(\",\"));\n else params.delete(\"groups\");\n const qs = params.toString();\n window.history.replaceState(null, \"\", qs ? `${window.location.pathname}?${qs}` : window.location.pathname);\n}\n\nexport function useLaunchpadFilter() {\n const [query, setQueryState] = useState(\"\");\n const [activeGroups, setActiveGroups] = useState<Set<ServiceGroup>>(new Set());\n\n useEffect(() => {\n const params = new URLSearchParams(window.location.search);\n const q = params.get(\"q\");\n if (q) setQueryState(q);\n const g = params.get(\"groups\");\n if (g) {\n const groups = g.split(\",\").filter((k): k is ServiceGroup => GROUP_KEYS.has(k as ServiceGroup));\n if (groups.length > 0) setActiveGroups(new Set(groups));\n }\n }, []);\n\n const filterableGroups = LAUNCHPAD_SERVICE_GROUPS.filter((g) => g.key !== \"coming-soon\");\n\n const setQuery = (value: string) => {\n setQueryState(value);\n syncFilterUrl(value, activeGroups);\n };\n\n const toggleGroup = (key: ServiceGroup) => {\n setActiveGroups((prev) => {\n const next = new Set(prev);\n if (next.has(key)) next.delete(key);\n else next.add(key);\n syncFilterUrl(query, next);\n return next;\n });\n };\n\n const matches = (def: ServiceDefinition): boolean => {\n if (activeGroups.size > 0 && !activeGroups.has(def.group)) return false;\n if (def.status !== \"live\") return false;\n return serviceMatchesQuery(def, query);\n };\n\n const totalMatches = useMemo(\n () => LAUNCHPAD_SERVICE_DEFINITIONS.filter(matches).length,\n\n [query, activeGroups],\n );\n\n const clear = () => {\n setQueryState(\"\");\n setActiveGroups(new Set());\n syncFilterUrl(\"\", new Set());\n };\n\n return { query, setQuery, activeGroups, toggleGroup, filterableGroups, totalMatches, clear };\n}\n\nexport interface LaunchpadGroupedSectionsProps {\n\n overrides: ServiceOverrides;\n\n query: string;\n\n activeGroups: Set<ServiceGroup>;\n\n onClearFilters: () => void;\n className?: string;\n\n excludeKeys?: string[];\n}\n\nexport function LaunchpadGroupedSections({ overrides, query, activeGroups, onClearFilters, className, excludeKeys }: LaunchpadGroupedSectionsProps) {\n const inActiveGroup = (d: ServiceDefinition) => activeGroups.size === 0 || activeGroups.has(d.group);\n const inSearch = (d: ServiceDefinition) => serviceMatchesQuery(d, query);\n const notExcluded = (d: ServiceDefinition) => !excludeKeys?.includes(d.key);\n\n const groupOrder = Object.fromEntries(LAUNCHPAD_SERVICE_GROUPS.map((g, i) => [g.key, i]));\n\n const liveDefs = LAUNCHPAD_SERVICE_DEFINITIONS\n .filter((d) => d.group !== \"coming-soon\" && d.status === \"live\" && notExcluded(d) && inActiveGroup(d) && inSearch(d))\n .sort((a, b) => (groupOrder[a.group] ?? 99) - (groupOrder[b.group] ?? 99));\n\n const comingSoonGroup = LAUNCHPAD_SERVICE_GROUPS.find((g) => g.key === \"coming-soon\");\n const comingSoonDefs = LAUNCHPAD_SERVICE_DEFINITIONS.filter(\n (d) => d.group === \"coming-soon\" && notExcluded(d) && inActiveGroup(d) && inSearch(d),\n );\n\n return (\n <div className={cn(\"space-y-8\", className)}>\n {liveDefs.length === 0 && comingSoonDefs.length === 0 ? (\n <div className=\"text-center py-16 space-y-3\">\n <p className=\"text-lg font-semibold\">No services match</p>\n <p className=\"text-sm text-muted-foreground\">Try a different search or clear your filters.</p>\n <button\n type=\"button\"\n onClick={onClearFilters}\n className=\"inline-flex items-center h-9 px-4 rounded-full text-sm font-semibold bg-primary text-primary-foreground\"\n >\n Clear filters\n </button>\n </div>\n ) : (\n <>\n <motion.div layout className=\"grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 xl:grid-cols-4 gap-5 sm:gap-6\">\n <AnimatePresence mode=\"popLayout\">\n {liveDefs.map((def, i) => (\n <LaunchpadServiceCard key={def.key} def={def} override={overrides[def.key]} index={i} />\n ))}\n </AnimatePresence>\n </motion.div>\n {comingSoonGroup && comingSoonDefs.length > 0 && (\n <ComingSoonStrip group={comingSoonGroup} defs={comingSoonDefs} />\n )}\n </>\n )}\n </div>\n );\n}\n"],"mappings":"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","names":["Link"]}
|
|
1
|
+
{"version":3,"sources":["../../src/components/launchpad-services.tsx"],"sourcesContent":["\"use client\";\n\nimport { useEffect, useMemo, useState } from \"react\";\nimport Link from \"./link.js\";\nimport { AnimatePresence, motion, useReducedMotion } from \"framer-motion\";\nimport { Lock, ArrowRight, Check } from \"lucide-react\";\nimport { cn } from \"../utils/cn.js\";\nimport {\n LAUNCHPAD_SERVICE_DEFINITIONS,\n LAUNCHPAD_SERVICE_GROUPS,\n type ServiceDefinition,\n type ServiceGroup,\n type ServiceGroupDefinition,\n type ServiceStatus,\n} from \"../data/launchpad-services.js\";\n\nexport interface ServiceOverride {\n\n href?: string;\n\n browseHref?: string;\n\n status?: ServiceStatus;\n\n blurb?: string;\n\n meta?: string;\n}\n\nexport type ServiceOverrides = Record<string, ServiceOverride>;\n\nexport const LAUNCHPAD_ROUTE_OVERRIDES: ServiceOverrides = {\n \"nfts\": { href: \"/launchpad/single-editions\" },\n \"create-collection\": { href: \"/launchpad/single-editions/collection\" },\n \"limited-editions\": { href: \"/launchpad/nfteditions\" },\n \"create-1155\": { href: \"/launchpad/nfteditions/create\" },\n \"remix-asset\": { href: \"/launchpad/remix\" },\n \"pop-protocol\": { href: \"/launchpad/pop\" },\n \"collection-drop\": { href: \"/launchpad/drop\" },\n \"creator-coins\": { href: \"/launchpad/coin/create\" },\n \"claim-memecoin\": { href: \"/launchpad/memecoin\" },\n \"claim-username\": { href: \"/claim/username\" },\n \"claim-collection\": { href: \"/claim/collection\" },\n \"claim-collection-name\": { href: \"/claim/collection-name\" },\n \"ip-tickets\": { href: \"/launchpad/tickets\" },\n \"ip-club\": { href: \"/launchpad/club\" },\n \"ip-sponsorship\": { href: \"/launchpad/sponsorship\" },\n};\n\ninterface GroupSlice {\n\n tint: string;\n\n text: string;\n\n border: string;\n}\n\nconst DEFAULT_SLICE: GroupSlice = {\n tint: \"bg-muted/60\",\n text: \"text-muted-foreground/60\",\n border: \"border-border/60\",\n};\n\nexport const GROUP_SLICES: Record<ServiceGroup, GroupSlice> = {\n \"nfts\": { tint: \"bg-brand-blue/10\", text: \"text-brand-blue\", border: \"border-brand-blue/30\" },\n \"limited-editions\": { tint: \"bg-brand-maeve/10\", text: \"text-brand-maeve\", border: \"border-brand-maeve/30\" },\n \"coins\": { tint: \"bg-brand-purple/10\", text: \"text-brand-purple\", border: \"border-brand-purple/30\" },\n \"community\": { tint: \"bg-brand-rose/10\", text: \"text-brand-rose\", border: \"border-brand-rose/30\" },\n \"claims\": { tint: \"bg-brand-orange/10\", text: \"text-brand-orange\", border: \"border-brand-orange/30\" },\n \"coming-soon\": DEFAULT_SLICE,\n};\n\ninterface ServiceHue {\n text: string;\n solid: string;\n border: string;\n tint: string;\n}\n\nexport const SERVICE_HUES: Record<string, ServiceHue> = {\n \"nfts\": { text: \"text-brand-blue\", solid: \"bg-brand-blue\", border: \"border-brand-blue/30\", tint: \"bg-brand-blue/10\" },\n \"limited-editions\": { text: \"text-brand-maeve\", solid: \"bg-brand-maeve\", border: \"border-brand-maeve/30\", tint: \"bg-brand-maeve/10\" },\n \"creator-coins\": { text: \"text-brand-maeve\", solid: \"bg-brand-maeve\", border: \"border-brand-maeve/30\", tint: \"bg-brand-maeve/10\" },\n \"claim-memecoin\": { text: \"text-brand-orange\", solid: \"bg-brand-orange\", border: \"border-brand-orange/30\", tint: \"bg-brand-orange/10\" },\n \"collection-drop\": { text: \"text-brand-purple\", solid: \"bg-brand-purple\", border: \"border-brand-purple/30\", tint: \"bg-brand-purple/10\" },\n \"pop-protocol\": { text: \"text-brand-rose\", solid: \"bg-brand-rose\", border: \"border-brand-rose/30\", tint: \"bg-brand-rose/10\" },\n \"ip-tickets\": { text: \"text-brand-orange\", solid: \"bg-brand-orange\", border: \"border-brand-orange/30\", tint: \"bg-brand-orange/10\" },\n \"remix-asset\": { text: \"text-brand-maeve\", solid: \"bg-brand-maeve\", border: \"border-brand-maeve/30\", tint: \"bg-brand-maeve/10\" },\n \"claim-username\": { text: \"text-brand-purple\", solid: \"bg-brand-purple\", border: \"border-brand-purple/30\", tint: \"bg-brand-purple/10\" },\n \"claim-collection\": { text: \"text-brand-blue\", solid: \"bg-brand-blue\", border: \"border-brand-blue/30\", tint: \"bg-brand-blue/10\" },\n \"claim-collection-name\": { text: \"text-brand-rose\", solid: \"bg-brand-rose\", border: \"border-brand-rose/30\", tint: \"bg-brand-rose/10\" },\n};\n\nconst GROUP_TITLE_BY_KEY = Object.fromEntries(\n LAUNCHPAD_SERVICE_GROUPS.map((g) => [g.key, g.title]),\n) as Record<ServiceGroup, string>;\n\nconst GROUP_KEYS = new Set(LAUNCHPAD_SERVICE_GROUPS.map((g) => g.key));\n\nexport interface LaunchpadServiceCardProps {\n def: ServiceDefinition;\n override?: ServiceOverride;\n\n index?: number;\n}\n\nexport function LaunchpadServiceCard({ def, override = {}, index = 0 }: LaunchpadServiceCardProps) {\n const { icon: Icon, title, group, features, cta } = def;\n const status = override.status ?? def.status;\n const blurb = override.blurb ?? def.blurb;\n const { href } = override;\n const reduceMotion = useReducedMotion();\n\n const live = status === \"live\";\n const slice = GROUP_SLICES[group] ?? DEFAULT_SLICE;\n\n const body = (\n <>\n\n <div className=\"flex items-start justify-between gap-3\">\n <span className={cn(\"flex h-14 w-14 items-center justify-center rounded-2xl\", live ? slice.tint : \"bg-muted/40\")}>\n <Icon className={cn(\"h-7 w-7\", live ? slice.text : \"text-muted-foreground/50\")} />\n </span>\n {!live ? (\n <span className=\"flex items-center gap-1 text-2xs font-medium text-muted-foreground/60 pt-1\">\n <Lock className=\"h-3 w-3\" />\n Coming soon\n </span>\n ) : override.meta ? (\n <span className=\"text-xs font-semibold tabular-nums px-2.5 py-1 rounded-full bg-muted/50 text-foreground/80\">\n {override.meta}\n </span>\n ) : null}\n </div>\n\n <div className=\"mt-auto space-y-2 pt-6\">\n <h3 className=\"text-2xl font-bold tracking-tight leading-tight\">{title}</h3>\n <p className={cn(\"text-sm leading-relaxed\", live ? \"text-muted-foreground\" : \"text-muted-foreground/60\")}>\n {blurb}\n </p>\n </div>\n\n {live && features.length > 0 && (\n <ul className=\"space-y-1.5 pt-4\">\n {features.slice(0, 3).map((feature) => (\n <li key={feature} className=\"flex items-start gap-2 text-xs text-muted-foreground leading-relaxed\">\n <Check className={cn(\"h-3.5 w-3.5 shrink-0 mt-px\", slice.text)} />\n {feature}\n </li>\n ))}\n </ul>\n )}\n\n {live && (\n <div className=\"pt-5\">\n <span className=\"inline-flex items-center gap-1.5 text-sm font-semibold\">\n {cta}\n <ArrowRight className=\"h-4 w-4 transition-transform duration-200 sm:group-hover:translate-x-0.5 motion-reduce:transform-none\" />\n </span>\n </div>\n )}\n </>\n );\n\n return (\n <motion.div\n layout={!reduceMotion}\n initial={{ opacity: 0, y: reduceMotion ? 0 : 16 }}\n animate={{ opacity: 1, y: 0 }}\n exit={{ opacity: 0 }}\n whileTap={live ? { scale: 0.97 } : undefined}\n transition={{ duration: 0.35, delay: index * 0.04, ease: [0.25, 0.46, 0.45, 0.94] }}\n className=\"flex\"\n >\n {live && href ? (\n <Link\n href={href}\n className={cn(\n \"group flex flex-1 flex-col rounded-3xl border bg-card p-6 sm:aspect-[4/5]\",\n \"ml-grad-hover-border\",\n slice.border,\n )}\n >\n {body}\n </Link>\n ) : (\n <div className=\"flex flex-1 flex-col rounded-3xl border border-border/30 bg-card p-6 sm:aspect-[4/5] opacity-75\">\n {body}\n </div>\n )}\n </motion.div>\n );\n}\n\nfunction ComingSoonStrip({ group, defs }: { group: ServiceGroupDefinition; defs: ServiceDefinition[] }) {\n return (\n <div className=\"rounded-2xl border border-border/40 p-5\">\n <p className=\"font-semibold text-sm\">{group.title}</p>\n <p className=\"text-sm text-muted-foreground mt-0.5\">{group.tagline}</p>\n <div className=\"flex flex-wrap gap-2 mt-4\">\n {defs.map(({ key, icon: Icon, title }) => (\n <div key={key} className=\"flex items-center gap-2 px-3 py-2 rounded-full bg-muted/30 border border-border/25\">\n <Icon className=\"h-3.5 w-3.5 text-muted-foreground/60\" />\n <span className=\"text-xs font-medium text-muted-foreground\">{title}</span>\n </div>\n ))}\n </div>\n </div>\n );\n}\n\nexport function serviceMatchesQuery(def: ServiceDefinition, query: string): boolean {\n if (query.trim() === \"\") return true;\n const haystack = `${def.title} ${def.blurb} ${def.subtitle} ${GROUP_TITLE_BY_KEY[def.group] ?? \"\"}`.toLowerCase();\n return haystack.includes(query.trim().toLowerCase());\n}\n\nfunction syncFilterUrl(query: string, groups: Set<ServiceGroup>) {\n if (typeof window === \"undefined\") return;\n const params = new URLSearchParams(window.location.search);\n if (query.trim()) params.set(\"q\", query.trim());\n else params.delete(\"q\");\n if (groups.size > 0) params.set(\"groups\", [...groups].join(\",\"));\n else params.delete(\"groups\");\n const qs = params.toString();\n window.history.replaceState(null, \"\", qs ? `${window.location.pathname}?${qs}` : window.location.pathname);\n}\n\nexport function useLaunchpadFilter() {\n const [query, setQueryState] = useState(\"\");\n const [activeGroups, setActiveGroups] = useState<Set<ServiceGroup>>(new Set());\n\n useEffect(() => {\n const params = new URLSearchParams(window.location.search);\n const q = params.get(\"q\");\n if (q) setQueryState(q);\n const g = params.get(\"groups\");\n if (g) {\n const groups = g.split(\",\").filter((k): k is ServiceGroup => GROUP_KEYS.has(k as ServiceGroup));\n if (groups.length > 0) setActiveGroups(new Set(groups));\n }\n }, []);\n\n const filterableGroups = LAUNCHPAD_SERVICE_GROUPS.filter((g) => g.key !== \"coming-soon\");\n\n const setQuery = (value: string) => {\n setQueryState(value);\n syncFilterUrl(value, activeGroups);\n };\n\n const toggleGroup = (key: ServiceGroup) => {\n setActiveGroups((prev) => {\n const next = new Set(prev);\n if (next.has(key)) next.delete(key);\n else next.add(key);\n syncFilterUrl(query, next);\n return next;\n });\n };\n\n const matches = (def: ServiceDefinition): boolean => {\n if (activeGroups.size > 0 && !activeGroups.has(def.group)) return false;\n if (def.status !== \"live\") return false;\n return serviceMatchesQuery(def, query);\n };\n\n const totalMatches = useMemo(\n () => LAUNCHPAD_SERVICE_DEFINITIONS.filter(matches).length,\n\n [query, activeGroups],\n );\n\n const clear = () => {\n setQueryState(\"\");\n setActiveGroups(new Set());\n syncFilterUrl(\"\", new Set());\n };\n\n return { query, setQuery, activeGroups, toggleGroup, filterableGroups, totalMatches, clear };\n}\n\nexport interface LaunchpadGroupedSectionsProps {\n\n overrides: ServiceOverrides;\n\n query: string;\n\n activeGroups: Set<ServiceGroup>;\n\n onClearFilters: () => void;\n className?: string;\n\n excludeKeys?: string[];\n}\n\nexport function LaunchpadGroupedSections({ overrides, query, activeGroups, onClearFilters, className, excludeKeys }: LaunchpadGroupedSectionsProps) {\n const inActiveGroup = (d: ServiceDefinition) => activeGroups.size === 0 || activeGroups.has(d.group);\n const inSearch = (d: ServiceDefinition) => serviceMatchesQuery(d, query);\n const notExcluded = (d: ServiceDefinition) => !excludeKeys?.includes(d.key);\n\n const groupOrder = Object.fromEntries(LAUNCHPAD_SERVICE_GROUPS.map((g, i) => [g.key, i]));\n\n const liveDefs = LAUNCHPAD_SERVICE_DEFINITIONS\n .filter((d) => d.group !== \"coming-soon\" && d.status === \"live\" && notExcluded(d) && inActiveGroup(d) && inSearch(d))\n .sort((a, b) => (groupOrder[a.group] ?? 99) - (groupOrder[b.group] ?? 99));\n\n const comingSoonGroup = LAUNCHPAD_SERVICE_GROUPS.find((g) => g.key === \"coming-soon\");\n const comingSoonDefs = LAUNCHPAD_SERVICE_DEFINITIONS.filter(\n (d) => d.group === \"coming-soon\" && notExcluded(d) && inActiveGroup(d) && inSearch(d),\n );\n\n return (\n <div className={cn(\"space-y-8\", className)}>\n {liveDefs.length === 0 && comingSoonDefs.length === 0 ? (\n <div className=\"text-center py-16 space-y-3\">\n <p className=\"text-lg font-semibold\">No services match</p>\n <p className=\"text-sm text-muted-foreground\">Try a different search or clear your filters.</p>\n <button\n type=\"button\"\n onClick={onClearFilters}\n className=\"inline-flex items-center h-9 px-4 rounded-full text-sm font-semibold bg-primary text-primary-foreground\"\n >\n Clear filters\n </button>\n </div>\n ) : (\n <>\n <motion.div layout className=\"grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 xl:grid-cols-4 gap-5 sm:gap-6\">\n <AnimatePresence mode=\"popLayout\">\n {liveDefs.map((def, i) => (\n <LaunchpadServiceCard key={def.key} def={def} override={overrides[def.key]} index={i} />\n ))}\n </AnimatePresence>\n </motion.div>\n {comingSoonGroup && comingSoonDefs.length > 0 && (\n <ComingSoonStrip group={comingSoonGroup} defs={comingSoonDefs} />\n )}\n </>\n )}\n </div>\n );\n}\n"],"mappings":"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","names":["Link"]}
|
|
@@ -10,9 +10,6 @@ interface ServiceOverride {
|
|
|
10
10
|
meta?: string;
|
|
11
11
|
}
|
|
12
12
|
type ServiceOverrides = Record<string, ServiceOverride>;
|
|
13
|
-
/** The launchpad route structure both apps share (`/launchpad/...`,
|
|
14
|
-
* `/claim/...`) — spread this into an app's own overrides and add
|
|
15
|
-
* `meta` counts or any app-specific extras on top. */
|
|
16
13
|
declare const LAUNCHPAD_ROUTE_OVERRIDES: ServiceOverrides;
|
|
17
14
|
interface GroupSlice {
|
|
18
15
|
tint: string;
|
|
@@ -10,9 +10,6 @@ interface ServiceOverride {
|
|
|
10
10
|
meta?: string;
|
|
11
11
|
}
|
|
12
12
|
type ServiceOverrides = Record<string, ServiceOverride>;
|
|
13
|
-
/** The launchpad route structure both apps share (`/launchpad/...`,
|
|
14
|
-
* `/claim/...`) — spread this into an app's own overrides and add
|
|
15
|
-
* `meta` counts or any app-specific extras on top. */
|
|
16
13
|
declare const LAUNCHPAD_ROUTE_OVERRIDES: ServiceOverrides;
|
|
17
14
|
interface GroupSlice {
|
|
18
15
|
tint: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/components/launchpad-services.tsx"],"sourcesContent":["\"use client\";\n\nimport { useEffect, useMemo, useState } from \"react\";\nimport Link from \"./link.js\";\nimport { AnimatePresence, motion, useReducedMotion } from \"framer-motion\";\nimport { Lock, ArrowRight, Check } from \"lucide-react\";\nimport { cn } from \"../utils/cn.js\";\nimport {\n LAUNCHPAD_SERVICE_DEFINITIONS,\n LAUNCHPAD_SERVICE_GROUPS,\n type ServiceDefinition,\n type ServiceGroup,\n type ServiceGroupDefinition,\n type ServiceStatus,\n} from \"../data/launchpad-services.js\";\n\nexport interface ServiceOverride {\n\n href?: string;\n\n browseHref?: string;\n\n status?: ServiceStatus;\n\n blurb?: string;\n\n meta?: string;\n}\n\nexport type ServiceOverrides = Record<string, ServiceOverride>;\n\n/** The launchpad route structure both apps share (`/launchpad/...`,\n * `/claim/...`) — spread this into an app's own overrides and add\n * `meta` counts or any app-specific extras on top. */\nexport const LAUNCHPAD_ROUTE_OVERRIDES: ServiceOverrides = {\n \"nfts\": { href: \"/launchpad/single-editions\" },\n \"create-collection\": { href: \"/launchpad/single-editions/collection\" },\n \"limited-editions\": { href: \"/launchpad/nfteditions\" },\n \"create-1155\": { href: \"/launchpad/nfteditions/create\" },\n \"remix-asset\": { href: \"/launchpad/remix\" },\n \"pop-protocol\": { href: \"/launchpad/pop\" },\n \"collection-drop\": { href: \"/launchpad/drop\" },\n \"creator-coins\": { href: \"/launchpad/coin/create\" },\n \"claim-memecoin\": { href: \"/launchpad/memecoin\" },\n \"claim-username\": { href: \"/claim/username\" },\n \"claim-collection\": { href: \"/claim/collection\" },\n \"claim-collection-name\": { href: \"/claim/collection-name\" },\n \"ip-tickets\": { href: \"/launchpad/tickets\" },\n \"ip-club\": { href: \"/launchpad/club\" },\n \"ip-sponsorship\": { href: \"/launchpad/sponsorship\" },\n};\n\ninterface GroupSlice {\n\n tint: string;\n\n text: string;\n\n border: string;\n}\n\nconst DEFAULT_SLICE: GroupSlice = {\n tint: \"bg-muted/60\",\n text: \"text-muted-foreground/60\",\n border: \"border-border/60\",\n};\n\nexport const GROUP_SLICES: Record<ServiceGroup, GroupSlice> = {\n \"nfts\": { tint: \"bg-brand-blue/10\", text: \"text-brand-blue\", border: \"border-brand-blue/30\" },\n \"limited-editions\": { tint: \"bg-brand-maeve/10\", text: \"text-brand-maeve\", border: \"border-brand-maeve/30\" },\n \"coins\": { tint: \"bg-brand-purple/10\", text: \"text-brand-purple\", border: \"border-brand-purple/30\" },\n \"community\": { tint: \"bg-brand-rose/10\", text: \"text-brand-rose\", border: \"border-brand-rose/30\" },\n \"claims\": { tint: \"bg-brand-orange/10\", text: \"text-brand-orange\", border: \"border-brand-orange/30\" },\n \"coming-soon\": DEFAULT_SLICE,\n};\n\ninterface ServiceHue {\n text: string;\n solid: string;\n border: string;\n tint: string;\n}\n\nexport const SERVICE_HUES: Record<string, ServiceHue> = {\n \"nfts\": { text: \"text-brand-blue\", solid: \"bg-brand-blue\", border: \"border-brand-blue/30\", tint: \"bg-brand-blue/10\" },\n \"limited-editions\": { text: \"text-brand-maeve\", solid: \"bg-brand-maeve\", border: \"border-brand-maeve/30\", tint: \"bg-brand-maeve/10\" },\n \"creator-coins\": { text: \"text-brand-maeve\", solid: \"bg-brand-maeve\", border: \"border-brand-maeve/30\", tint: \"bg-brand-maeve/10\" },\n \"claim-memecoin\": { text: \"text-brand-orange\", solid: \"bg-brand-orange\", border: \"border-brand-orange/30\", tint: \"bg-brand-orange/10\" },\n \"collection-drop\": { text: \"text-brand-purple\", solid: \"bg-brand-purple\", border: \"border-brand-purple/30\", tint: \"bg-brand-purple/10\" },\n \"pop-protocol\": { text: \"text-brand-rose\", solid: \"bg-brand-rose\", border: \"border-brand-rose/30\", tint: \"bg-brand-rose/10\" },\n \"ip-tickets\": { text: \"text-brand-orange\", solid: \"bg-brand-orange\", border: \"border-brand-orange/30\", tint: \"bg-brand-orange/10\" },\n \"remix-asset\": { text: \"text-brand-maeve\", solid: \"bg-brand-maeve\", border: \"border-brand-maeve/30\", tint: \"bg-brand-maeve/10\" },\n \"claim-username\": { text: \"text-brand-purple\", solid: \"bg-brand-purple\", border: \"border-brand-purple/30\", tint: \"bg-brand-purple/10\" },\n \"claim-collection\": { text: \"text-brand-blue\", solid: \"bg-brand-blue\", border: \"border-brand-blue/30\", tint: \"bg-brand-blue/10\" },\n \"claim-collection-name\": { text: \"text-brand-rose\", solid: \"bg-brand-rose\", border: \"border-brand-rose/30\", tint: \"bg-brand-rose/10\" },\n};\n\nconst GROUP_TITLE_BY_KEY = Object.fromEntries(\n LAUNCHPAD_SERVICE_GROUPS.map((g) => [g.key, g.title]),\n) as Record<ServiceGroup, string>;\n\nconst GROUP_KEYS = new Set(LAUNCHPAD_SERVICE_GROUPS.map((g) => g.key));\n\nexport interface LaunchpadServiceCardProps {\n def: ServiceDefinition;\n override?: ServiceOverride;\n\n index?: number;\n}\n\nexport function LaunchpadServiceCard({ def, override = {}, index = 0 }: LaunchpadServiceCardProps) {\n const { icon: Icon, title, group, features, cta } = def;\n const status = override.status ?? def.status;\n const blurb = override.blurb ?? def.blurb;\n const { href } = override;\n const reduceMotion = useReducedMotion();\n\n const live = status === \"live\";\n const slice = GROUP_SLICES[group] ?? DEFAULT_SLICE;\n\n const body = (\n <>\n\n <div className=\"flex items-start justify-between gap-3\">\n <span className={cn(\"flex h-14 w-14 items-center justify-center rounded-2xl\", live ? slice.tint : \"bg-muted/40\")}>\n <Icon className={cn(\"h-7 w-7\", live ? slice.text : \"text-muted-foreground/50\")} />\n </span>\n {!live ? (\n <span className=\"flex items-center gap-1 text-2xs font-medium text-muted-foreground/60 pt-1\">\n <Lock className=\"h-3 w-3\" />\n Coming soon\n </span>\n ) : override.meta ? (\n <span className=\"text-xs font-semibold tabular-nums px-2.5 py-1 rounded-full bg-muted/50 text-foreground/80\">\n {override.meta}\n </span>\n ) : null}\n </div>\n\n <div className=\"mt-auto space-y-2 pt-6\">\n <h3 className=\"text-2xl font-bold tracking-tight leading-tight\">{title}</h3>\n <p className={cn(\"text-sm leading-relaxed\", live ? \"text-muted-foreground\" : \"text-muted-foreground/60\")}>\n {blurb}\n </p>\n </div>\n\n {live && features.length > 0 && (\n <ul className=\"space-y-1.5 pt-4\">\n {features.slice(0, 3).map((feature) => (\n <li key={feature} className=\"flex items-start gap-2 text-xs text-muted-foreground leading-relaxed\">\n <Check className={cn(\"h-3.5 w-3.5 shrink-0 mt-px\", slice.text)} />\n {feature}\n </li>\n ))}\n </ul>\n )}\n\n {live && (\n <div className=\"pt-5\">\n <span className=\"inline-flex items-center gap-1.5 text-sm font-semibold\">\n {cta}\n <ArrowRight className=\"h-4 w-4 transition-transform duration-200 sm:group-hover:translate-x-0.5 motion-reduce:transform-none\" />\n </span>\n </div>\n )}\n </>\n );\n\n return (\n <motion.div\n layout={!reduceMotion}\n initial={{ opacity: 0, y: reduceMotion ? 0 : 16 }}\n animate={{ opacity: 1, y: 0 }}\n exit={{ opacity: 0 }}\n whileTap={live ? { scale: 0.97 } : undefined}\n transition={{ duration: 0.35, delay: index * 0.04, ease: [0.25, 0.46, 0.45, 0.94] }}\n className=\"flex\"\n >\n {live && href ? (\n <Link\n href={href}\n className={cn(\n \"group flex flex-1 flex-col rounded-3xl border bg-card p-6 sm:aspect-[4/5]\",\n \"ml-grad-hover-border\",\n slice.border,\n )}\n >\n {body}\n </Link>\n ) : (\n <div className=\"flex flex-1 flex-col rounded-3xl border border-border/30 bg-card p-6 sm:aspect-[4/5] opacity-75\">\n {body}\n </div>\n )}\n </motion.div>\n );\n}\n\nfunction ComingSoonStrip({ group, defs }: { group: ServiceGroupDefinition; defs: ServiceDefinition[] }) {\n return (\n <div className=\"rounded-2xl border border-border/40 p-5\">\n <p className=\"font-semibold text-sm\">{group.title}</p>\n <p className=\"text-sm text-muted-foreground mt-0.5\">{group.tagline}</p>\n <div className=\"flex flex-wrap gap-2 mt-4\">\n {defs.map(({ key, icon: Icon, title }) => (\n <div key={key} className=\"flex items-center gap-2 px-3 py-2 rounded-full bg-muted/30 border border-border/25\">\n <Icon className=\"h-3.5 w-3.5 text-muted-foreground/60\" />\n <span className=\"text-xs font-medium text-muted-foreground\">{title}</span>\n </div>\n ))}\n </div>\n </div>\n );\n}\n\nexport function serviceMatchesQuery(def: ServiceDefinition, query: string): boolean {\n if (query.trim() === \"\") return true;\n const haystack = `${def.title} ${def.blurb} ${def.subtitle} ${GROUP_TITLE_BY_KEY[def.group] ?? \"\"}`.toLowerCase();\n return haystack.includes(query.trim().toLowerCase());\n}\n\nfunction syncFilterUrl(query: string, groups: Set<ServiceGroup>) {\n if (typeof window === \"undefined\") return;\n const params = new URLSearchParams(window.location.search);\n if (query.trim()) params.set(\"q\", query.trim());\n else params.delete(\"q\");\n if (groups.size > 0) params.set(\"groups\", [...groups].join(\",\"));\n else params.delete(\"groups\");\n const qs = params.toString();\n window.history.replaceState(null, \"\", qs ? `${window.location.pathname}?${qs}` : window.location.pathname);\n}\n\nexport function useLaunchpadFilter() {\n const [query, setQueryState] = useState(\"\");\n const [activeGroups, setActiveGroups] = useState<Set<ServiceGroup>>(new Set());\n\n useEffect(() => {\n const params = new URLSearchParams(window.location.search);\n const q = params.get(\"q\");\n if (q) setQueryState(q);\n const g = params.get(\"groups\");\n if (g) {\n const groups = g.split(\",\").filter((k): k is ServiceGroup => GROUP_KEYS.has(k as ServiceGroup));\n if (groups.length > 0) setActiveGroups(new Set(groups));\n }\n }, []);\n\n const filterableGroups = LAUNCHPAD_SERVICE_GROUPS.filter((g) => g.key !== \"coming-soon\");\n\n const setQuery = (value: string) => {\n setQueryState(value);\n syncFilterUrl(value, activeGroups);\n };\n\n const toggleGroup = (key: ServiceGroup) => {\n setActiveGroups((prev) => {\n const next = new Set(prev);\n if (next.has(key)) next.delete(key);\n else next.add(key);\n syncFilterUrl(query, next);\n return next;\n });\n };\n\n const matches = (def: ServiceDefinition): boolean => {\n if (activeGroups.size > 0 && !activeGroups.has(def.group)) return false;\n if (def.status !== \"live\") return false;\n return serviceMatchesQuery(def, query);\n };\n\n const totalMatches = useMemo(\n () => LAUNCHPAD_SERVICE_DEFINITIONS.filter(matches).length,\n\n [query, activeGroups],\n );\n\n const clear = () => {\n setQueryState(\"\");\n setActiveGroups(new Set());\n syncFilterUrl(\"\", new Set());\n };\n\n return { query, setQuery, activeGroups, toggleGroup, filterableGroups, totalMatches, clear };\n}\n\nexport interface LaunchpadGroupedSectionsProps {\n\n overrides: ServiceOverrides;\n\n query: string;\n\n activeGroups: Set<ServiceGroup>;\n\n onClearFilters: () => void;\n className?: string;\n\n excludeKeys?: string[];\n}\n\nexport function LaunchpadGroupedSections({ overrides, query, activeGroups, onClearFilters, className, excludeKeys }: LaunchpadGroupedSectionsProps) {\n const inActiveGroup = (d: ServiceDefinition) => activeGroups.size === 0 || activeGroups.has(d.group);\n const inSearch = (d: ServiceDefinition) => serviceMatchesQuery(d, query);\n const notExcluded = (d: ServiceDefinition) => !excludeKeys?.includes(d.key);\n\n const groupOrder = Object.fromEntries(LAUNCHPAD_SERVICE_GROUPS.map((g, i) => [g.key, i]));\n\n const liveDefs = LAUNCHPAD_SERVICE_DEFINITIONS\n .filter((d) => d.group !== \"coming-soon\" && d.status === \"live\" && notExcluded(d) && inActiveGroup(d) && inSearch(d))\n .sort((a, b) => (groupOrder[a.group] ?? 99) - (groupOrder[b.group] ?? 99));\n\n const comingSoonGroup = LAUNCHPAD_SERVICE_GROUPS.find((g) => g.key === \"coming-soon\");\n const comingSoonDefs = LAUNCHPAD_SERVICE_DEFINITIONS.filter(\n (d) => d.group === \"coming-soon\" && notExcluded(d) && inActiveGroup(d) && inSearch(d),\n );\n\n return (\n <div className={cn(\"space-y-8\", className)}>\n {liveDefs.length === 0 && comingSoonDefs.length === 0 ? (\n <div className=\"text-center py-16 space-y-3\">\n <p className=\"text-lg font-semibold\">No services match</p>\n <p className=\"text-sm text-muted-foreground\">Try a different search or clear your filters.</p>\n <button\n type=\"button\"\n onClick={onClearFilters}\n className=\"inline-flex items-center h-9 px-4 rounded-full text-sm font-semibold bg-primary text-primary-foreground\"\n >\n Clear filters\n </button>\n </div>\n ) : (\n <>\n <motion.div layout className=\"grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 xl:grid-cols-4 gap-5 sm:gap-6\">\n <AnimatePresence mode=\"popLayout\">\n {liveDefs.map((def, i) => (\n <LaunchpadServiceCard key={def.key} def={def} override={overrides[def.key]} index={i} />\n ))}\n </AnimatePresence>\n </motion.div>\n {comingSoonGroup && comingSoonDefs.length > 0 && (\n <ComingSoonStrip group={comingSoonGroup} defs={comingSoonDefs} />\n )}\n </>\n )}\n </div>\n );\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../src/components/launchpad-services.tsx"],"sourcesContent":["\"use client\";\n\nimport { useEffect, useMemo, useState } from \"react\";\nimport Link from \"./link.js\";\nimport { AnimatePresence, motion, useReducedMotion } from \"framer-motion\";\nimport { Lock, ArrowRight, Check } from \"lucide-react\";\nimport { cn } from \"../utils/cn.js\";\nimport {\n LAUNCHPAD_SERVICE_DEFINITIONS,\n LAUNCHPAD_SERVICE_GROUPS,\n type ServiceDefinition,\n type ServiceGroup,\n type ServiceGroupDefinition,\n type ServiceStatus,\n} from \"../data/launchpad-services.js\";\n\nexport interface ServiceOverride {\n\n href?: string;\n\n browseHref?: string;\n\n status?: ServiceStatus;\n\n blurb?: string;\n\n meta?: string;\n}\n\nexport type ServiceOverrides = Record<string, ServiceOverride>;\n\nexport const LAUNCHPAD_ROUTE_OVERRIDES: ServiceOverrides = {\n \"nfts\": { href: \"/launchpad/single-editions\" },\n \"create-collection\": { href: \"/launchpad/single-editions/collection\" },\n \"limited-editions\": { href: \"/launchpad/nfteditions\" },\n \"create-1155\": { href: \"/launchpad/nfteditions/create\" },\n \"remix-asset\": { href: \"/launchpad/remix\" },\n \"pop-protocol\": { href: \"/launchpad/pop\" },\n \"collection-drop\": { href: \"/launchpad/drop\" },\n \"creator-coins\": { href: \"/launchpad/coin/create\" },\n \"claim-memecoin\": { href: \"/launchpad/memecoin\" },\n \"claim-username\": { href: \"/claim/username\" },\n \"claim-collection\": { href: \"/claim/collection\" },\n \"claim-collection-name\": { href: \"/claim/collection-name\" },\n \"ip-tickets\": { href: \"/launchpad/tickets\" },\n \"ip-club\": { href: \"/launchpad/club\" },\n \"ip-sponsorship\": { href: \"/launchpad/sponsorship\" },\n};\n\ninterface GroupSlice {\n\n tint: string;\n\n text: string;\n\n border: string;\n}\n\nconst DEFAULT_SLICE: GroupSlice = {\n tint: \"bg-muted/60\",\n text: \"text-muted-foreground/60\",\n border: \"border-border/60\",\n};\n\nexport const GROUP_SLICES: Record<ServiceGroup, GroupSlice> = {\n \"nfts\": { tint: \"bg-brand-blue/10\", text: \"text-brand-blue\", border: \"border-brand-blue/30\" },\n \"limited-editions\": { tint: \"bg-brand-maeve/10\", text: \"text-brand-maeve\", border: \"border-brand-maeve/30\" },\n \"coins\": { tint: \"bg-brand-purple/10\", text: \"text-brand-purple\", border: \"border-brand-purple/30\" },\n \"community\": { tint: \"bg-brand-rose/10\", text: \"text-brand-rose\", border: \"border-brand-rose/30\" },\n \"claims\": { tint: \"bg-brand-orange/10\", text: \"text-brand-orange\", border: \"border-brand-orange/30\" },\n \"coming-soon\": DEFAULT_SLICE,\n};\n\ninterface ServiceHue {\n text: string;\n solid: string;\n border: string;\n tint: string;\n}\n\nexport const SERVICE_HUES: Record<string, ServiceHue> = {\n \"nfts\": { text: \"text-brand-blue\", solid: \"bg-brand-blue\", border: \"border-brand-blue/30\", tint: \"bg-brand-blue/10\" },\n \"limited-editions\": { text: \"text-brand-maeve\", solid: \"bg-brand-maeve\", border: \"border-brand-maeve/30\", tint: \"bg-brand-maeve/10\" },\n \"creator-coins\": { text: \"text-brand-maeve\", solid: \"bg-brand-maeve\", border: \"border-brand-maeve/30\", tint: \"bg-brand-maeve/10\" },\n \"claim-memecoin\": { text: \"text-brand-orange\", solid: \"bg-brand-orange\", border: \"border-brand-orange/30\", tint: \"bg-brand-orange/10\" },\n \"collection-drop\": { text: \"text-brand-purple\", solid: \"bg-brand-purple\", border: \"border-brand-purple/30\", tint: \"bg-brand-purple/10\" },\n \"pop-protocol\": { text: \"text-brand-rose\", solid: \"bg-brand-rose\", border: \"border-brand-rose/30\", tint: \"bg-brand-rose/10\" },\n \"ip-tickets\": { text: \"text-brand-orange\", solid: \"bg-brand-orange\", border: \"border-brand-orange/30\", tint: \"bg-brand-orange/10\" },\n \"remix-asset\": { text: \"text-brand-maeve\", solid: \"bg-brand-maeve\", border: \"border-brand-maeve/30\", tint: \"bg-brand-maeve/10\" },\n \"claim-username\": { text: \"text-brand-purple\", solid: \"bg-brand-purple\", border: \"border-brand-purple/30\", tint: \"bg-brand-purple/10\" },\n \"claim-collection\": { text: \"text-brand-blue\", solid: \"bg-brand-blue\", border: \"border-brand-blue/30\", tint: \"bg-brand-blue/10\" },\n \"claim-collection-name\": { text: \"text-brand-rose\", solid: \"bg-brand-rose\", border: \"border-brand-rose/30\", tint: \"bg-brand-rose/10\" },\n};\n\nconst GROUP_TITLE_BY_KEY = Object.fromEntries(\n LAUNCHPAD_SERVICE_GROUPS.map((g) => [g.key, g.title]),\n) as Record<ServiceGroup, string>;\n\nconst GROUP_KEYS = new Set(LAUNCHPAD_SERVICE_GROUPS.map((g) => g.key));\n\nexport interface LaunchpadServiceCardProps {\n def: ServiceDefinition;\n override?: ServiceOverride;\n\n index?: number;\n}\n\nexport function LaunchpadServiceCard({ def, override = {}, index = 0 }: LaunchpadServiceCardProps) {\n const { icon: Icon, title, group, features, cta } = def;\n const status = override.status ?? def.status;\n const blurb = override.blurb ?? def.blurb;\n const { href } = override;\n const reduceMotion = useReducedMotion();\n\n const live = status === \"live\";\n const slice = GROUP_SLICES[group] ?? DEFAULT_SLICE;\n\n const body = (\n <>\n\n <div className=\"flex items-start justify-between gap-3\">\n <span className={cn(\"flex h-14 w-14 items-center justify-center rounded-2xl\", live ? slice.tint : \"bg-muted/40\")}>\n <Icon className={cn(\"h-7 w-7\", live ? slice.text : \"text-muted-foreground/50\")} />\n </span>\n {!live ? (\n <span className=\"flex items-center gap-1 text-2xs font-medium text-muted-foreground/60 pt-1\">\n <Lock className=\"h-3 w-3\" />\n Coming soon\n </span>\n ) : override.meta ? (\n <span className=\"text-xs font-semibold tabular-nums px-2.5 py-1 rounded-full bg-muted/50 text-foreground/80\">\n {override.meta}\n </span>\n ) : null}\n </div>\n\n <div className=\"mt-auto space-y-2 pt-6\">\n <h3 className=\"text-2xl font-bold tracking-tight leading-tight\">{title}</h3>\n <p className={cn(\"text-sm leading-relaxed\", live ? \"text-muted-foreground\" : \"text-muted-foreground/60\")}>\n {blurb}\n </p>\n </div>\n\n {live && features.length > 0 && (\n <ul className=\"space-y-1.5 pt-4\">\n {features.slice(0, 3).map((feature) => (\n <li key={feature} className=\"flex items-start gap-2 text-xs text-muted-foreground leading-relaxed\">\n <Check className={cn(\"h-3.5 w-3.5 shrink-0 mt-px\", slice.text)} />\n {feature}\n </li>\n ))}\n </ul>\n )}\n\n {live && (\n <div className=\"pt-5\">\n <span className=\"inline-flex items-center gap-1.5 text-sm font-semibold\">\n {cta}\n <ArrowRight className=\"h-4 w-4 transition-transform duration-200 sm:group-hover:translate-x-0.5 motion-reduce:transform-none\" />\n </span>\n </div>\n )}\n </>\n );\n\n return (\n <motion.div\n layout={!reduceMotion}\n initial={{ opacity: 0, y: reduceMotion ? 0 : 16 }}\n animate={{ opacity: 1, y: 0 }}\n exit={{ opacity: 0 }}\n whileTap={live ? { scale: 0.97 } : undefined}\n transition={{ duration: 0.35, delay: index * 0.04, ease: [0.25, 0.46, 0.45, 0.94] }}\n className=\"flex\"\n >\n {live && href ? (\n <Link\n href={href}\n className={cn(\n \"group flex flex-1 flex-col rounded-3xl border bg-card p-6 sm:aspect-[4/5]\",\n \"ml-grad-hover-border\",\n slice.border,\n )}\n >\n {body}\n </Link>\n ) : (\n <div className=\"flex flex-1 flex-col rounded-3xl border border-border/30 bg-card p-6 sm:aspect-[4/5] opacity-75\">\n {body}\n </div>\n )}\n </motion.div>\n );\n}\n\nfunction ComingSoonStrip({ group, defs }: { group: ServiceGroupDefinition; defs: ServiceDefinition[] }) {\n return (\n <div className=\"rounded-2xl border border-border/40 p-5\">\n <p className=\"font-semibold text-sm\">{group.title}</p>\n <p className=\"text-sm text-muted-foreground mt-0.5\">{group.tagline}</p>\n <div className=\"flex flex-wrap gap-2 mt-4\">\n {defs.map(({ key, icon: Icon, title }) => (\n <div key={key} className=\"flex items-center gap-2 px-3 py-2 rounded-full bg-muted/30 border border-border/25\">\n <Icon className=\"h-3.5 w-3.5 text-muted-foreground/60\" />\n <span className=\"text-xs font-medium text-muted-foreground\">{title}</span>\n </div>\n ))}\n </div>\n </div>\n );\n}\n\nexport function serviceMatchesQuery(def: ServiceDefinition, query: string): boolean {\n if (query.trim() === \"\") return true;\n const haystack = `${def.title} ${def.blurb} ${def.subtitle} ${GROUP_TITLE_BY_KEY[def.group] ?? \"\"}`.toLowerCase();\n return haystack.includes(query.trim().toLowerCase());\n}\n\nfunction syncFilterUrl(query: string, groups: Set<ServiceGroup>) {\n if (typeof window === \"undefined\") return;\n const params = new URLSearchParams(window.location.search);\n if (query.trim()) params.set(\"q\", query.trim());\n else params.delete(\"q\");\n if (groups.size > 0) params.set(\"groups\", [...groups].join(\",\"));\n else params.delete(\"groups\");\n const qs = params.toString();\n window.history.replaceState(null, \"\", qs ? `${window.location.pathname}?${qs}` : window.location.pathname);\n}\n\nexport function useLaunchpadFilter() {\n const [query, setQueryState] = useState(\"\");\n const [activeGroups, setActiveGroups] = useState<Set<ServiceGroup>>(new Set());\n\n useEffect(() => {\n const params = new URLSearchParams(window.location.search);\n const q = params.get(\"q\");\n if (q) setQueryState(q);\n const g = params.get(\"groups\");\n if (g) {\n const groups = g.split(\",\").filter((k): k is ServiceGroup => GROUP_KEYS.has(k as ServiceGroup));\n if (groups.length > 0) setActiveGroups(new Set(groups));\n }\n }, []);\n\n const filterableGroups = LAUNCHPAD_SERVICE_GROUPS.filter((g) => g.key !== \"coming-soon\");\n\n const setQuery = (value: string) => {\n setQueryState(value);\n syncFilterUrl(value, activeGroups);\n };\n\n const toggleGroup = (key: ServiceGroup) => {\n setActiveGroups((prev) => {\n const next = new Set(prev);\n if (next.has(key)) next.delete(key);\n else next.add(key);\n syncFilterUrl(query, next);\n return next;\n });\n };\n\n const matches = (def: ServiceDefinition): boolean => {\n if (activeGroups.size > 0 && !activeGroups.has(def.group)) return false;\n if (def.status !== \"live\") return false;\n return serviceMatchesQuery(def, query);\n };\n\n const totalMatches = useMemo(\n () => LAUNCHPAD_SERVICE_DEFINITIONS.filter(matches).length,\n\n [query, activeGroups],\n );\n\n const clear = () => {\n setQueryState(\"\");\n setActiveGroups(new Set());\n syncFilterUrl(\"\", new Set());\n };\n\n return { query, setQuery, activeGroups, toggleGroup, filterableGroups, totalMatches, clear };\n}\n\nexport interface LaunchpadGroupedSectionsProps {\n\n overrides: ServiceOverrides;\n\n query: string;\n\n activeGroups: Set<ServiceGroup>;\n\n onClearFilters: () => void;\n className?: string;\n\n excludeKeys?: string[];\n}\n\nexport function LaunchpadGroupedSections({ overrides, query, activeGroups, onClearFilters, className, excludeKeys }: LaunchpadGroupedSectionsProps) {\n const inActiveGroup = (d: ServiceDefinition) => activeGroups.size === 0 || activeGroups.has(d.group);\n const inSearch = (d: ServiceDefinition) => serviceMatchesQuery(d, query);\n const notExcluded = (d: ServiceDefinition) => !excludeKeys?.includes(d.key);\n\n const groupOrder = Object.fromEntries(LAUNCHPAD_SERVICE_GROUPS.map((g, i) => [g.key, i]));\n\n const liveDefs = LAUNCHPAD_SERVICE_DEFINITIONS\n .filter((d) => d.group !== \"coming-soon\" && d.status === \"live\" && notExcluded(d) && inActiveGroup(d) && inSearch(d))\n .sort((a, b) => (groupOrder[a.group] ?? 99) - (groupOrder[b.group] ?? 99));\n\n const comingSoonGroup = LAUNCHPAD_SERVICE_GROUPS.find((g) => g.key === \"coming-soon\");\n const comingSoonDefs = LAUNCHPAD_SERVICE_DEFINITIONS.filter(\n (d) => d.group === \"coming-soon\" && notExcluded(d) && inActiveGroup(d) && inSearch(d),\n );\n\n return (\n <div className={cn(\"space-y-8\", className)}>\n {liveDefs.length === 0 && comingSoonDefs.length === 0 ? (\n <div className=\"text-center py-16 space-y-3\">\n <p className=\"text-lg font-semibold\">No services match</p>\n <p className=\"text-sm text-muted-foreground\">Try a different search or clear your filters.</p>\n <button\n type=\"button\"\n onClick={onClearFilters}\n className=\"inline-flex items-center h-9 px-4 rounded-full text-sm font-semibold bg-primary text-primary-foreground\"\n >\n Clear filters\n </button>\n </div>\n ) : (\n <>\n <motion.div layout className=\"grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 xl:grid-cols-4 gap-5 sm:gap-6\">\n <AnimatePresence mode=\"popLayout\">\n {liveDefs.map((def, i) => (\n <LaunchpadServiceCard key={def.key} def={def} override={overrides[def.key]} index={i} />\n ))}\n </AnimatePresence>\n </motion.div>\n {comingSoonGroup && comingSoonDefs.length > 0 && (\n <ComingSoonStrip group={comingSoonGroup} defs={comingSoonDefs} />\n )}\n </>\n )}\n </div>\n );\n}\n"],"mappings":"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","names":[]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/components/link.tsx"],"sourcesContent":["
|
|
1
|
+
{"version":3,"sources":["../../src/components/link.tsx"],"sourcesContent":["export { default } from \"next/link\";\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,kBAAwB;","names":[]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/components/link.tsx"],"sourcesContent":["
|
|
1
|
+
{"version":3,"sources":["../../src/components/link.tsx"],"sourcesContent":["export { default } from \"next/link\";\n"],"mappings":"AAAA,SAAS,WAAAA,gBAAe;","names":["default"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/components/medialane-collection-card.tsx"],"sourcesContent":["\"use client\";\n\nimport { ImageIcon } from \"lucide-react\";\nimport { cn } from \"../utils/cn.js\";\n\nexport interface MedialaneCollectionCardProps {\n\n image?: string | null;\n\n name?: string;\n\n collection?: string;\n\n serial?: string;\n\n creator?: string;\n\n
|
|
1
|
+
{"version":3,"sources":["../../src/components/medialane-collection-card.tsx"],"sourcesContent":["\"use client\";\n\nimport { ImageIcon } from \"lucide-react\";\nimport { cn } from \"../utils/cn.js\";\n\nexport interface MedialaneCollectionCardProps {\n\n image?: string | null;\n\n name?: string;\n\n collection?: string;\n\n serial?: string;\n\n creator?: string;\n\n creatorHref?: string;\n className?: string;\n}\n\nexport function MedialaneCollectionCard({\n image,\n name,\n collection,\n serial,\n creator,\n creatorHref,\n className,\n}: MedialaneCollectionCardProps) {\n const displayName = name?.trim() || \"Untitled\";\n\n return (\n <div className={cn(className)}>\n <div className=\"rounded-[24px] border border-border\">\n <div\n className=\"ml-card-material relative rounded-[23px] overflow-hidden text-[#0a0e1f] dark:text-white\"\n >\n\n <div className=\"p-2.5 pb-0\">\n <div className=\"relative aspect-[4/5] w-full overflow-hidden rounded-[16px] ring-1 ring-black/10 dark:ring-white/10 bg-muted\">\n {image ? (\n\n <img src={image} alt={displayName} loading=\"lazy\" decoding=\"async\" className=\"h-full w-full object-cover\" />\n ) : (\n <div className=\"h-full w-full flex items-center justify-center\">\n <ImageIcon className=\"h-8 w-8 text-muted-foreground/40\" />\n </div>\n )}\n </div>\n </div>\n\n <div className=\"relative px-4 pt-3 pb-4\">\n <div className=\"flex items-start gap-2\">\n <p className=\"min-w-0 flex-1 text-[18px] font-bold leading-snug truncate\">\n {displayName}\n </p>\n {serial && (\n <span className=\"mt-0.5 shrink-0 rounded-full bg-black/5 dark:bg-white/10 px-2 py-0.5 text-2xs font-semibold text-[#0a0e1f]/80 dark:text-white/80 tabular-nums\">\n {serial}\n </span>\n )}\n </div>\n {collection && (\n <p className=\"mt-0.5 text-[12.5px] text-[#0a0e1f]/55 dark:text-white/55 truncate\">{collection}</p>\n )}\n <div className=\"my-3 h-px bg-black/10 dark:bg-white/10\" />\n <div className=\"flex items-center gap-2\">\n <span className=\"text-[9px] font-bold tracking-[0.18em] text-[#0a0e1f]/50 dark:text-white/50\">\n STARKNET\n </span>\n {creator && (\n creatorHref ? (\n <a\n href={creatorHref}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n className=\"ml-auto min-w-0 truncate text-2xs text-[#0a0e1f]/55 dark:text-white/55 tabular-nums hover:text-[#0a0e1f]/80 dark:hover:text-white/80 transition-colors\"\n >\n {creator}\n </a>\n ) : (\n <span className=\"ml-auto min-w-0 truncate text-2xs text-[#0a0e1f]/55 dark:text-white/55 tabular-nums\">\n {creator}\n </span>\n )\n )}\n </div>\n </div>\n </div>\n </div>\n </div>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AA2CgB;AAzChB,0BAA0B;AAC1B,gBAAmB;AAkBZ,SAAS,wBAAwB;AAAA,EACtC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAAiC;AAC/B,QAAM,cAAc,MAAM,KAAK,KAAK;AAEpC,SACE,4CAAC,SAAI,eAAW,cAAG,SAAS,GAC1B,sDAAC,SAAI,WAAU,uCACb;AAAA,IAAC;AAAA;AAAA,MACC,WAAU;AAAA,MAGV;AAAA,oDAAC,SAAI,WAAU,cACb,sDAAC,SAAI,WAAU,gHACZ,kBAEC,4CAAC,SAAI,KAAK,OAAO,KAAK,aAAa,SAAQ,QAAO,UAAS,SAAQ,WAAU,8BAA6B,IAE1G,4CAAC,SAAI,WAAU,kDACb,sDAAC,iCAAU,WAAU,oCAAmC,GAC1D,GAEJ,GACF;AAAA,QAEA,6CAAC,SAAI,WAAU,2BACb;AAAA,uDAAC,SAAI,WAAU,0BACb;AAAA,wDAAC,OAAE,WAAU,8DACV,uBACH;AAAA,YACC,UACC,4CAAC,UAAK,WAAU,iJACb,kBACH;AAAA,aAEJ;AAAA,UACC,cACC,4CAAC,OAAE,WAAU,sEAAsE,sBAAW;AAAA,UAEhG,4CAAC,SAAI,WAAU,0CAAyC;AAAA,UACxD,6CAAC,SAAI,WAAU,2BACb;AAAA,wDAAC,UAAK,WAAU,+EAA8E,sBAE9F;AAAA,YACC,YACC,cACE;AAAA,cAAC;AAAA;AAAA,gBACC,MAAM;AAAA,gBACN,QAAO;AAAA,gBACP,KAAI;AAAA,gBACJ,WAAU;AAAA,gBAET;AAAA;AAAA,YACH,IAEA,4CAAC,UAAK,WAAU,uFACb,mBACH;AAAA,aAGN;AAAA,WACF;AAAA;AAAA;AAAA,EACF,GACF,GACF;AAEJ;","names":[]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/components/medialane-collection-card.tsx"],"sourcesContent":["\"use client\";\n\nimport { ImageIcon } from \"lucide-react\";\nimport { cn } from \"../utils/cn.js\";\n\nexport interface MedialaneCollectionCardProps {\n\n image?: string | null;\n\n name?: string;\n\n collection?: string;\n\n serial?: string;\n\n creator?: string;\n\n
|
|
1
|
+
{"version":3,"sources":["../../src/components/medialane-collection-card.tsx"],"sourcesContent":["\"use client\";\n\nimport { ImageIcon } from \"lucide-react\";\nimport { cn } from \"../utils/cn.js\";\n\nexport interface MedialaneCollectionCardProps {\n\n image?: string | null;\n\n name?: string;\n\n collection?: string;\n\n serial?: string;\n\n creator?: string;\n\n creatorHref?: string;\n className?: string;\n}\n\nexport function MedialaneCollectionCard({\n image,\n name,\n collection,\n serial,\n creator,\n creatorHref,\n className,\n}: MedialaneCollectionCardProps) {\n const displayName = name?.trim() || \"Untitled\";\n\n return (\n <div className={cn(className)}>\n <div className=\"rounded-[24px] border border-border\">\n <div\n className=\"ml-card-material relative rounded-[23px] overflow-hidden text-[#0a0e1f] dark:text-white\"\n >\n\n <div className=\"p-2.5 pb-0\">\n <div className=\"relative aspect-[4/5] w-full overflow-hidden rounded-[16px] ring-1 ring-black/10 dark:ring-white/10 bg-muted\">\n {image ? (\n\n <img src={image} alt={displayName} loading=\"lazy\" decoding=\"async\" className=\"h-full w-full object-cover\" />\n ) : (\n <div className=\"h-full w-full flex items-center justify-center\">\n <ImageIcon className=\"h-8 w-8 text-muted-foreground/40\" />\n </div>\n )}\n </div>\n </div>\n\n <div className=\"relative px-4 pt-3 pb-4\">\n <div className=\"flex items-start gap-2\">\n <p className=\"min-w-0 flex-1 text-[18px] font-bold leading-snug truncate\">\n {displayName}\n </p>\n {serial && (\n <span className=\"mt-0.5 shrink-0 rounded-full bg-black/5 dark:bg-white/10 px-2 py-0.5 text-2xs font-semibold text-[#0a0e1f]/80 dark:text-white/80 tabular-nums\">\n {serial}\n </span>\n )}\n </div>\n {collection && (\n <p className=\"mt-0.5 text-[12.5px] text-[#0a0e1f]/55 dark:text-white/55 truncate\">{collection}</p>\n )}\n <div className=\"my-3 h-px bg-black/10 dark:bg-white/10\" />\n <div className=\"flex items-center gap-2\">\n <span className=\"text-[9px] font-bold tracking-[0.18em] text-[#0a0e1f]/50 dark:text-white/50\">\n STARKNET\n </span>\n {creator && (\n creatorHref ? (\n <a\n href={creatorHref}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n className=\"ml-auto min-w-0 truncate text-2xs text-[#0a0e1f]/55 dark:text-white/55 tabular-nums hover:text-[#0a0e1f]/80 dark:hover:text-white/80 transition-colors\"\n >\n {creator}\n </a>\n ) : (\n <span className=\"ml-auto min-w-0 truncate text-2xs text-[#0a0e1f]/55 dark:text-white/55 tabular-nums\">\n {creator}\n </span>\n )\n )}\n </div>\n </div>\n </div>\n </div>\n </div>\n );\n}\n"],"mappings":";AA2CgB,cAUJ,YAVI;AAzChB,SAAS,iBAAiB;AAC1B,SAAS,UAAU;AAkBZ,SAAS,wBAAwB;AAAA,EACtC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAAiC;AAC/B,QAAM,cAAc,MAAM,KAAK,KAAK;AAEpC,SACE,oBAAC,SAAI,WAAW,GAAG,SAAS,GAC1B,8BAAC,SAAI,WAAU,uCACb;AAAA,IAAC;AAAA;AAAA,MACC,WAAU;AAAA,MAGV;AAAA,4BAAC,SAAI,WAAU,cACb,8BAAC,SAAI,WAAU,gHACZ,kBAEC,oBAAC,SAAI,KAAK,OAAO,KAAK,aAAa,SAAQ,QAAO,UAAS,SAAQ,WAAU,8BAA6B,IAE1G,oBAAC,SAAI,WAAU,kDACb,8BAAC,aAAU,WAAU,oCAAmC,GAC1D,GAEJ,GACF;AAAA,QAEA,qBAAC,SAAI,WAAU,2BACb;AAAA,+BAAC,SAAI,WAAU,0BACb;AAAA,gCAAC,OAAE,WAAU,8DACV,uBACH;AAAA,YACC,UACC,oBAAC,UAAK,WAAU,iJACb,kBACH;AAAA,aAEJ;AAAA,UACC,cACC,oBAAC,OAAE,WAAU,sEAAsE,sBAAW;AAAA,UAEhG,oBAAC,SAAI,WAAU,0CAAyC;AAAA,UACxD,qBAAC,SAAI,WAAU,2BACb;AAAA,gCAAC,UAAK,WAAU,+EAA8E,sBAE9F;AAAA,YACC,YACC,cACE;AAAA,cAAC;AAAA;AAAA,gBACC,MAAM;AAAA,gBACN,QAAO;AAAA,gBACP,KAAI;AAAA,gBACJ,WAAU;AAAA,gBAET;AAAA;AAAA,YACH,IAEA,oBAAC,UAAK,WAAU,uFACb,mBACH;AAAA,aAGN;AAAA,WACF;AAAA;AAAA;AAAA,EACF,GACF,GACF;AAEJ;","names":[]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/components/portfolio-section.tsx"],"sourcesContent":["\"use client\";\n\nimport Link from \"./link.js\";\nimport { ArrowRight } from \"lucide-react\";\nimport { cn } from \"../utils/cn.js\";\n\nexport type PortfolioSectionColor =\n | \"blue\"\n | \"purple\"\n | \"rose\"\n | \"orange\"\n | \"maeve\"\n | \"navy\";\n\nconst BORDER_BY_COLOR: Record<PortfolioSectionColor, string> = {\n blue: \"border-brand-blue/40\",\n purple: \"border-brand-purple/40\",\n rose: \"border-brand-rose/40\",\n orange: \"border-brand-orange/40\",\n maeve: \"border-brand-maeve/40\",\n navy: \"border-brand-navy/40\",\n};\n\nexport interface PortfolioSectionProps {\n title: string;\n href: string;\n\n
|
|
1
|
+
{"version":3,"sources":["../../src/components/portfolio-section.tsx"],"sourcesContent":["\"use client\";\n\nimport Link from \"./link.js\";\nimport { ArrowRight } from \"lucide-react\";\nimport { cn } from \"../utils/cn.js\";\n\nexport type PortfolioSectionColor =\n | \"blue\"\n | \"purple\"\n | \"rose\"\n | \"orange\"\n | \"maeve\"\n | \"navy\";\n\nconst BORDER_BY_COLOR: Record<PortfolioSectionColor, string> = {\n blue: \"border-brand-blue/40\",\n purple: \"border-brand-purple/40\",\n rose: \"border-brand-rose/40\",\n orange: \"border-brand-orange/40\",\n maeve: \"border-brand-maeve/40\",\n navy: \"border-brand-navy/40\",\n};\n\nexport interface PortfolioSectionProps {\n title: string;\n href: string;\n\n color?: PortfolioSectionColor;\n\n size?: \"default\" | \"wide\";\n children: React.ReactNode;\n className?: string;\n}\n\nexport function PortfolioSection({\n title,\n href,\n color = \"purple\",\n size = \"default\",\n children,\n className,\n}: PortfolioSectionProps) {\n return (\n <section\n className={cn(\n \"rounded-2xl border min-w-0\",\n BORDER_BY_COLOR[color],\n size === \"wide\" && \"sm:col-span-2 lg:col-span-3\",\n className,\n )}\n >\n <div className=\"flex items-center justify-between gap-3 px-5 pt-5 pb-3\">\n <h2 className=\"text-sm font-black text-foreground tracking-tight\">\n {title}\n </h2>\n <Link\n href={href}\n className=\"inline-flex items-center gap-1 text-xs font-medium text-muted-foreground hover:text-foreground transition-colors\"\n >\n See all\n <ArrowRight className=\"h-3 w-3\" />\n </Link>\n </div>\n <div className=\"px-5 pb-5\">{children}</div>\n </section>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAoDQ;AAlDR,kBAAiB;AACjB,0BAA2B;AAC3B,gBAAmB;AAUnB,MAAM,kBAAyD;AAAA,EAC7D,MAAM;AAAA,EACN,QAAQ;AAAA,EACR,MAAM;AAAA,EACN,QAAQ;AAAA,EACR,OAAO;AAAA,EACP,MAAM;AACR;AAaO,SAAS,iBAAiB;AAAA,EAC/B;AAAA,EACA;AAAA,EACA,QAAQ;AAAA,EACR,OAAO;AAAA,EACP;AAAA,EACA;AACF,GAA0B;AACxB,SACE;AAAA,IAAC;AAAA;AAAA,MACC,eAAW;AAAA,QACT;AAAA,QACA,gBAAgB,KAAK;AAAA,QACrB,SAAS,UAAU;AAAA,QACnB;AAAA,MACF;AAAA,MAEA;AAAA,qDAAC,SAAI,WAAU,0DACb;AAAA,sDAAC,QAAG,WAAU,qDACX,iBACH;AAAA,UACA;AAAA,YAAC,YAAAA;AAAA,YAAA;AAAA,cACC;AAAA,cACA,WAAU;AAAA,cACX;AAAA;AAAA,gBAEC,4CAAC,kCAAW,WAAU,WAAU;AAAA;AAAA;AAAA,UAClC;AAAA,WACF;AAAA,QACA,4CAAC,SAAI,WAAU,aAAa,UAAS;AAAA;AAAA;AAAA,EACvC;AAEJ;","names":["Link"]}
|
|
@@ -4,11 +4,7 @@ type PortfolioSectionColor = "blue" | "purple" | "rose" | "orange" | "maeve" | "
|
|
|
4
4
|
interface PortfolioSectionProps {
|
|
5
5
|
title: string;
|
|
6
6
|
href: string;
|
|
7
|
-
/** Which brand color accents this section's hairline border. */
|
|
8
7
|
color?: PortfolioSectionColor;
|
|
9
|
-
/** "default" sits in the grid alongside its siblings; "wide" spans the
|
|
10
|
-
* full row instead — for list-like content (e.g. Activity) that reads
|
|
11
|
-
* better edge-to-edge than column-width. */
|
|
12
8
|
size?: "default" | "wide";
|
|
13
9
|
children: React.ReactNode;
|
|
14
10
|
className?: string;
|
|
@@ -4,11 +4,7 @@ type PortfolioSectionColor = "blue" | "purple" | "rose" | "orange" | "maeve" | "
|
|
|
4
4
|
interface PortfolioSectionProps {
|
|
5
5
|
title: string;
|
|
6
6
|
href: string;
|
|
7
|
-
/** Which brand color accents this section's hairline border. */
|
|
8
7
|
color?: PortfolioSectionColor;
|
|
9
|
-
/** "default" sits in the grid alongside its siblings; "wide" spans the
|
|
10
|
-
* full row instead — for list-like content (e.g. Activity) that reads
|
|
11
|
-
* better edge-to-edge than column-width. */
|
|
12
8
|
size?: "default" | "wide";
|
|
13
9
|
children: React.ReactNode;
|
|
14
10
|
className?: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/components/portfolio-section.tsx"],"sourcesContent":["\"use client\";\n\nimport Link from \"./link.js\";\nimport { ArrowRight } from \"lucide-react\";\nimport { cn } from \"../utils/cn.js\";\n\nexport type PortfolioSectionColor =\n | \"blue\"\n | \"purple\"\n | \"rose\"\n | \"orange\"\n | \"maeve\"\n | \"navy\";\n\nconst BORDER_BY_COLOR: Record<PortfolioSectionColor, string> = {\n blue: \"border-brand-blue/40\",\n purple: \"border-brand-purple/40\",\n rose: \"border-brand-rose/40\",\n orange: \"border-brand-orange/40\",\n maeve: \"border-brand-maeve/40\",\n navy: \"border-brand-navy/40\",\n};\n\nexport interface PortfolioSectionProps {\n title: string;\n href: string;\n\n
|
|
1
|
+
{"version":3,"sources":["../../src/components/portfolio-section.tsx"],"sourcesContent":["\"use client\";\n\nimport Link from \"./link.js\";\nimport { ArrowRight } from \"lucide-react\";\nimport { cn } from \"../utils/cn.js\";\n\nexport type PortfolioSectionColor =\n | \"blue\"\n | \"purple\"\n | \"rose\"\n | \"orange\"\n | \"maeve\"\n | \"navy\";\n\nconst BORDER_BY_COLOR: Record<PortfolioSectionColor, string> = {\n blue: \"border-brand-blue/40\",\n purple: \"border-brand-purple/40\",\n rose: \"border-brand-rose/40\",\n orange: \"border-brand-orange/40\",\n maeve: \"border-brand-maeve/40\",\n navy: \"border-brand-navy/40\",\n};\n\nexport interface PortfolioSectionProps {\n title: string;\n href: string;\n\n color?: PortfolioSectionColor;\n\n size?: \"default\" | \"wide\";\n children: React.ReactNode;\n className?: string;\n}\n\nexport function PortfolioSection({\n title,\n href,\n color = \"purple\",\n size = \"default\",\n children,\n className,\n}: PortfolioSectionProps) {\n return (\n <section\n className={cn(\n \"rounded-2xl border min-w-0\",\n BORDER_BY_COLOR[color],\n size === \"wide\" && \"sm:col-span-2 lg:col-span-3\",\n className,\n )}\n >\n <div className=\"flex items-center justify-between gap-3 px-5 pt-5 pb-3\">\n <h2 className=\"text-sm font-black text-foreground tracking-tight\">\n {title}\n </h2>\n <Link\n href={href}\n className=\"inline-flex items-center gap-1 text-xs font-medium text-muted-foreground hover:text-foreground transition-colors\"\n >\n See all\n <ArrowRight className=\"h-3 w-3\" />\n </Link>\n </div>\n <div className=\"px-5 pb-5\">{children}</div>\n </section>\n );\n}\n"],"mappings":";AAoDQ,cAGA,YAHA;AAlDR,OAAO,UAAU;AACjB,SAAS,kBAAkB;AAC3B,SAAS,UAAU;AAUnB,MAAM,kBAAyD;AAAA,EAC7D,MAAM;AAAA,EACN,QAAQ;AAAA,EACR,MAAM;AAAA,EACN,QAAQ;AAAA,EACR,OAAO;AAAA,EACP,MAAM;AACR;AAaO,SAAS,iBAAiB;AAAA,EAC/B;AAAA,EACA;AAAA,EACA,QAAQ;AAAA,EACR,OAAO;AAAA,EACP;AAAA,EACA;AACF,GAA0B;AACxB,SACE;AAAA,IAAC;AAAA;AAAA,MACC,WAAW;AAAA,QACT;AAAA,QACA,gBAAgB,KAAK;AAAA,QACrB,SAAS,UAAU;AAAA,QACnB;AAAA,MACF;AAAA,MAEA;AAAA,6BAAC,SAAI,WAAU,0DACb;AAAA,8BAAC,QAAG,WAAU,qDACX,iBACH;AAAA,UACA;AAAA,YAAC;AAAA;AAAA,cACC;AAAA,cACA,WAAU;AAAA,cACX;AAAA;AAAA,gBAEC,oBAAC,cAAW,WAAU,WAAU;AAAA;AAAA;AAAA,UAClC;AAAA,WACF;AAAA,QACA,oBAAC,SAAI,WAAU,aAAa,UAAS;AAAA;AAAA;AAAA,EACvC;AAEJ;","names":[]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/components/router.tsx"],"sourcesContent":["
|
|
1
|
+
{"version":3,"sources":["../../src/components/router.tsx"],"sourcesContent":["export { useRouter } from \"next/navigation\";\n"],"mappings":";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,wBAA0B;","names":[]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/components/router.tsx"],"sourcesContent":["
|
|
1
|
+
{"version":3,"sources":["../../src/components/router.tsx"],"sourcesContent":["export { useRouter } from \"next/navigation\";\n"],"mappings":"AAAA,SAAS,iBAAiB;","names":[]}
|