import dedent from "dedent" import { UI_COMPONENTS } from "@/lib/components" import { buildRegistryBase, getBodyFont, getHeadingFont, getInheritedHeadingFontValue, type DesignSystemConfig, } from "@/registry/config" // Builds step-by-step markdown instructions for manually setting up a project. export function buildInstructions(config: DesignSystemConfig) { const registryBase = buildRegistryBase(config) const normalizedFontHeading = config.fontHeading === config.font ? "inherit" : config.fontHeading const dependencies = [...(registryBase.dependencies ?? []), "cn"] const lightVars = Object.entries(registryBase.cssVars?.light ?? {}) .map(([key, value]) => ` --${key}: ${value};`) .join("\n") const darkVars = Object.entries(registryBase.cssVars?.dark ?? {}) .map(([key, value]) => ` --${key}: ${value};`) .join("\n") const font = getBodyFont(config.font) const headingFont = normalizedFontHeading === "inherit" ? undefined : getHeadingFont(normalizedFontHeading) const sections = [ buildDependenciesSection(dependencies), buildUtilsSection(), buildCssSection( lightVars, darkVars, normalizedFontHeading === "inherit" ? getInheritedHeadingFontValue(config.font) : "var(--font-heading)" ), buildFontSection(font, headingFont), buildComponentsJsonSection(config), buildAvailableComponentsSection(config), config.rtl ? buildRtlSection(config) : null, ] return sections.filter(Boolean).join("\n\n---\n\n") } function buildDependenciesSection(dependencies: string[]) { const list = dependencies.map((dep) => `- ${dep}`).join("\n") return dedent` ## Step 1: Dependencies The following dependencies are required: ${list} ` } function buildUtilsSection() { return dedent` ## Step 2: Create \`lib/utils.ts\` \`\`\`ts export { cn } from "cn" \`\`\` ` } function buildCssSection( lightVars: string, darkVars: string, fontHeadingValue: string ) { return dedent` ## Step 3: Set up CSS Add the following to your global CSS file (e.g. \`app/globals.css\`): \`\`\`css @import "tailwindcss"; @import "tw-animate-css"; @import "shadcn/tailwind.css"; @theme inline { --font-sans: var(--font-sans); --font-heading: ${fontHeadingValue}; --font-mono: var(--font-mono); --color-background: var(--background); --color-foreground: var(--foreground); --color-card: var(--card); --color-card-foreground: var(--card-foreground); --color-popover: var(--popover); --color-popover-foreground: var(--popover-foreground); --color-primary: var(--primary); --color-primary-foreground: var(--primary-foreground); --color-secondary: var(--secondary); --color-secondary-foreground: var(--secondary-foreground); --color-muted: var(--muted); --color-muted-foreground: var(--muted-foreground); --color-accent: var(--accent); --color-accent-foreground: var(--accent-foreground); --color-destructive: var(--destructive); --color-destructive-foreground: var(--destructive-foreground); --color-border: var(--border); --color-input: var(--input); --color-ring: var(--ring); --color-chart-1: var(--chart-1); --color-chart-2: var(--chart-2); --color-chart-3: var(--chart-3); --color-chart-4: var(--chart-4); --color-chart-5: var(--chart-5); --color-sidebar: var(--sidebar); --color-sidebar-foreground: var(--sidebar-foreground); --color-sidebar-primary: var(--sidebar-primary); --color-sidebar-primary-foreground: var(--sidebar-primary-foreground); --color-sidebar-accent: var(--sidebar-accent); --color-sidebar-accent-foreground: var(--sidebar-accent-foreground); --color-sidebar-border: var(--sidebar-border); --color-sidebar-ring: var(--sidebar-ring); --radius-sm: calc(var(--radius) * 0.6); --radius-md: calc(var(--radius) * 0.8); --radius-lg: var(--radius); --radius-xl: calc(var(--radius) * 1.4); --radius-2xl: calc(var(--radius) * 1.8); --radius-3xl: calc(var(--radius) * 2.2); --radius-4xl: calc(var(--radius) * 2.6); } :root { ${lightVars} } .dark { ${darkVars} } @layer base { * { @apply border-border outline-ring/50; } body { @apply bg-background text-foreground; } } \`\`\` ` } function buildFontSection( font: ReturnType, headingFont: ReturnType ) { if (!font) { return null } const googleFontsUrl = `https://fonts.google.com/specimen/${font.font.import.replace(/_/g, "+")}` const headingGoogleFontsUrl = headingFont ? `https://fonts.google.com/specimen/${headingFont.font.import.replace(/_/g, "+")}` : null const nextImports = headingFont ? `${font.font.import}, ${headingFont.font.import}` : font.font.import const nextDeclarations = [ `const fontSans = ${font.font.import}({`, ` subsets: ["latin"],`, ` variable: "${font.font.variable}",`, `})`, headingFont ? `const fontHeading = ${headingFont.font.import}({` : null, headingFont ? ` subsets: ["latin"],` : null, headingFont ? ` variable: "${headingFont.font.variable}",` : null, headingFont ? `})` : null, ] .filter(Boolean) .join("\n") const nextHtmlClassName = headingFont ? 'className={fontSans.variable + " " + fontHeading.variable}' : `className={fontSans.variable}` const otherFrameworkCss = [ ":root {", ` ${font.font.variable}: ${font.font.family};`, ...(headingFont ? [` ${headingFont.font.variable}: ${headingFont.font.family};`] : []), "}", ].join("\n") const headingSection = headingFont ? dedent` This config also uses **${headingFont.title.replace(" (Heading)", "")}** for headings (\`${headingFont.font.variable}\`). ` : "" return dedent` ## Step 4: Set up the font This config uses **${font.title}** (\`${font.font.variable}\`). ${headingSection} ### Next.js \`\`\`tsx import { ${nextImports} } from "next/font/google" ${nextDeclarations} // Add the font variable classes to your className. // \`\`\` ### Other frameworks Add the font${headingFont ? "s" : ""} from [Google Fonts](${googleFontsUrl})${headingGoogleFontsUrl ? ` and [Google Fonts](${headingGoogleFontsUrl})` : ""} and set the CSS variable${headingFont ? "s" : ""} to the font famil${headingFont ? "ies" : "y"}: \`\`\`css ${otherFrameworkCss} \`\`\` ` } function buildComponentsJsonSection(config: DesignSystemConfig) { const componentsJson = { $schema: "https://ui.shadcn.com/schema.json", style: `${config.base}-${config.style}`, tailwind: { css: "app/globals.css", baseColor: config.baseColor, }, iconLibrary: config.iconLibrary, aliases: { components: "@/components", utils: "@/lib/utils", ui: "@/components/ui", lib: "@/lib", hooks: "@/hooks", }, } return dedent` ## Step 5: Create \`components.json\` Add a \`components.json\` file to the root of your project: \`\`\`json ${JSON.stringify(componentsJson, null, 2)} \`\`\` ` } function buildAvailableComponentsSection(config: DesignSystemConfig) { const list = UI_COMPONENTS.join(", ") const style = `${config.base}-${config.style}` return dedent` ## Available Components ${list} To fetch the source for a component, use: \`https://ui.shadcn.com/r/styles/${style}/.json\` For documentation and examples, visit: \`https://ui.shadcn.com/docs/components/${config.base}/\` ` } function buildRtlSection(config: DesignSystemConfig) { const template = config.template === "next-monorepo" ? "next" : (config.template ?? "next") return dedent` ## RTL Support Add \`dir="rtl"\` to your root \`\` element: \`\`\`html \`\`\` For full RTL setup including the \`DirectionProvider\`, see the [RTL documentation](https://ui.shadcn.com/docs/rtl/${template}). ` }