diff --git a/apps/v4/app/(app)/blocks/[...categories]/page.tsx b/apps/v4/app/(app)/blocks/[...categories]/page.tsx index 9c3b9f227c..3cca42607e 100644 --- a/apps/v4/app/(app)/blocks/[...categories]/page.tsx +++ b/apps/v4/app/(app)/blocks/[...categories]/page.tsx @@ -1,6 +1,7 @@ import { getAllBlockIds } from "@/lib/blocks" +import { registryCategories } from "@/lib/categories" import { BlockDisplay } from "@/components/block-display" -import { registryCategories } from "@/registry/registry-categories" +import { getActiveStyle } from "@/registry/styles" export const revalidate = false export const dynamic = "force-static" @@ -17,13 +18,16 @@ export default async function BlocksPage({ }: { params: Promise<{ categories?: string[] }> }) { - const { categories = [] } = await params + const [{ categories = [] }, activeStyle] = await Promise.all([ + params, + getActiveStyle(), + ]) const blocks = await getAllBlockIds(["registry:block"], categories) return (
{blocks.map((name) => ( - + ))}
) diff --git a/apps/v4/app/(app)/blocks/page.tsx b/apps/v4/app/(app)/blocks/page.tsx index e9687a962a..f79e95d65c 100644 --- a/apps/v4/app/(app)/blocks/page.tsx +++ b/apps/v4/app/(app)/blocks/page.tsx @@ -2,6 +2,7 @@ import Link from "next/link" import { BlockDisplay } from "@/components/block-display" import { Button } from "@/registry/new-york-v4/ui/button" +import { getActiveStyle } from "@/registry/styles" export const dynamic = "force-static" export const revalidate = false @@ -15,10 +16,12 @@ const FEATURED_BLOCKS = [ ] export default async function BlocksPage() { + const activeStyle = await getActiveStyle() + return (
{FEATURED_BLOCKS.map((name) => ( - + ))}
diff --git a/apps/v4/app/(app)/charts/[type]/page.tsx b/apps/v4/app/(app)/charts/[type]/page.tsx index 65cdac9ab3..28c6a17e86 100644 --- a/apps/v4/app/(app)/charts/[type]/page.tsx +++ b/apps/v4/app/(app)/charts/[type]/page.tsx @@ -3,6 +3,7 @@ import { notFound } from "next/navigation" import { cn } from "@/lib/utils" import { ChartDisplay } from "@/components/chart-display" +import { getActiveStyle } from "@/registry/styles" import { charts } from "@/app/(app)/charts/charts" export const revalidate = false @@ -41,6 +42,7 @@ export default async function ChartPage({ params }: ChartPageProps) { const chartType = type as ChartType const chartList = charts[chartType] + const activeStyle = await getActiveStyle() return (
@@ -54,6 +56,7 @@ export default async function ChartPage({ params }: ChartPageProps) { diff --git a/apps/v4/app/(app)/llm/[[...slug]]/route.ts b/apps/v4/app/(app)/llm/[[...slug]]/route.ts index c5da2bd218..8cbf7705a1 100644 --- a/apps/v4/app/(app)/llm/[[...slug]]/route.ts +++ b/apps/v4/app/(app)/llm/[[...slug]]/route.ts @@ -3,6 +3,7 @@ import { NextResponse, type NextRequest } from "next/server" import { processMdxForLLMs } from "@/lib/llm" import { source } from "@/lib/source" +import { getActiveStyle } from "@/registry/styles" export const revalidate = false @@ -10,14 +11,18 @@ export async function GET( _req: NextRequest, { params }: { params: Promise<{ slug?: string[] }> } ) { - const slug = (await params).slug + const [{ slug }, activeStyle] = await Promise.all([params, getActiveStyle()]) + const page = source.getPage(slug) if (!page) { notFound() } - const processedContent = processMdxForLLMs(await page.data.getText("raw")) + const processedContent = processMdxForLLMs( + await page.data.getText("raw"), + activeStyle.name + ) return new NextResponse(processedContent, { headers: { diff --git a/apps/v4/app/(sandbox)/sandbox/[style]/page.tsx b/apps/v4/app/(sandbox)/sandbox/[style]/page.tsx new file mode 100644 index 0000000000..9fa89e5db1 --- /dev/null +++ b/apps/v4/app/(sandbox)/sandbox/[style]/page.tsx @@ -0,0 +1,105 @@ +import { Metadata } from "next" +import { notFound } from "next/navigation" + +import { siteConfig } from "@/lib/config" +import { getRegistryComponent, getRegistryItems } from "@/lib/registry" +import { absoluteUrl, cn } from "@/lib/utils" +import { getStyle, STYLES } from "@/registry/styles" + +export const revalidate = false +export const dynamic = "force-static" +export const dynamicParams = false + +const allowedTypes = ["registry:example"] + +export async function generateMetadata({ + params, +}: { + params: Promise<{ + style: string + }> +}): Promise { + const { style: styleName } = await params + const style = getStyle(styleName) + + if (!style) { + return {} + } + + const title = style.title + + return { + title, + openGraph: { + title, + type: "article", + url: absoluteUrl(`/sandbox/${style.name}`), + images: [ + { + url: siteConfig.ogImage, + width: 1200, + height: 630, + alt: siteConfig.name, + }, + ], + }, + twitter: { + card: "summary_large_image", + title, + images: [siteConfig.ogImage], + creator: "@shadcn", + }, + } +} + +export async function generateStaticParams() { + return STYLES.map((style) => ({ + style: style.name, + })) +} + +export default async function BlockPage({ + params, +}: { + params: Promise<{ + style: string + }> +}) { + const { style: styleName } = await params + const style = getStyle(styleName) + + if (!style) { + return notFound() + } + + const items = await getRegistryItems(style.name, (item) => + allowedTypes.includes(item.type) + ) + + if (items.length === 0) { + return notFound() + } + + return ( + <> +
+ {items + .filter((item) => item !== null) + .map((item) => { + const Component = getRegistryComponent(item.name, style.name) + if (!Component) { + return null + } + return ( +
+ +
+ ) + })} +
+ + ) +} diff --git a/apps/v4/app/(view)/view/[name]/page.tsx b/apps/v4/app/(view)/view/[style]/[name]/page.tsx similarity index 52% rename from apps/v4/app/(view)/view/[name]/page.tsx rename to apps/v4/app/(view)/view/[style]/[name]/page.tsx index 422591ec61..bdd384ba3c 100644 --- a/apps/v4/app/(view)/view/[name]/page.tsx +++ b/apps/v4/app/(view)/view/[style]/[name]/page.tsx @@ -2,30 +2,38 @@ import * as React from "react" import { Metadata } from "next" import { notFound } from "next/navigation" -import { registryItemSchema } from "shadcn/schema" -import { z } from "zod" import { siteConfig } from "@/lib/config" import { getRegistryComponent, getRegistryItem } from "@/lib/registry" import { absoluteUrl, cn } from "@/lib/utils" +import { getStyle, STYLES, type Style } from "@/registry/styles" export const revalidate = false export const dynamic = "force-static" export const dynamicParams = false -const getCachedRegistryItem = React.cache(async (name: string) => { - return await getRegistryItem(name) -}) +const getCachedRegistryItem = React.cache( + async (name: string, styleName: Style["name"]) => { + return await getRegistryItem(name, styleName) + } +) export async function generateMetadata({ params, }: { params: Promise<{ + style: string name: string }> }): Promise { - const { name } = await params - const item = await getCachedRegistryItem(name) + const { style: styleName, name } = await params + const style = getStyle(styleName) + + if (!style) { + return {} + } + + const item = await getCachedRegistryItem(name, style.name) if (!item) { return {} @@ -35,13 +43,13 @@ export async function generateMetadata({ const description = item.description return { - title: item.description, + title: item.name, description, openGraph: { title, description, type: "article", - url: absoluteUrl(`/view/${item.name}`), + url: absoluteUrl(`/view/${style.name}/${item.name}`), images: [ { url: siteConfig.ogImage, @@ -63,32 +71,52 @@ export async function generateMetadata({ export async function generateStaticParams() { const { Index } = await import("@/registry/__index__") - const index = z.record(registryItemSchema).parse(Index) + const params: Array<{ style: string; name: string }> = [] - return Object.values(index) - .filter((block) => - [ - "registry:block", - "registry:component", - "registry:example", - "registry:internal", - ].includes(block.type) - ) - .map((block) => ({ - name: block.name, - })) + for (const style of STYLES) { + if (!Index[style.name]) { + continue + } + + const styleIndex = Index[style.name] + for (const itemName in styleIndex) { + const item = styleIndex[itemName] + if ( + [ + "registry:block", + "registry:component", + "registry:example", + "registry:internal", + ].includes(item.type) + ) { + params.push({ + style: style.name, + name: item.name, + }) + } + } + } + + return params } export default async function BlockPage({ params, }: { params: Promise<{ + style: string name: string }> }) { - const { name } = await params - const item = await getCachedRegistryItem(name) - const Component = getRegistryComponent(name) + const { style: styleName, name } = await params + const style = getStyle(styleName) + + if (!style) { + return notFound() + } + + const item = await getCachedRegistryItem(name, style.name) + const Component = getRegistryComponent(name, style.name) if (!item || !Component) { return notFound() diff --git a/apps/v4/components/block-display.tsx b/apps/v4/components/block-display.tsx index f860cff158..00abe037b0 100644 --- a/apps/v4/components/block-display.tsx +++ b/apps/v4/components/block-display.tsx @@ -10,9 +10,16 @@ import { import { cn } from "@/lib/utils" import { BlockViewer } from "@/components/block-viewer" import { ComponentPreview } from "@/components/component-preview" +import { type Style } from "@/registry/styles" -export async function BlockDisplay({ name }: { name: string }) { - const item = await getCachedRegistryItem(name) +export async function BlockDisplay({ + name, + styleName, +}: { + name: string + styleName: Style["name"] +}) { + const item = await getCachedRegistryItem(name, styleName) if (!item?.files) { return null @@ -24,9 +31,15 @@ export async function BlockDisplay({ name }: { name: string }) { ]) return ( - + .p-6]:p-0", @@ -37,9 +50,11 @@ export async function BlockDisplay({ name }: { name: string }) { ) } -const getCachedRegistryItem = React.cache(async (name: string) => { - return await getRegistryItem(name) -}) +const getCachedRegistryItem = React.cache( + async (name: string, styleName: Style["name"]) => { + return await getRegistryItem(name, styleName) + } +) const getCachedFileTree = React.cache( async (files: Array<{ path: string; target?: string }>) => { diff --git a/apps/v4/components/block-viewer.tsx b/apps/v4/components/block-viewer.tsx index 5a65b1e7fe..509c94ded6 100644 --- a/apps/v4/components/block-viewer.tsx +++ b/apps/v4/components/block-viewer.tsx @@ -54,6 +54,7 @@ import { ToggleGroup, ToggleGroupItem, } from "@/registry/new-york-v4/ui/toggle-group" +import { type Style } from "@/registry/styles" type BlockViewerContext = { item: z.infer @@ -128,7 +129,15 @@ function BlockViewerProvider({ ) } -function BlockViewerToolbar() { +type BlockViewerProps = Pick< + BlockViewerContext, + "item" | "tree" | "highlightedFiles" +> & { + children: React.ReactNode + styleName: Style["name"] +} + +function BlockViewerToolbar({ styleName }: { styleName: Style["name"] }) { const { setView, view, item, resizablePanelRef, setIframeKey } = useBlockViewer() const { copyToClipboard, isCopied } = useCopyToClipboard() @@ -181,7 +190,7 @@ function BlockViewerToolbar() { asChild title="Open in New Tab" > - + Open in New Tab @@ -222,13 +231,19 @@ function BlockViewerToolbar() { ) } -function BlockViewerIframe({ className }: { className?: string }) { +function BlockViewerIframe({ + className, + styleName, +}: { + className?: string + styleName: Style["name"] +}) { const { item, iframeKey } = useBlockViewer() return (