From 92e28e270e01b096c3180754e91d68a1b00f0dac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dandelion=20Man=C3=A9?= Date: Mon, 2 Jun 2025 14:20:30 -0700 Subject: [PATCH 1/3] feat: add button-link component --- bun.lock | 13 ++ mirascope-ui/ui/button-link.stories.tsx | 227 ++++++++++++++++++++++++ mirascope-ui/ui/button-link.tsx | 47 +++++ package.json | 1 + registry.json | 14 ++ 5 files changed, 302 insertions(+) create mode 100644 mirascope-ui/ui/button-link.stories.tsx create mode 100644 mirascope-ui/ui/button-link.tsx diff --git a/bun.lock b/bun.lock index b860260..dcc470f 100644 --- a/bun.lock +++ b/bun.lock @@ -26,6 +26,7 @@ "@storybook/addon-actions": "^8.6.14", "@storybook/addon-themes": "^8.6.14", "@tailwindcss/typography": "^0.5.16", + "@tanstack/react-router": "^1.120.13", "bun-types": "^1.2.9", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", @@ -723,6 +724,16 @@ "@tailwindcss/typography": ["@tailwindcss/typography@0.5.16", "", { "dependencies": { "lodash.castarray": "^4.4.0", "lodash.isplainobject": "^4.0.6", "lodash.merge": "^4.6.2", "postcss-selector-parser": "6.0.10" }, "peerDependencies": { "tailwindcss": ">=3.0.0 || insiders || >=4.0.0-alpha.20 || >=4.0.0-beta.1" } }, "sha512-0wDLwCVF5V3x3b1SGXPCDcdsbDHMBe+lkFzBRaHeLvNi+nrrnZ1lA18u+OTWO8iSWU2GxUOCvlXtDuqftc1oiA=="], + "@tanstack/history": ["@tanstack/history@1.115.0", "", {}, "sha512-K7JJNrRVvyjAVnbXOH2XLRhFXDkeP54Kt2P4FR1Kl2KDGlIbkua5VqZQD2rot3qaDrpufyUa63nuLai1kOLTsQ=="], + + "@tanstack/react-router": ["@tanstack/react-router@1.120.13", "", { "dependencies": { "@tanstack/history": "1.115.0", "@tanstack/react-store": "^0.7.0", "@tanstack/router-core": "1.120.13", "jsesc": "^3.1.0", "tiny-invariant": "^1.3.3", "tiny-warning": "^1.0.3" }, "peerDependencies": { "react": ">=18.0.0 || >=19.0.0", "react-dom": ">=18.0.0 || >=19.0.0" } }, "sha512-WAw5ZBo9Z3KQJMbxTK8O/3TW38K/F+ef1MzmA7uPrROMeNcBeZOEvhqeE4cKQbpnyWzIdWzNITjHs6wjn1ydzQ=="], + + "@tanstack/react-store": ["@tanstack/react-store@0.7.1", "", { "dependencies": { "@tanstack/store": "0.7.1", "use-sync-external-store": "^1.5.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-qUTEKdId6QPWGiWyKAPf/gkN29scEsz6EUSJ0C3HgLMgaqTAyBsQ2sMCfGVcqb+kkhEXAdjleCgH6LAPD6f2sA=="], + + "@tanstack/router-core": ["@tanstack/router-core@1.120.13", "", { "dependencies": { "@tanstack/history": "1.115.0", "@tanstack/store": "^0.7.0", "tiny-invariant": "^1.3.3" } }, "sha512-6elPiA6XSrAg6riKzl+lqYuHSp38++oWvEP50I6kSBDp0QmP/NKVYM0SL/g2R85AFD/hL9sFm+P5aTXzMgrPaA=="], + + "@tanstack/store": ["@tanstack/store@0.7.1", "", {}, "sha512-PjUQKXEXhLYj2X5/6c1Xn/0/qKY0IVFxTJweopRfF26xfjVyb14yALydJrHupDh3/d+1WKmfEgZPBVCmDkzzwg=="], + "@testing-library/dom": ["@testing-library/dom@10.4.0", "", { "dependencies": { "@babel/code-frame": "^7.10.4", "@babel/runtime": "^7.12.5", "@types/aria-query": "^5.0.1", "aria-query": "5.3.0", "chalk": "^4.1.0", "dom-accessibility-api": "^0.5.9", "lz-string": "^1.5.0", "pretty-format": "^27.0.2" } }, "sha512-pemlzrSESWbdAloYml3bAJMEfNh1Z7EduzqPKprCH5S341frlpYnUEW0H72dLxa6IsYr+mPno20GiSm+h9dEdQ=="], "@testing-library/jest-dom": ["@testing-library/jest-dom@6.5.0", "", { "dependencies": { "@adobe/css-tools": "^4.4.0", "aria-query": "^5.0.0", "chalk": "^3.0.0", "css.escape": "^1.5.1", "dom-accessibility-api": "^0.6.3", "lodash": "^4.17.21", "redent": "^3.0.0" } }, "sha512-xGGHpBXYSHUUr6XsKBfs85TWlYKpTc37cSBBVrXcib2MkHLboWlkClhWF37JKlDb9KEq3dHs+f2xR7XJEWGBxA=="], @@ -2177,6 +2188,8 @@ "tiny-invariant": ["tiny-invariant@1.3.3", "", {}, "sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg=="], + "tiny-warning": ["tiny-warning@1.0.3", "", {}, "sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA=="], + "tinyglobby": ["tinyglobby@0.2.13", "", { "dependencies": { "fdir": "^6.4.4", "picomatch": "^4.0.2" } }, "sha512-mEwzpUgrLySlveBwEVDMKk5B57bhLPYovRfPAXD5gA/98Opn0rCDj3GtLwFvCvH5RK9uPCExUROW5NjDwvqkxw=="], "tinyrainbow": ["tinyrainbow@1.2.0", "", {}, "sha512-weEDEq7Z5eTHPDh4xjX789+fHfF+P8boiFB+0vbWzpbnbsEr/GRaohi/uMKxg8RZMXnl1ItAi/IUHWMsjDV7kQ=="], diff --git a/mirascope-ui/ui/button-link.stories.tsx b/mirascope-ui/ui/button-link.stories.tsx new file mode 100644 index 0000000..f9724c9 --- /dev/null +++ b/mirascope-ui/ui/button-link.stories.tsx @@ -0,0 +1,227 @@ +import { ButtonLink } from "@/mirascope-ui/ui/button-link"; +import type { Meta, StoryObj } from "@storybook/react"; +import { ExternalLink, ArrowRight } from "lucide-react"; +import { + createMemoryHistory, + createRootRoute, + createRoute, + createRouter, + RouterProvider, +} from "@tanstack/react-router"; + +// Mock router setup for Storybook + +const memoryHistory = createMemoryHistory({ + initialEntries: ["/"], +}); + +const withRouter = (Story: any) => { + // Update the root route component to render the story + const StoryRoute = createRootRoute({ + component: () => , + }); + + const storyRouteTree = StoryRoute.addChildren([ + createRoute({ + getParentRoute: () => StoryRoute, + path: "/", + component: () => , + }), + ]); + + const storyRouter = createRouter({ + routeTree: storyRouteTree, + history: memoryHistory, + }); + + return ; +}; + +const meta = { + title: "UI/ButtonLink", + component: ButtonLink, + decorators: [withRouter], + parameters: { + layout: "centered", + }, + argTypes: { + variant: { + control: "select", + options: ["default", "destructive", "outline", "secondary", "ghost", "link"], + description: "The visual style of the button link", + }, + size: { + control: "select", + options: ["default", "sm", "lg", "icon"], + description: "The size of the button link", + }, + external: { + control: "boolean", + description: "Whether the link is external (forces anchor tag)", + }, + href: { + control: "text", + description: "The URL or path to link to", + }, + }, + args: { + variant: "outline", + size: "default", + external: false, + href: "#", + }, + tags: ["autodocs"], +} satisfies Meta; + +export default meta; +type Story = StoryObj; + +export const Default: Story = { + args: { + children: "Button Link", + href: "#", + }, +}; + +export const External: Story = { + args: { + children: ( + <> + Visit Website + + + ), + href: "https://example.com", + external: true, + }, +}; + +export const InternalLink: Story = { + args: { + children: "Go to Dashboard", + href: "/dashboard", + external: false, + }, +}; + +export const Primary: Story = { + args: { + variant: "default", + children: "Primary Link", + href: "#", + }, +}; + +export const Secondary: Story = { + args: { + variant: "secondary", + children: "Secondary Link", + href: "#", + }, +}; + +export const Destructive: Story = { + args: { + variant: "destructive", + children: "Delete Item", + href: "#", + }, +}; + +export const Ghost: Story = { + args: { + variant: "ghost", + children: "Ghost Link", + href: "#", + }, +}; + +export const Small: Story = { + args: { + size: "sm", + children: "Small Link", + href: "#", + }, +}; + +export const Large: Story = { + args: { + size: "lg", + children: "Large Link", + href: "#", + }, +}; + +export const WithIcon: Story = { + args: { + children: ( + <> + Next Page + + + ), + href: "/next", + }, +}; + +export const AutoDetectExternal: Story = { + args: { + children: "Auto External", + href: "https://github.com", + }, + parameters: { + docs: { + description: { + story: "Automatically detects external links based on href starting with http/https", + }, + }, + }, +}; + +export const LinkVariants: Story = { + render: () => ( +
+
+ + Default + + + Secondary + + + Outline + + + Ghost + + + Link + +
+
+ + Small + + + Default + + + Large + +
+
+ Internal Link + + External Link + +
+
+ ), + parameters: { + docs: { + description: { + story: "A showcase of all ButtonLink variants, sizes, and link types.", + }, + }, + }, +}; diff --git a/mirascope-ui/ui/button-link.tsx b/mirascope-ui/ui/button-link.tsx new file mode 100644 index 0000000..6986682 --- /dev/null +++ b/mirascope-ui/ui/button-link.tsx @@ -0,0 +1,47 @@ +import { Link } from "@tanstack/react-router"; +import type { LinkProps } from "@tanstack/react-router"; +import { buttonVariants } from "./button"; +import { cn } from "@/mirascope-ui/lib/utils"; +import type { ButtonProps } from "./button"; + +export interface ButtonLinkProps { + href: string; + external?: boolean; + variant?: ButtonProps["variant"]; + size?: ButtonProps["size"]; + className?: string; + children?: React.ReactNode; +} + +export function ButtonLink({ + className, + variant = "outline", + size = "default", + href, + external, + children, + ...props +}: ButtonLinkProps & Omit, keyof ButtonLinkProps>) { + const isExternal = + external || href.startsWith("http") || href.startsWith("https") || href.startsWith("//"); + + const classNames = cn(buttonVariants({ variant, size, className })); + + if (isExternal) { + return ( + + {children} + + ); + } + + return ( + )} + > + {children} + + ); +} diff --git a/package.json b/package.json index 65b268d..628cdf5 100644 --- a/package.json +++ b/package.json @@ -52,6 +52,7 @@ "@storybook/addon-actions": "^8.6.14", "@storybook/addon-themes": "^8.6.14", "@tailwindcss/typography": "^0.5.16", + "@tanstack/react-router": "^1.120.13", "bun-types": "^1.2.9", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", diff --git a/registry.json b/registry.json index 3e17346..0757874 100644 --- a/registry.json +++ b/registry.json @@ -96,6 +96,20 @@ } ] }, + { + "name": "button-link", + "type": "registry:ui", + "title": "Button Link", + "description": "A component that renders either a Link or anchor tag with button styling.", + "dependencies": ["@tanstack/react-router"], + "registryDependencies": ["button", "utils"], + "files": [ + { + "path": "mirascope-ui/ui/button-link.tsx", + "type": "registry:ui" + } + ] + }, { "name": "input", "type": "registry:ui", From 0c29c87954152e4878c0f27aafb344d9b8a1de8b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dandelion=20Man=C3=A9?= Date: Mon, 2 Jun 2025 14:27:58 -0700 Subject: [PATCH 2/3] feat: synchronize tabs to mirascope/website variant --- mirascope-ui/ui/tabs.tsx | 11 ++++++++--- registry.json | 2 +- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/mirascope-ui/ui/tabs.tsx b/mirascope-ui/ui/tabs.tsx index 2d23286..289eeed 100644 --- a/mirascope-ui/ui/tabs.tsx +++ b/mirascope-ui/ui/tabs.tsx @@ -1,5 +1,6 @@ import * as TabsPrimitive from "@radix-ui/react-tabs"; import * as React from "react"; +import { buttonVariants } from "./button"; import { cn } from "@/mirascope-ui/lib/utils"; @@ -18,7 +19,7 @@ function TabsList({ className, ...props }: React.ComponentProps Date: Mon, 2 Jun 2025 14:29:18 -0700 Subject: [PATCH 3/3] feat: add lilypad-pricing component --- .../blocks/lilypad-pricing.stories.tsx | 192 +++++++++ mirascope-ui/blocks/lilypad-pricing.tsx | 376 ++++++++++++++++++ registry.json | 14 + 3 files changed, 582 insertions(+) create mode 100644 mirascope-ui/blocks/lilypad-pricing.stories.tsx create mode 100644 mirascope-ui/blocks/lilypad-pricing.tsx diff --git a/mirascope-ui/blocks/lilypad-pricing.stories.tsx b/mirascope-ui/blocks/lilypad-pricing.stories.tsx new file mode 100644 index 0000000..bd3ce48 --- /dev/null +++ b/mirascope-ui/blocks/lilypad-pricing.stories.tsx @@ -0,0 +1,192 @@ +import { LilypadPricing } from "@/mirascope-ui/blocks/lilypad-pricing"; +import type { Meta, StoryObj } from "@storybook/react"; +import { + createMemoryHistory, + createRootRoute, + createRoute, + createRouter, + RouterProvider, +} from "@tanstack/react-router"; + +// Mock router setup for Storybook +const memoryHistory = createMemoryHistory({ + initialEntries: ["/"], +}); + +const withRouter = (Story: any) => { + const StoryRoute = createRootRoute({ + component: () => , + }); + + const storyRouteTree = StoryRoute.addChildren([ + createRoute({ + getParentRoute: () => StoryRoute, + path: "/", + component: () => , + }), + ]); + + const storyRouter = createRouter({ + routeTree: storyRouteTree, + history: memoryHistory, + }); + + return ; +}; + +const meta = { + title: "Blocks/Lilypad Pricing", + component: LilypadPricing, + decorators: [withRouter], + parameters: { + layout: "fullscreen", + }, + argTypes: { + actions: { + description: "Configuration for button actions across different hosting options and tiers", + }, + }, + tags: ["autodocs"], +} satisfies Meta; + +export default meta; +type Story = StoryObj; + +const defaultActions = { + hosted: { + free: { + buttonText: "Get Started Free", + buttonLink: "/signup", + variant: "default" as const, + }, + pro: { + buttonText: "Join Waitlist", + buttonLink: "/waitlist", + variant: "outline" as const, + }, + team: { + buttonText: "Contact Sales", + buttonLink: "/contact", + variant: "outline" as const, + }, + }, + selfHosted: { + free: { + buttonText: "Download", + buttonLink: "/download", + variant: "default" as const, + }, + pro: { + buttonText: "Request Access", + buttonLink: "/request-access", + variant: "outline" as const, + }, + team: { + buttonText: "Contact Sales", + buttonLink: "/contact", + variant: "outline" as const, + }, + }, +}; + +export const Default: Story = { + args: { + actions: defaultActions, + }, +}; + +export const WithExternalLinks: Story = { + args: { + actions: { + hosted: { + free: { + buttonText: "Sign Up Now", + buttonLink: "https://app.lilypad.com/signup", + variant: "default" as const, + }, + pro: { + buttonText: "Join Beta", + buttonLink: "https://forms.lilypad.com/beta", + variant: "outline" as const, + }, + team: { + buttonText: "Schedule Demo", + buttonLink: "https://calendly.com/lilypad-team", + variant: "outline" as const, + }, + }, + selfHosted: { + free: { + buttonText: "GitHub Release", + buttonLink: "https://github.com/mirascope/lilypad/releases", + variant: "default" as const, + }, + pro: { + buttonText: "Enterprise Inquiry", + buttonLink: "https://forms.lilypad.com/enterprise", + variant: "outline" as const, + }, + team: { + buttonText: "Book Consultation", + buttonLink: "https://calendly.com/lilypad-enterprise", + variant: "outline" as const, + }, + }, + }, + }, + parameters: { + docs: { + description: { + story: "Example with external links for a live deployment scenario", + }, + }, + }, +}; + +export const AllPrimaryButtons: Story = { + args: { + actions: { + hosted: { + free: { + buttonText: "Start Free", + buttonLink: "/start", + variant: "default" as const, + }, + pro: { + buttonText: "Upgrade to Pro", + buttonLink: "/upgrade-pro", + variant: "default" as const, + }, + team: { + buttonText: "Get Team Plan", + buttonLink: "/upgrade-team", + variant: "default" as const, + }, + }, + selfHosted: { + free: { + buttonText: "Download Free", + buttonLink: "/download", + variant: "default" as const, + }, + pro: { + buttonText: "Get Pro License", + buttonLink: "/license-pro", + variant: "default" as const, + }, + team: { + buttonText: "Get Team License", + buttonLink: "/license-team", + variant: "default" as const, + }, + }, + }, + }, + parameters: { + docs: { + description: { + story: "All tiers using primary button styling for a more aggressive CTA approach", + }, + }, + }, +}; diff --git a/mirascope-ui/blocks/lilypad-pricing.tsx b/mirascope-ui/blocks/lilypad-pricing.tsx new file mode 100644 index 0000000..4fcef72 --- /dev/null +++ b/mirascope-ui/blocks/lilypad-pricing.tsx @@ -0,0 +1,376 @@ +import { Check, X } from "lucide-react"; +import { Tabs, TabsList, TabsTrigger, TabsContent } from "@/mirascope-ui/ui/tabs"; +import { ButtonLink } from "@/mirascope-ui/ui/button-link"; +import { cn } from "@/mirascope-ui/lib/utils"; + +// Feature row component for displaying features with the same value across tiers +const FeatureRow = ({ + feature, + free, + pro, + team, +}: { + feature: string; + free: string | boolean; + pro: string | boolean; + team: string | boolean; +}) => { + // If all tiers have the exact same value (and it's not a boolean) + const allSameNonBoolean = free === pro && pro === team && typeof free === "string" && free !== ""; + + return ( +
+
{feature}
+ + {allSameNonBoolean ? ( +
{free}
+ ) : ( + <> +
+ {typeof free === "boolean" ? ( +
+ {free ? ( +
+ +
+ ) : ( +
+ +
+ )} +
+ ) : ( + {free} + )} +
+ +
+ {typeof pro === "boolean" ? ( +
+ {pro ? ( +
+ +
+ ) : ( +
+ +
+ )} +
+ ) : ( + {pro} + )} +
+ +
+ {typeof team === "boolean" ? ( +
+ {team ? ( +
+ +
+ ) : ( +
+ +
+ )} +
+ ) : ( + {team} + )} +
+ + )} +
+ ); +}; + +// Pricing tier component +const PricingTier = ({ + name, + price, + description, + buttonText, + buttonLink, + badge, + variant = "default", +}: { + name: string; + price: string; + description: string; + buttonText: string; + buttonLink: string; + badge?: "Open Beta" | "Closed Beta"; + variant?: "default" | "outline"; +}) => ( +
+
+
+

{name}

+ {badge && ( + + {badge} + + )} +
+

{description}

+
+ {price} + {price !== "TBD" && price !== "N/A" && ( + / month + )} +
+ + {buttonText} + +
+
+); + +// Feature comparison table component +const FeatureComparisonTable = ({ + features, +}: { + features: Array<{ + feature: string; + free: string | boolean; + pro: string | boolean; + team: string | boolean; + }>; +}) => ( +
+
+

Feature Comparison

+
+
+ {/* Table header */} +
+
Feature
+
Free
+
Pro
+
Team
+
+ + {/* Table rows */} + {features.map((feat, i) => ( + + ))} +
+
+); + +interface TierAction { + buttonText: string; + buttonLink: string; + variant?: "default" | "outline"; +} + +interface PricingActions { + hosted: { + free: TierAction; + pro: TierAction; + team: TierAction; + }; + selfHosted: { + free: TierAction; + pro: TierAction; + team: TierAction; + }; +} + +interface LilypadPricingProps { + actions: PricingActions; +} + +export function LilypadPricing({ actions }: LilypadPricingProps) { + // Cloud hosted features + const cloudHostedFeatures = [ + { feature: "Projects", free: "Unlimited", pro: "Unlimited", team: "Unlimited" }, + { feature: "Users", free: "2", pro: "10", team: "Unlimited" }, + { + feature: "Tracing", + free: "30k spans / month", + pro: "100k spans / month (thereafter $1 per 10k)", + team: "1M spans / month (thereafter $1 per 10k)", + }, + { feature: "Data Retention", free: "30 days", pro: "90 days", team: "180 days" }, + { feature: "Versioned Functions", free: true, pro: true, team: true }, + { feature: "Playground", free: true, pro: true, team: true }, + { feature: "Comparisons", free: true, pro: true, team: true }, + { feature: "Annotations", free: true, pro: true, team: true }, + { feature: "Support (Community)", free: true, pro: true, team: true }, + { feature: "Support (Chat / Email)", free: false, pro: true, team: true }, + { feature: "Support (Private Slack)", free: false, pro: false, team: true }, + { feature: "API Rate Limits", free: "10 / minute", pro: "100 / minute", team: "1000 / minute" }, + ]; + + // Self-hosted features + const selfHostedFeatures = [ + { feature: "Projects", free: "Unlimited", pro: "Unlimited", team: "Unlimited" }, + { feature: "Users", free: "Unlimited", pro: "As licensed", team: "As licensed" }, + { feature: "Tracing", free: "No limits", pro: "No limits", team: "No limits" }, + { feature: "Data Retention", free: "No limits", pro: "No limits", team: "No limits" }, + { feature: "Versioned Functions", free: true, pro: true, team: true }, + { feature: "Playground", free: false, pro: true, team: true }, + { feature: "Comparisons", free: false, pro: true, team: true }, + { feature: "Annotations", free: false, pro: true, team: true }, + { feature: "Support (Community)", free: true, pro: true, team: true }, + { feature: "Support (Chat / Email)", free: false, pro: true, team: true }, + { feature: "Support (Private Slack)", free: false, pro: false, team: true }, + { feature: "API Rate Limits", free: "No limits", pro: "No limits", team: "No limits" }, + ]; + + return ( +
+
+
+

Lilypad Pricing

+

+ Get started with the Free plan today. +

+

+ No credit card required. +

+
+ + +
+ + Hosted By Us + Self Hosting + +
+ + {/* Hosted By Us Tab Content */} + +
+ + + +
+ + {/* Feature comparison table */} + +
+ + {/* Self Hosting Tab Content */} + +
+ + + +
+ + {/* Feature comparison table */} + +
+
+ + {/* FAQ Section */} +
+

+ Frequently Asked Questions +

+
+
+

+ How long will the open beta last? +

+

+ The open beta period is ongoing, and we'll provide advance notice before moving to + paid plans. +

+
+
+

+ What happens when the beta ends? +

+

+ All existing users will receive a grace period to evaluate which plan is right for + them before making any changes. +

+
+
+
+ +
+

+ Have questions about our pricing? +

+

+ Join our{" "} + + community + {" "} + and ask the team directly! +

+
+
+
+ ); +} diff --git a/registry.json b/registry.json index b132e22..c092b63 100644 --- a/registry.json +++ b/registry.json @@ -56,6 +56,20 @@ } ] }, + { + "name": "lilypad-pricing", + "type": "registry:block", + "title": "Lilypad Pricing", + "description": "A complete pricing page component with feature comparison tables", + "dependencies": ["lucide-react"], + "registryDependencies": ["tabs", "button-link", "utils"], + "files": [ + { + "path": "mirascope-ui/blocks/lilypad-pricing.tsx", + "type": "registry:block" + } + ] + }, { "name": "code-block", "type": "registry:block",