{
	"$schema": "https://ui.shadcn.com/schema/registry-item.json",
	"name": "app-01",
	"type": "registry:block",
	"description": "A app with generative UI capabilities.",
	"dependencies": [
		"ai",
		"@ai-sdk/openai",
		"@uiw/react-codemirror",
		"@codemirror/lang-javascript",
		"zustand"
	],
	"registryDependencies": [
		"avatar",
		"input",
		"label",
		"tabs",
		"resizable",
		"breadcrumb",
		"button",
		"toggle-group",
		"switch",
		"separator",
		"card",
		"badge",
		"dialog",
		"slider",
		"@simple-ai/chat-input",
		"@simple-ai/chat-message-area",
		"@simple-ai/chat-message",
		"@simple-ai/jsx-renderer",
		"@simple-ai/jsx-utils"
	],
	"files": [
		{
			"path": "./src/registry/blocks/app-01/page.tsx",
			"content": "\"use client\";\n\nimport { EditorLayout } from \"@/registry/blocks/app-01/components/editor-layout\";\nimport { Versions } from \"@/registry/blocks/app-01/components/versions\";\n\nexport default function Page() {\n\treturn (\n\t\t<div className=\"flex w-screen h-screen justify-start\">\n\t\t\t<Versions className=\"hidden md:flex\" />\n\t\t\t<EditorLayout />\n\t\t</div>\n\t);\n}\n",
			"type": "registry:page",
			"target": "app/generative-ui/page.tsx"
		},
		{
			"path": "./src/registry/blocks/app-01/canvas/page.tsx",
			"content": "\"use client\";\n\nimport * as LucideIcons from \"lucide-react\";\nimport Script from \"next/script\";\nimport type * as React from \"react\";\nimport { useCallback, useEffect, useMemo, useState } from \"react\";\nimport { Avatar, AvatarFallback, AvatarImage } from \"@/components/ui/avatar\";\nimport { Badge } from \"@/components/ui/badge\";\nimport { Button } from \"@/components/ui/button\";\nimport {\n\tCard,\n\tCardContent,\n\tCardDescription,\n\tCardFooter,\n\tCardHeader,\n\tCardTitle,\n} from \"@/components/ui/card\";\nimport { Input } from \"@/components/ui/input\";\nimport { Label } from \"@/components/ui/label\";\nimport { Slider } from \"@/components/ui/slider\";\nimport { Switch } from \"@/components/ui/switch\";\nimport { Tabs, TabsContent, TabsList, TabsTrigger } from \"@/components/ui/tabs\";\nimport { extractJsxContent } from \"@/registry/lib/jsx-utils\";\nimport { JsxRenderer } from \"@/registry/ui/jsx-renderer\";\n\nexport default function Canvas() {\n\tconst [code, setCode] = useState(\"\");\n\n\tconst handleMessageFromCanvasParent = useCallback((event: MessageEvent) => {\n\t\tconst message = event.data;\n\t\tif (message.type === \"CODE\") {\n\t\t\tconst jsx = extractJsxContent(message.code);\n\t\t\tif (jsx) {\n\t\t\t\tsetCode(jsx);\n\t\t\t}\n\t\t}\n\t}, []);\n\n\tuseEffect(() => {\n\t\twindow.addEventListener(\"message\", handleMessageFromCanvasParent);\n\t\treturn () =>\n\t\t\twindow.removeEventListener(\n\t\t\t\t\"message\",\n\t\t\t\thandleMessageFromCanvasParent,\n\t\t\t);\n\t}, [handleMessageFromCanvasParent]);\n\n\tconst components = useMemo(() => {\n\t\tconst iconComponents = Object.keys(LucideIcons).reduce(\n\t\t\t(acc, iconName) => {\n\t\t\t\tif (iconName === \"default\") {\n\t\t\t\t\treturn acc;\n\t\t\t\t}\n\t\t\t\t//@ts-expect-error\n\t\t\t\t// biome-ignore lint/performance/noDynamicNamespaceImportAccess: TODO: fix this\n\t\t\t\tacc[iconName] = LucideIcons[iconName];\n\t\t\t\treturn acc;\n\t\t\t},\n\t\t\t{} as Record<string, React.ComponentType>,\n\t\t);\n\n\t\treturn {\n\t\t\t...iconComponents,\n\t\t\tButton,\n\t\t\tInput,\n\t\t\tLabel,\n\t\t\tTabs,\n\t\t\tTabsContent,\n\t\t\tTabsList,\n\t\t\tTabsTrigger,\n\t\t\tCard,\n\t\t\tCardContent,\n\t\t\tCardDescription,\n\t\t\tCardFooter,\n\t\t\tCardHeader,\n\t\t\tCardTitle,\n\t\t\tSwitch,\n\t\t\tSlider,\n\t\t\tBadge,\n\t\t\tAvatar,\n\t\t\tAvatarImage,\n\t\t\tAvatarFallback,\n\t\t};\n\t}, []);\n\n\treturn (\n\t\t<>\n\t\t\t<Script src=\"https://cdn.tailwindcss.com\" />\n\t\t\t{/* @ts-ignore Some types are not properly typed */}\n\t\t\t<JsxRenderer jsx={code} components={components} />\n\t\t</>\n\t);\n}\n",
			"type": "registry:page",
			"target": "app/canvas/page.tsx"
		},
		{
			"path": "./src/registry/blocks/app-01/route.ts",
			"content": "import { openai } from \"@ai-sdk/openai\";\nimport { smoothStream, streamText } from \"ai\";\n\nexport async function POST(req: Request) {\n\tconst { prompt, currentCode }: { prompt: string; currentCode: string } =\n\t\tawait req.json();\n\n\tconst result = streamText({\n\t\tmodel: openai(\"gpt-4o-mini\"),\n\t\tsystem: `\n<internal_reminder>\n\t<assistant_info>\n\t\t- Assistant is an expert web developer focused on UI/UX design.\n        - Assistant is an expert in tailwind css, react jsx, and lucide icons.\n        - Assistant is an expert in responsive design and layout combined with best accessibility practices.\n\t\t- Assistant only responds with jsx code\n\t\t- You also get a current_code that will either be null if it is the first version or it will contain the code of the previous generation for which the user might want to make further changes\n\t</assistant_info>\n\t<code_generation_rules>\n\t\ta. Code rules\n\t\t\t- Use JSX syntax with Tailwind CSS classes\n\t\t\t- ALWAYS writes COMPLETE code. NEVER writes partial code snippets or includes comments for the user to fill in.\n\t\t\t- Do not use hooks or inline function calling\n\t\t\t- Do not use any external libraries or frameworks\n\t\t\t- DOES NOT output <svg> for icons. ALWAYS use icons from the \"lucide-react\" package.\n\t\t\t- Do not use inline functions like {[].map(item => <div>{item}</div>)} instead explicitly define all the items you need\n\n\t\tb. Lucide Icon\n            - You can use any Lucide icon from https://lucide.dev/ like this: \\`<IconName className=\"...\" />\\`\n\n\t\tc. shadcn/ui\n\t\t\t- You can use the following shadcn/ui components: Button, Input, Label, Tabs, TabsContent, TabsList, TabsTrigger, Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, Switch, Slider, Badge, Avatar, AvatarImage, AvatarFallback\n\t\t\t- Always prefer to use shadcn/ui components over custom components, if we need to modify the style modify the shadcn components through their props\n\t\t\n\t\td. Style guide\n\t\t\t- Use Tailwind CSS\n\t\t\t- Always generate responsive designs taking into account multiple screen sizes\n\t\t\t- Use h-screen and w-screen for the outer container\n\t\t\t- Define text and background colors using tailwind classes in the outer container, be sure  to handle dark mode\n\n\t\te. Images\n\t\t\t- When using images use placeholder images from https://via.assets.so/img.jpg?w=400&h=150&tc=blue&bg=#cecece\n\t\t\t- We specify w(width) and h(height) in the url and tc is the text color and bg is the background color\n\t</code_generation_rules>\n\t<forming_correct_responses>\n        - Do not use \\`\\`\\`jsx\\`\\`\\` tags, just return the code without indicating that it is jsx code.\n        - Do not respond in any other way that is not the jsx code.\n\t</forming_correct_responses>\n\t<example_output>\n\t\tfunction ExampleComponent() {\n\t\t\treturn (\n\t\t\t\t<div className=\"bg-red-600 text-white dark:text-black dark:bg-white h-screen w-screen flex flex-col items-center justify-center gap-4\">\n\t\t\t\t\t<h1 className=\"text-white\">Hello from the preview</h1>\n\t\t\t\t\t<p className=\"text-white\">This is a paragraph</p>\n\t\t\t\t</div>\n\t\t\t);\n\t\t}\n\t</example_output>\n</internal_reminder>\n<current_code>\n\t${currentCode.length > 0 ? currentCode : \"null\"}\n</current_code>\n\t\t\t`,\n\t\tprompt: prompt,\n\t\texperimental_transform: smoothStream({\n\t\t\tchunking: \"line\",\n\t\t}),\n\t});\n\n\treturn result.toTextStreamResponse();\n}\n",
			"type": "registry:page",
			"target": "app/api/ai/generate/route.ts"
		},
		{
			"path": "./src/registry/blocks/app-01/hooks/generation-store.ts",
			"content": "import { create } from \"zustand\";\n\ninterface Version {\n\tcode: string;\n\tprompt: string;\n\tversionNumber: number;\n\tstatus: \"generating\" | \"complete\";\n}\n\ntype ViewMode = \"preview\" | \"code\";\n\ninterface GenerationStore {\n\tversions: Version[];\n\tcurrentVersion: number;\n\tview: ViewMode;\n\tchatOpen: boolean;\n\taddVersion: (code: string, prompt: string) => void;\n\tsetCurrentVersion: (versionNumber: number) => void;\n\tupdateCurrentCode: (code: string) => void;\n\tupdateStatus: (status: Version[\"status\"]) => void;\n\tsetView: (view: ViewMode) => void;\n\tsetChatOpen: (open: boolean) => void;\n}\n\nexport const useGenerationStore = create<GenerationStore>((set) => ({\n\tversions: [],\n\tcurrentVersion: -1,\n\tview: \"preview\",\n\tchatOpen: true,\n\taddVersion: (code, prompt) =>\n\t\tset((state) => {\n\t\t\tconst newVersion: Version = {\n\t\t\t\tcode,\n\t\t\t\tprompt,\n\t\t\t\tversionNumber: state.versions.length,\n\t\t\t\tstatus: \"generating\",\n\t\t\t};\n\t\t\treturn {\n\t\t\t\tversions: [...state.versions, newVersion],\n\t\t\t\tcurrentVersion: newVersion.versionNumber,\n\t\t\t};\n\t\t}),\n\tsetCurrentVersion: (versionNumber) =>\n\t\tset({ currentVersion: versionNumber }),\n\tupdateCurrentCode: (code) =>\n\t\tset((state) => {\n\t\t\tif (state.currentVersion === -1) {\n\t\t\t\treturn state;\n\t\t\t}\n\n\t\t\tconst updatedVersions = [...state.versions];\n\t\t\tupdatedVersions[state.currentVersion] = {\n\t\t\t\t...updatedVersions[state.currentVersion],\n\t\t\t\tcode,\n\t\t\t};\n\n\t\t\treturn {\n\t\t\t\tversions: updatedVersions,\n\t\t\t};\n\t\t}),\n\tupdateStatus: (status) =>\n\t\tset((state) => {\n\t\t\tif (state.currentVersion === -1) {\n\t\t\t\treturn state;\n\t\t\t}\n\n\t\t\tconst updatedVersions = [...state.versions];\n\t\t\tupdatedVersions[state.currentVersion] = {\n\t\t\t\t...updatedVersions[state.currentVersion],\n\t\t\t\tstatus,\n\t\t\t};\n\n\t\t\treturn {\n\t\t\t\tversions: updatedVersions,\n\t\t\t};\n\t\t}),\n\tsetView: (view) => set({ view }),\n\tsetChatOpen: (open) => set({ chatOpen: open }),\n}));\n",
			"type": "registry:hook"
		},
		{
			"path": "./src/registry/blocks/app-01/components/versions.tsx",
			"content": "import { BotMessageSquare } from \"lucide-react\";\nimport type { ComponentPropsWithoutRef } from \"react\";\nimport { Badge } from \"@/components/ui/badge\";\nimport { Button } from \"@/components/ui/button\";\nimport { Card, CardContent } from \"@/components/ui/card\";\nimport { cn } from \"@/lib/utils\";\nimport { useGenerationStore } from \"@/registry/blocks/app-01/hooks/generation-store\";\n\nexport function Versions({\n\tclassName,\n\t...props\n}: ComponentPropsWithoutRef<\"div\">) {\n\tconst versions = useGenerationStore((state) => state.versions);\n\tconst currentVersion = useGenerationStore((state) => state.currentVersion);\n\tconst setChatOpen = useGenerationStore((state) => state.setChatOpen);\n\tconst currentVersionData = versions[currentVersion];\n\tconst isGenerating = currentVersionData?.status === \"generating\";\n\n\treturn (\n\t\t<div\n\t\t\t{...props}\n\t\t\tclassName={cn(\n\t\t\t\t\"flex flex-col h-full w-[200px] shrink-0 border-r border-border\",\n\t\t\t\tclassName,\n\t\t\t)}\n\t\t>\n\t\t\t<div className=\"flex-1 p-2 space-y-1.5 overflow-y-auto\">\n\t\t\t\t{versions.length === 0 ? (\n\t\t\t\t\t<div className=\"p-4 text-sm text-muted-foreground text-center\">\n\t\t\t\t\t\tNo versions yet\n\t\t\t\t\t</div>\n\t\t\t\t) : (\n\t\t\t\t\tversions.map((version) => (\n\t\t\t\t\t\t<Card\n\t\t\t\t\t\t\tkey={version.versionNumber}\n\t\t\t\t\t\t\tclassName={cn(\n\t\t\t\t\t\t\t\t\"transition-all\",\n\t\t\t\t\t\t\t\tversion.versionNumber === currentVersion &&\n\t\t\t\t\t\t\t\t\t\"border-primary\",\n\t\t\t\t\t\t\t\tversion.status === \"generating\" &&\n\t\t\t\t\t\t\t\t\t\"border-primary animate-pulse\",\n\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t<CardContent className=\"p-3\">\n\t\t\t\t\t\t\t\t<div className=\"flex items-center justify-between\">\n\t\t\t\t\t\t\t\t\t<div className=\"text-xs font-medium\">\n\t\t\t\t\t\t\t\t\t\tVersion {version.versionNumber + 1}\n\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t{version.versionNumber ===\n\t\t\t\t\t\t\t\t\t\tcurrentVersion && (\n\t\t\t\t\t\t\t\t\t\t<Badge\n\t\t\t\t\t\t\t\t\t\t\tvariant=\"secondary\"\n\t\t\t\t\t\t\t\t\t\t\tclassName=\"text-[10px] px-1 py-0\"\n\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t{version.status === \"generating\"\n\t\t\t\t\t\t\t\t\t\t\t\t? \"Loading...\"\n\t\t\t\t\t\t\t\t\t\t\t\t: \"Current\"}\n\t\t\t\t\t\t\t\t\t\t</Badge>\n\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t</CardContent>\n\t\t\t\t\t\t</Card>\n\t\t\t\t\t))\n\t\t\t\t)}\n\t\t\t</div>\n\t\t\t<div className=\"p-2 border-t\">\n\t\t\t\t<Button\n\t\t\t\t\tsize=\"sm\"\n\t\t\t\t\tclassName=\"w-full\"\n\t\t\t\t\tonClick={() => setChatOpen(true)}\n\t\t\t\t\tdisabled={isGenerating}\n\t\t\t\t>\n\t\t\t\t\t<BotMessageSquare className=\"h-4 w-4 mr-2\" />\n\t\t\t\t\tNew generation\n\t\t\t\t</Button>\n\t\t\t</div>\n\t\t</div>\n\t);\n}\n",
			"type": "registry:component"
		},
		{
			"path": "./src/registry/blocks/app-01/components/editor-layout.tsx",
			"content": "\"use client\";\n\nimport { useMemo, useState } from \"react\";\nimport { ChatDialog } from \"@/registry/blocks/app-01/components/chat-dialog\";\nimport { CodeEditor } from \"@/registry/blocks/app-01/components/code-editor\";\nimport { CopyButton } from \"@/registry/blocks/app-01/components/copy-button\";\nimport { EditorToolbar } from \"@/registry/blocks/app-01/components/editor-toolbar\";\nimport {\n\tPreview,\n\tPreviewControls,\n} from \"@/registry/blocks/app-01/components/preview\";\nimport { useGenerationStore } from \"@/registry/blocks/app-01/hooks/generation-store\";\n\nexport const EditorLayout = () => {\n\tconst view = useGenerationStore((state) => state.view);\n\tconst versions = useGenerationStore((state) => state.versions);\n\tconst currentVersion = useGenerationStore((state) => state.currentVersion);\n\tconst currentCode = versions[currentVersion]?.code ?? \"\";\n\tconst [viewerSize, setViewerSize] = useState(\"100\");\n\n\tconst toolbarActions = useMemo(() => {\n\t\tif (view === \"preview\") {\n\t\t\treturn (\n\t\t\t\t<PreviewControls\n\t\t\t\t\tviewerSize={viewerSize}\n\t\t\t\t\tonViewerSizeChange={setViewerSize}\n\t\t\t\t/>\n\t\t\t);\n\t\t}\n\t\tif (view === \"code\") {\n\t\t\treturn <CopyButton value={currentCode} className=\"h-8\" />;\n\t\t}\n\t\treturn null;\n\t}, [view, viewerSize, currentCode]);\n\n\treturn (\n\t\t<div className=\"flex-1 relative py-3 pl-4 pr-1 flex flex-col gap-2 border-l border-border\">\n\t\t\t<EditorToolbar actions={toolbarActions} />\n\t\t\t<Preview\n\t\t\t\tclassName={view === \"preview\" ? \"block\" : \"hidden\"}\n\t\t\t\tviewerSize={viewerSize}\n\t\t\t\tonViewerSizeChange={setViewerSize}\n\t\t\t/>\n\t\t\t<CodeEditor className={view === \"code\" ? \"block\" : \"hidden\"} />\n\t\t\t<ChatDialog />\n\t\t</div>\n\t);\n};\n",
			"type": "registry:component"
		},
		{
			"path": "./src/registry/blocks/app-01/components/editor-toolbar.tsx",
			"content": "\"use client\";\n\nimport { BotMessageSquare, Code, EyeIcon } from \"lucide-react\";\nimport type { FC, ReactNode } from \"react\";\nimport { Button } from \"@/components/ui/button\";\nimport { Separator } from \"@/components/ui/separator\";\nimport { Switch } from \"@/components/ui/switch\";\nimport { useGenerationStore } from \"@/registry/blocks/app-01/hooks/generation-store\";\n\ninterface EditorToolbarProps {\n\tactions?: ReactNode;\n}\n\nexport const EditorToolbar: FC<EditorToolbarProps> = ({ actions }) => {\n\tconst view = useGenerationStore((state) => state.view);\n\tconst setView = useGenerationStore((state) => state.setView);\n\tconst setChatOpen = useGenerationStore((state) => state.setChatOpen);\n\tconst versions = useGenerationStore((state) => state.versions);\n\tconst currentVersion = useGenerationStore((state) => state.currentVersion);\n\tconst currentVersionData = versions[currentVersion];\n\tconst isGenerating = currentVersionData?.status === \"generating\";\n\n\treturn (\n\t\t<div className=\"h-10 flex items-center justify-start gap-4\">\n\t\t\t<Button\n\t\t\t\tsize=\"icon\"\n\t\t\t\tonClick={() => setChatOpen(true)}\n\t\t\t\tdisabled={isGenerating}\n\t\t\t>\n\t\t\t\t<BotMessageSquare className=\"h-4 w-4\" />\n\t\t\t</Button>\n\t\t\t<Separator orientation=\"vertical\" className=\"h-6\" />\n\t\t\t<div className=\"flex items-center gap-1.5\">\n\t\t\t\t<Code className=\"h-4 w-4\" />\n\t\t\t\t<Switch\n\t\t\t\t\tchecked={view === \"preview\"}\n\t\t\t\t\tonCheckedChange={(checked) =>\n\t\t\t\t\t\tsetView(checked ? \"preview\" : \"code\")\n\t\t\t\t\t}\n\t\t\t\t/>\n\t\t\t\t<EyeIcon className=\"h-4 w-4\" />\n\t\t\t</div>\n\t\t\t<Separator orientation=\"vertical\" className=\"h-6\" />\n\t\t\t{actions}\n\t\t</div>\n\t);\n};\n",
			"type": "registry:component"
		},
		{
			"path": "./src/registry/blocks/app-01/components/code-editor.tsx",
			"content": "import { javascript } from \"@codemirror/lang-javascript\";\nimport CodeMirror from \"@uiw/react-codemirror\";\nimport { cn } from \"@/lib/utils\";\nimport { useGenerationStore } from \"@/registry/blocks/app-01/hooks/generation-store\";\n\ninterface CodeEditorProps {\n\tclassName?: string;\n}\n\nexport function CodeEditor({ className }: CodeEditorProps) {\n\tconst versions = useGenerationStore((state) => state.versions);\n\tconst currentVersion = useGenerationStore((state) => state.currentVersion);\n\tconst updateCurrentCode = useGenerationStore(\n\t\t(state) => state.updateCurrentCode,\n\t);\n\n\tconst currentCode = versions[currentVersion]?.code ?? \"\";\n\n\treturn (\n\t\t<div className={cn(\"flex-1 relative overflow-hidden\", className)}>\n\t\t\t<CodeMirror\n\t\t\t\tvalue={currentCode}\n\t\t\t\theight=\"100%\"\n\t\t\t\textensions={[javascript({ jsx: true })]}\n\t\t\t\tonChange={updateCurrentCode}\n\t\t\t\ttheme=\"dark\"\n\t\t\t\tclassName=\"h-full border rounded-md\"\n\t\t\t/>\n\t\t</div>\n\t);\n}\n",
			"type": "registry:component"
		},
		{
			"path": "./src/registry/blocks/app-01/components/preview.tsx",
			"content": "\"use client\";\n\nimport { LoaderCircle, Monitor, Smartphone, Tablet } from \"lucide-react\";\nimport { useCallback, useEffect, useRef } from \"react\";\nimport type { ImperativePanelHandle } from \"react-resizable-panels\";\nimport {\n\tResizableHandle,\n\tResizablePanel,\n\tResizablePanelGroup,\n} from \"@/components/ui/resizable\";\nimport { ToggleGroup, ToggleGroupItem } from \"@/components/ui/toggle-group\";\nimport { cn } from \"@/lib/utils\";\nimport { useGenerationStore } from \"@/registry/blocks/app-01/hooks/generation-store\";\n\ninterface CanvasMessage {\n\ttype: string;\n\tcode: string;\n}\n\ninterface PreviewProps {\n\tclassName?: string;\n\tviewerSize: string;\n\tonViewerSizeChange: (size: string) => void;\n}\n\nexport function PreviewControls({\n\tviewerSize,\n\tonViewerSizeChange,\n}: {\n\tviewerSize: string;\n\tonViewerSizeChange: (value: string) => void;\n}) {\n\treturn (\n\t\t<ToggleGroup\n\t\t\ttype=\"single\"\n\t\t\tsize=\"sm\"\n\t\t\tdefaultValue=\"100\"\n\t\t\tvalue={viewerSize}\n\t\t\tonValueChange={(value) => {\n\t\t\t\tonViewerSizeChange(value);\n\t\t\t}}\n\t\t\tclassName=\"items-center gap-1.5 rounded-md border p-1 shadow-sm flex bg-background\"\n\t\t>\n\t\t\t<ToggleGroupItem value=\"100\" className=\"h-7 w-7 rounded-sm p-0\">\n\t\t\t\t<Monitor className=\"h-3.5 w-3.5\" />\n\t\t\t</ToggleGroupItem>\n\t\t\t<ToggleGroupItem value=\"60\" className=\"h-7 w-7 rounded-sm p-0\">\n\t\t\t\t<Tablet className=\"h-3.5 w-3.5\" />\n\t\t\t</ToggleGroupItem>\n\t\t\t<ToggleGroupItem value=\"35\" className=\"h-7 w-7 rounded-sm p-0\">\n\t\t\t\t<Smartphone className=\"h-3.5 w-3.5\" />\n\t\t\t</ToggleGroupItem>\n\t\t</ToggleGroup>\n\t);\n}\n\nexport function Preview({\n\tclassName,\n\tviewerSize,\n\tonViewerSizeChange,\n}: PreviewProps) {\n\tconst versions = useGenerationStore((state) => state.versions);\n\tconst currentVersion = useGenerationStore((state) => state.currentVersion);\n\tconst currentCode = versions[currentVersion]?.code ?? \"\";\n\tconst isGenerating = versions[currentVersion]?.status === \"generating\";\n\tconst iframeRef = useRef<HTMLIFrameElement>(null);\n\tconst viewerPanelRef = useRef<ImperativePanelHandle>(null);\n\n\tconst sendMessageToCanvas = useCallback((message: CanvasMessage) => {\n\t\tif (iframeRef.current?.contentWindow) {\n\t\t\tiframeRef.current.contentWindow.postMessage(message, \"*\");\n\t\t}\n\t}, []);\n\n\tuseEffect(() => {\n\t\tsendMessageToCanvas({\n\t\t\ttype: \"CODE\",\n\t\t\tcode: currentCode,\n\t\t});\n\t}, [sendMessageToCanvas, currentCode]);\n\n\t// Update panel size when viewerSize changes\n\tuseEffect(() => {\n\t\tif (viewerPanelRef.current) {\n\t\t\tviewerPanelRef.current.resize(Number.parseInt(viewerSize, 10));\n\t\t}\n\t}, [viewerSize]);\n\n\treturn (\n\t\t<div className={cn(\"flex-1 relative\", className)}>\n\t\t\t<div\n\t\t\t\tclassName={cn(\n\t\t\t\t\t\"h-full relative after:absolute after:inset-0 after:right-3 after:z-0 after:rounded-lg after:bg-muted-foreground/25 after:border after:border-border\",\n\t\t\t\t)}\n\t\t\t>\n\t\t\t\t<ResizablePanelGroup\n\t\t\t\t\tid=\"app-01-preview\"\n\t\t\t\t\tdirection=\"horizontal\"\n\t\t\t\t\tclassName=\"relative z-10\"\n\t\t\t\t>\n\t\t\t\t\t<ResizablePanel\n\t\t\t\t\t\tref={viewerPanelRef}\n\t\t\t\t\t\torder={1}\n\t\t\t\t\t\tclassName={cn(\n\t\t\t\t\t\t\t\"relative rounded-lg border bg-background border-border\",\n\t\t\t\t\t\t)}\n\t\t\t\t\t\tdefaultSize={Number.parseInt(viewerSize, 10)}\n\t\t\t\t\t\tonResize={(size) => {\n\t\t\t\t\t\t\tonViewerSizeChange(size.toString());\n\t\t\t\t\t\t}}\n\t\t\t\t\t\tminSize={30}\n\t\t\t\t\t>\n\t\t\t\t\t\t<iframe\n\t\t\t\t\t\t\tref={iframeRef}\n\t\t\t\t\t\t\ttitle=\"block-preview\"\n\t\t\t\t\t\t\tsrc={\"/canvas\"}\n\t\t\t\t\t\t\tclassName=\"relative z-20 w-full h-full bg-background\"\n\t\t\t\t\t\t/>\n\t\t\t\t\t\t{isGenerating && (\n\t\t\t\t\t\t\t<div className=\"absolute inset-0 z-30 bg-background/50 backdrop-blur-[1px] flex items-center justify-center pointer-events-auto animate-pulse\">\n\t\t\t\t\t\t\t\t<LoaderCircle className=\"w-10 h-10 animate-spin\" />\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t)}\n\t\t\t\t\t</ResizablePanel>\n\t\t\t\t\t<ResizableHandle\n\t\t\t\t\t\tclassName={cn(\n\t\t\t\t\t\t\t\"relative w-3 bg-transparent p-0 after:absolute after:right-0 after:top-1/2 after:h-8 after:w-[6px] after:-translate-y-1/2 after:translate-x-[-1px] after:rounded-full after:bg-border after:transition-all after:hover:h-10 pointer-events-none opacity-0 sm:pointer-events-auto sm:opacity-100\",\n\t\t\t\t\t\t)}\n\t\t\t\t\t/>\n\t\t\t\t\t<ResizablePanel defaultSize={0} minSize={0} order={2} />\n\t\t\t\t</ResizablePanelGroup>\n\t\t\t</div>\n\t\t</div>\n\t);\n}\n",
			"type": "registry:component"
		},
		{
			"path": "./src/registry/blocks/app-01/components/chat-dialog.tsx",
			"content": "import { useCompletion } from \"@ai-sdk/react\";\nimport { useEffect } from \"react\";\nimport {\n\tDialog,\n\tDialogContent,\n\tDialogHeader,\n\tDialogTitle,\n} from \"@/components/ui/dialog\";\nimport { useGenerationStore } from \"@/registry/blocks/app-01/hooks/generation-store\";\nimport {\n\tChatInput,\n\tChatInputEditor,\n\tChatInputGroupAddon,\n\tChatInputSubmitButton,\n\tuseChatInput,\n} from \"@/registry/ui/chat-input\";\n\nexport function ChatDialog() {\n\tconst versions = useGenerationStore((state) => state.versions);\n\tconst currentVersion = useGenerationStore((state) => state.currentVersion);\n\tconst setView = useGenerationStore((state) => state.setView);\n\tconst updateCurrentCode = useGenerationStore(\n\t\t(state) => state.updateCurrentCode,\n\t);\n\tconst updateStatus = useGenerationStore((state) => state.updateStatus);\n\tconst addVersion = useGenerationStore((state) => state.addVersion);\n\tconst chatOpen = useGenerationStore((state) => state.chatOpen);\n\tconst setChatOpen = useGenerationStore((state) => state.setChatOpen);\n\n\tconst { completion, isLoading, complete, stop } = useCompletion({\n\t\tapi: \"/api/ai/generate\",\n\t\tonFinish: (_prompt, completion) => {\n\t\t\tsetView(\"preview\");\n\t\t\tupdateCurrentCode(completion);\n\t\t\tupdateStatus(\"complete\");\n\t\t\tsetChatOpen(false);\n\t\t},\n\t\tbody: {\n\t\t\tcurrentCode: versions[currentVersion]?.code ?? \"\",\n\t\t},\n\t});\n\n\tconst {\n\t\tvalue,\n\t\tonChange,\n\t\thandleSubmit: chatInputSubmit,\n\t} = useChatInput({\n\t\tonSubmit: (parsedValue) => {\n\t\t\taddVersion(\"\", parsedValue.content);\n\t\t\tcomplete(parsedValue.content);\n\t\t\tsetChatOpen(false);\n\t\t},\n\t});\n\n\tuseEffect(() => {\n\t\tif (completion) {\n\t\t\tupdateCurrentCode(completion);\n\t\t}\n\t}, [completion, updateCurrentCode]);\n\n\treturn (\n\t\t<Dialog open={chatOpen} onOpenChange={setChatOpen}>\n\t\t\t<DialogContent>\n\t\t\t\t<DialogHeader>\n\t\t\t\t\t<DialogTitle>What UI do you want to build?</DialogTitle>\n\t\t\t\t</DialogHeader>\n\n\t\t\t\t<ChatInput\n\t\t\t\t\tvalue={value}\n\t\t\t\t\tonChange={onChange}\n\t\t\t\t\tonSubmit={chatInputSubmit}\n\t\t\t\t\tisStreaming={isLoading}\n\t\t\t\t\tonStop={stop}\n\t\t\t\t>\n\t\t\t\t\t<ChatInputEditor placeholder=\"Type your code generation prompt...\" />\n\t\t\t\t\t<ChatInputGroupAddon align=\"block-end\">\n\t\t\t\t\t\t<ChatInputSubmitButton className=\"ml-auto\" />\n\t\t\t\t\t</ChatInputGroupAddon>\n\t\t\t\t</ChatInput>\n\t\t\t</DialogContent>\n\t\t</Dialog>\n\t);\n}\n",
			"type": "registry:component"
		},
		{
			"path": "./src/registry/blocks/app-01/components/copy-button.tsx",
			"content": "\"use client\";\n\nimport { Check, Copy } from \"lucide-react\";\nimport { useState } from \"react\";\nimport { Button } from \"@/components/ui/button\";\n\ninterface CopyButtonProps {\n\tvalue: string;\n\tclassName?: string;\n}\n\nexport function CopyButton({ value, className }: CopyButtonProps) {\n\tconst [copied, setCopied] = useState(false);\n\n\tconst handleCopy = async () => {\n\t\tawait navigator.clipboard.writeText(value);\n\t\tsetCopied(true);\n\t\tsetTimeout(() => setCopied(false), 2000);\n\t};\n\n\treturn (\n\t\t<Button\n\t\t\tsize=\"sm\"\n\t\t\tvariant=\"outline\"\n\t\t\tonClick={handleCopy}\n\t\t\tclassName={className}\n\t\t>\n\t\t\t<div className=\"relative w-4 h-4 mr-2\">\n\t\t\t\t<Copy\n\t\t\t\t\tclassName={`h-4 w-4 absolute inset-0 transition-all ${\n\t\t\t\t\t\tcopied ? \"scale-0 opacity-0\" : \"scale-100 opacity-100\"\n\t\t\t\t\t}`}\n\t\t\t\t/>\n\t\t\t\t<Check\n\t\t\t\t\tclassName={`h-4 w-4 absolute inset-0 text-green-500 transition-all ${\n\t\t\t\t\t\tcopied ? \"scale-100 opacity-100\" : \"scale-0 opacity-0\"\n\t\t\t\t\t}`}\n\t\t\t\t/>\n\t\t\t</div>\n\t\t\tCopy\n\t\t</Button>\n\t);\n}\n",
			"type": "registry:component"
		}
	],
	"envVars": {
		"OPENAI_API_KEY": ""
	},
	"docs": "Get an OpenAI API key from https://platform.openai.com/ and add it to the environment variables file and run the dev server. \n\nLearn more about how to use the Vercel AI SDK https://ai-sdk.dev/docs/introduction.",
	"categories": ["app"]
}
