test
This commit is contained in:
commit
22baba9e7b
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
node_modules
|
14
package.json
Normal file
14
package.json
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
{
|
||||||
|
"name": "test-shadcn",
|
||||||
|
"version": "0.0.1",
|
||||||
|
"description": "",
|
||||||
|
"main": "index.js",
|
||||||
|
"scripts": {
|
||||||
|
"test": "echo \"Error: no test specified\" && exit 1"
|
||||||
|
},
|
||||||
|
"keywords": [],
|
||||||
|
"author": "abearxiong <xiongxiao@xiongxiao.me> (https://www.xiongxiao.me)",
|
||||||
|
"license": "MIT",
|
||||||
|
"packageManager": "pnpm@10.6.2",
|
||||||
|
"type": "module"
|
||||||
|
}
|
4511
pnpm-lock.yaml
generated
Normal file
4511
pnpm-lock.yaml
generated
Normal file
File diff suppressed because it is too large
Load Diff
6
pnpm-workspace.yaml
Normal file
6
pnpm-workspace.yaml
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
packages:
|
||||||
|
- 'packages/*'
|
||||||
|
- 'apps/*'
|
||||||
|
- 'registry*/*'
|
||||||
|
- 'vite-react-template'
|
||||||
|
- 'vite-*'
|
41
registry-template/.gitignore
vendored
Normal file
41
registry-template/.gitignore
vendored
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
|
||||||
|
|
||||||
|
# dependencies
|
||||||
|
/node_modules
|
||||||
|
/.pnp
|
||||||
|
.pnp.*
|
||||||
|
.yarn/*
|
||||||
|
!.yarn/patches
|
||||||
|
!.yarn/plugins
|
||||||
|
!.yarn/releases
|
||||||
|
!.yarn/versions
|
||||||
|
|
||||||
|
# testing
|
||||||
|
/coverage
|
||||||
|
|
||||||
|
# next.js
|
||||||
|
/.next/
|
||||||
|
/out/
|
||||||
|
|
||||||
|
# production
|
||||||
|
/build
|
||||||
|
|
||||||
|
# misc
|
||||||
|
.DS_Store
|
||||||
|
*.pem
|
||||||
|
|
||||||
|
# debug
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
.pnpm-debug.log*
|
||||||
|
|
||||||
|
# env files (can opt-in for committing if needed)
|
||||||
|
.env*
|
||||||
|
|
||||||
|
# vercel
|
||||||
|
.vercel
|
||||||
|
|
||||||
|
# typescript
|
||||||
|
*.tsbuildinfo
|
||||||
|
next-env.d.ts
|
21
registry-template/LICENSE
Normal file
21
registry-template/LICENSE
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2025 shadcn
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
23
registry-template/README.md
Normal file
23
registry-template/README.md
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
# registry-template
|
||||||
|
|
||||||
|
You can use the `shadcn` CLI to run your own component registry. Running your own
|
||||||
|
component registry allows you to distribute your custom components, hooks, pages, and
|
||||||
|
other files to any React project.
|
||||||
|
|
||||||
|
> [!IMPORTANT]
|
||||||
|
> This template uses Tailwind v3. For Tailwind v4, see [registry-template](https://github.com/shadcn-ui/registry-template-v4).
|
||||||
|
|
||||||
|
## Getting Started
|
||||||
|
|
||||||
|
This is a template for creating a custom registry using Next.js.
|
||||||
|
|
||||||
|
- The template uses a `registry.json` file to define components and their files.
|
||||||
|
- The `shadcn build` command is used to build the registry.
|
||||||
|
- The registry items are served as static files under `public/r/[name].json`.
|
||||||
|
- The template also includes a route handler for serving registry items.
|
||||||
|
- Every registry item are compatible with the `shadcn` CLI.
|
||||||
|
- We have also added v0 integration using the `Open in v0` api.
|
||||||
|
|
||||||
|
## Documentation
|
||||||
|
|
||||||
|
Visit the [shadcn documentation](https://ui.shadcn.com/docs/registry) to view the full documentation.
|
BIN
registry-template/app/favicon.ico
Normal file
BIN
registry-template/app/favicon.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 25 KiB |
136
registry-template/app/globals.css
Normal file
136
registry-template/app/globals.css
Normal file
@ -0,0 +1,136 @@
|
|||||||
|
@tailwind base;
|
||||||
|
@tailwind components;
|
||||||
|
@tailwind utilities;
|
||||||
|
|
||||||
|
@layer base {
|
||||||
|
:root {
|
||||||
|
--background: 0 0% 100%;
|
||||||
|
--foreground: 240 10% 3.9%;
|
||||||
|
--card: 0 0% 100%;
|
||||||
|
--card-foreground: 240 10% 3.9%;
|
||||||
|
--popover: 0 0% 100%;
|
||||||
|
--popover-foreground: 240 10% 3.9%;
|
||||||
|
--primary: 240 5.9% 10%;
|
||||||
|
--primary-foreground: 0 0% 98%;
|
||||||
|
--secondary: 240 4.8% 95.9%;
|
||||||
|
--secondary-foreground: 240 5.9% 10%;
|
||||||
|
--muted: 240 4.8% 95.9%;
|
||||||
|
--muted-foreground: 240 3.8% 46.1%;
|
||||||
|
--accent: 240 4.8% 95.9%;
|
||||||
|
--accent-foreground: 240 5.9% 10%;
|
||||||
|
--destructive: 0 84.2% 60.2%;
|
||||||
|
--destructive-foreground: 0 0% 98%;
|
||||||
|
--border: 240 5.9% 90%;
|
||||||
|
--input: 240 5.9% 90%;
|
||||||
|
--ring: 240 10% 3.9%;
|
||||||
|
--chart-1: 12 76% 61%;
|
||||||
|
--chart-2: 173 58% 39%;
|
||||||
|
--chart-3: 197 37% 24%;
|
||||||
|
--chart-4: 43 74% 66%;
|
||||||
|
--chart-5: 27 87% 67%;
|
||||||
|
--radius: 0.5rem;
|
||||||
|
--sidebar-background: 0 0% 98%;
|
||||||
|
--sidebar-foreground: 240 5.3% 26.1%;
|
||||||
|
--sidebar-primary: 240 5.9% 10%;
|
||||||
|
--sidebar-primary-foreground: 0 0% 98%;
|
||||||
|
--sidebar-accent: 240 4.8% 95.9%;
|
||||||
|
--sidebar-accent-foreground: 240 5.9% 10%;
|
||||||
|
--sidebar-border: 220 13% 91%;
|
||||||
|
--sidebar-ring: 217.2 91.2% 59.8%;
|
||||||
|
}
|
||||||
|
.dark {
|
||||||
|
--background: 240 10% 3.9%;
|
||||||
|
--foreground: 0 0% 98%;
|
||||||
|
--card: 240 10% 3.9%;
|
||||||
|
--card-foreground: 0 0% 98%;
|
||||||
|
--popover: 240 10% 3.9%;
|
||||||
|
--popover-foreground: 0 0% 98%;
|
||||||
|
--primary: 0 0% 98%;
|
||||||
|
--primary-foreground: 240 5.9% 10%;
|
||||||
|
--secondary: 240 3.7% 15.9%;
|
||||||
|
--secondary-foreground: 0 0% 98%;
|
||||||
|
--muted: 240 3.7% 15.9%;
|
||||||
|
--muted-foreground: 240 5% 64.9%;
|
||||||
|
--accent: 240 3.7% 15.9%;
|
||||||
|
--accent-foreground: 0 0% 98%;
|
||||||
|
--destructive: 0 62.8% 30.6%;
|
||||||
|
--destructive-foreground: 0 0% 98%;
|
||||||
|
--border: 240 3.7% 15.9%;
|
||||||
|
--input: 240 3.7% 15.9%;
|
||||||
|
--ring: 240 4.9% 83.9%;
|
||||||
|
--chart-1: 220 70% 50%;
|
||||||
|
--chart-2: 160 60% 45%;
|
||||||
|
--chart-3: 30 80% 55%;
|
||||||
|
--chart-4: 280 65% 60%;
|
||||||
|
--chart-5: 340 75% 55%;
|
||||||
|
--sidebar-background: 240 5.9% 10%;
|
||||||
|
--sidebar-foreground: 240 4.8% 95.9%;
|
||||||
|
--sidebar-primary: 224.3 76.3% 48%;
|
||||||
|
--sidebar-primary-foreground: 0 0% 100%;
|
||||||
|
--sidebar-accent: 240 3.7% 15.9%;
|
||||||
|
--sidebar-accent-foreground: 240 4.8% 95.9%;
|
||||||
|
--sidebar-border: 240 3.7% 15.9%;
|
||||||
|
--sidebar-ring: 217.2 91.2% 59.8%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@layer base {
|
||||||
|
* {
|
||||||
|
@apply border-border;
|
||||||
|
}
|
||||||
|
body {
|
||||||
|
@apply bg-background text-foreground;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-rehype-pretty-code-fragment] {
|
||||||
|
@apply relative text-white;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-rehype-pretty-code-fragment] code {
|
||||||
|
@apply grid min-w-full break-words rounded-none border-0 bg-transparent p-0;
|
||||||
|
counter-reset: line;
|
||||||
|
box-decoration-break: clone;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-rehype-pretty-code-fragment] .line {
|
||||||
|
@apply px-4 min-h-[1rem] py-0.5 w-full inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-rehype-pretty-code-fragment] [data-line-numbers] .line {
|
||||||
|
@apply px-2;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-rehype-pretty-code-fragment] [data-line-numbers] > .line::before {
|
||||||
|
@apply text-zinc-50/40 text-xs;
|
||||||
|
counter-increment: line;
|
||||||
|
content: counter(line);
|
||||||
|
display: inline-block;
|
||||||
|
width: 1.8rem;
|
||||||
|
margin-right: 1.4rem;
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-rehype-pretty-code-fragment] .line--highlighted {
|
||||||
|
@apply bg-zinc-700/50;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-rehype-pretty-code-fragment] .line-highlighted span {
|
||||||
|
@apply relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-rehype-pretty-code-fragment] .word--highlighted {
|
||||||
|
@apply rounded-md bg-zinc-700/50 border-zinc-700/70 p-1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dark [data-rehype-pretty-code-fragment] .word--highlighted {
|
||||||
|
@apply bg-zinc-900;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-rehype-pretty-code-title] {
|
||||||
|
@apply mt-2 pt-6 px-4 text-sm font-medium text-foreground;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-rehype-pretty-code-title] + pre {
|
||||||
|
@apply mt-2;
|
||||||
|
}
|
34
registry-template/app/layout.tsx
Normal file
34
registry-template/app/layout.tsx
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
import type { Metadata } from "next";
|
||||||
|
import { Geist, Geist_Mono } from "next/font/google";
|
||||||
|
import "./globals.css";
|
||||||
|
|
||||||
|
const geistSans = Geist({
|
||||||
|
variable: "--font-geist-sans",
|
||||||
|
subsets: ["latin"],
|
||||||
|
});
|
||||||
|
|
||||||
|
const geistMono = Geist_Mono({
|
||||||
|
variable: "--font-geist-mono",
|
||||||
|
subsets: ["latin"],
|
||||||
|
});
|
||||||
|
|
||||||
|
export const metadata: Metadata = {
|
||||||
|
title: "Create Next App",
|
||||||
|
description: "Generated by create next app",
|
||||||
|
};
|
||||||
|
|
||||||
|
export default function RootLayout({
|
||||||
|
children,
|
||||||
|
}: Readonly<{
|
||||||
|
children: React.ReactNode;
|
||||||
|
}>) {
|
||||||
|
return (
|
||||||
|
<html lang="en">
|
||||||
|
<body
|
||||||
|
className={`${geistSans.variable} ${geistMono.variable} antialiased`}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
);
|
||||||
|
}
|
58
registry-template/app/page.tsx
Normal file
58
registry-template/app/page.tsx
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
import * as React from "react"
|
||||||
|
import { OpenInV0Button } from "@/components/open-in-v0-button"
|
||||||
|
import { HelloWorld } from "@/registry/new-york/hello-world/hello-world"
|
||||||
|
import { ExampleForm } from "@/registry/new-york/example-form/example-form"
|
||||||
|
import PokemonPage from "@/registry/new-york/complex-component/page"
|
||||||
|
|
||||||
|
// This page displays items from the custom registry.
|
||||||
|
// You are free to implement this with your own design as needed.
|
||||||
|
|
||||||
|
export default function Home() {
|
||||||
|
return (
|
||||||
|
<div className="max-w-3xl mx-auto flex flex-col min-h-svh px-4 py-8 gap-8">
|
||||||
|
<header className="flex flex-col gap-1">
|
||||||
|
<h1 className="text-3xl font-bold tracking-tight">Custom Registry</h1>
|
||||||
|
<p className="text-muted-foreground">
|
||||||
|
A custom registry for distributing code using shadcn.
|
||||||
|
</p>
|
||||||
|
</header>
|
||||||
|
<main className="flex flex-col flex-1 gap-8">
|
||||||
|
<div className="flex flex-col gap-4 border rounded-lg p-4 min-h-[450px] relative">
|
||||||
|
<div className="flex items-center justify-between">
|
||||||
|
<h2 className="text-sm text-muted-foreground sm:pl-3">
|
||||||
|
A simple hello world component
|
||||||
|
</h2>
|
||||||
|
<OpenInV0Button name="hello-world" className="w-fit" />
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center justify-center min-h-[400px] relative">
|
||||||
|
<HelloWorld />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex flex-col gap-4 border rounded-lg p-4 min-h-[450px] relative">
|
||||||
|
<div className="flex items-center justify-between">
|
||||||
|
<h2 className="text-sm text-muted-foreground sm:pl-3">
|
||||||
|
A contact form with Zod validation.
|
||||||
|
</h2>
|
||||||
|
<OpenInV0Button name="example-form" className="w-fit" />
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center justify-center min-h-[500px] relative">
|
||||||
|
<ExampleForm />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex flex-col gap-4 border rounded-lg p-4 min-h-[450px] relative">
|
||||||
|
<div className="flex items-center justify-between">
|
||||||
|
<h2 className="text-sm text-muted-foreground sm:pl-3">
|
||||||
|
A complex component showing hooks, libs and components.
|
||||||
|
</h2>
|
||||||
|
<OpenInV0Button name="complex-component" className="w-fit" />
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center justify-center min-h-[400px] relative">
|
||||||
|
<PokemonPage />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
64
registry-template/app/registry/[name]/route.ts
Normal file
64
registry-template/app/registry/[name]/route.ts
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
import { NextResponse } from "next/server"
|
||||||
|
import path from "path"
|
||||||
|
import { promises as fs } from "fs"
|
||||||
|
import { registryItemSchema } from "shadcn/registry"
|
||||||
|
|
||||||
|
// Use the registry.json file to generate static paths.
|
||||||
|
export const generateStaticParams = async () => {
|
||||||
|
const registryData = await import("@/registry.json");
|
||||||
|
const registry = registryData.default;
|
||||||
|
|
||||||
|
return registry.items.map((item) => ({
|
||||||
|
name: item.name,
|
||||||
|
}));
|
||||||
|
};
|
||||||
|
|
||||||
|
// This route shows an example for serving a component using a route handler.
|
||||||
|
export async function GET(
|
||||||
|
request: Request,
|
||||||
|
{ params }: { params: Promise<{ name: string }> }
|
||||||
|
) {
|
||||||
|
try {
|
||||||
|
const { name } = await params
|
||||||
|
// Cache the registry import
|
||||||
|
const registryData = await import("@/registry.json")
|
||||||
|
const registry = registryData.default
|
||||||
|
|
||||||
|
// Find the component from the registry.
|
||||||
|
const component = registry.items.find((c) => c.name === name)
|
||||||
|
|
||||||
|
// If the component is not found, return a 404 error.
|
||||||
|
if (!component) {
|
||||||
|
return NextResponse.json(
|
||||||
|
{ error: "Component not found" },
|
||||||
|
{ status: 404 }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate before file operations.
|
||||||
|
const registryItem = registryItemSchema.parse(component)
|
||||||
|
|
||||||
|
// If the component has no files, return a 400 error.
|
||||||
|
if (!registryItem.files?.length) {
|
||||||
|
return NextResponse.json(
|
||||||
|
{ error: "Component has no files" },
|
||||||
|
{ status: 400 }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read all files in parallel.
|
||||||
|
const filesWithContent = await Promise.all(
|
||||||
|
registryItem.files.map(async (file) => {
|
||||||
|
const filePath = path.join(process.cwd(), file.path)
|
||||||
|
const content = await fs.readFile(filePath, "utf8")
|
||||||
|
return { ...file, content }
|
||||||
|
})
|
||||||
|
)
|
||||||
|
|
||||||
|
// Return the component with the files.
|
||||||
|
return NextResponse.json({ ...registryItem, files: filesWithContent })
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error processing component request:", error)
|
||||||
|
return NextResponse.json({ error: "Something went wrong" }, { status: 500 })
|
||||||
|
}
|
||||||
|
}
|
21
registry-template/components.json
Normal file
21
registry-template/components.json
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
{
|
||||||
|
"$schema": "https://ui.shadcn.com/schema.json",
|
||||||
|
"style": "new-york",
|
||||||
|
"rsc": true,
|
||||||
|
"tsx": true,
|
||||||
|
"tailwind": {
|
||||||
|
"config": "tailwind.config.ts",
|
||||||
|
"css": "app/globals.css",
|
||||||
|
"baseColor": "zinc",
|
||||||
|
"cssVariables": true,
|
||||||
|
"prefix": ""
|
||||||
|
},
|
||||||
|
"aliases": {
|
||||||
|
"components": "@/components",
|
||||||
|
"utils": "@/lib/utils",
|
||||||
|
"ui": "@/components/ui",
|
||||||
|
"lib": "@/lib",
|
||||||
|
"hooks": "@/hooks"
|
||||||
|
},
|
||||||
|
"iconLibrary": "lucide"
|
||||||
|
}
|
29
registry-template/components/mode-toggle.tsx
Normal file
29
registry-template/components/mode-toggle.tsx
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
"use client"
|
||||||
|
|
||||||
|
import * as React from "react"
|
||||||
|
import { useTheme } from "next-themes"
|
||||||
|
|
||||||
|
import { Moon, Sun } from "lucide-react"
|
||||||
|
import { Button } from "@/components/ui/button"
|
||||||
|
|
||||||
|
export function ModeToggle() {
|
||||||
|
const { setTheme, resolvedTheme } = useTheme()
|
||||||
|
const [, startTransition] = React.useTransition()
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Button
|
||||||
|
className="h-7 w-7"
|
||||||
|
onClick={() => {
|
||||||
|
startTransition(() => {
|
||||||
|
setTheme(resolvedTheme === "dark" ? "light" : "dark")
|
||||||
|
})
|
||||||
|
}}
|
||||||
|
size="icon"
|
||||||
|
variant="ghost"
|
||||||
|
>
|
||||||
|
<Moon className="dark:hidden" />
|
||||||
|
<Sun className="hidden dark:block" />
|
||||||
|
<span className="sr-only">Toggle theme</span>
|
||||||
|
</Button>
|
||||||
|
)
|
||||||
|
}
|
41
registry-template/components/open-in-v0-button.tsx
Normal file
41
registry-template/components/open-in-v0-button.tsx
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
import { Button } from "@/components/ui/button"
|
||||||
|
import { cn } from "@/lib/utils"
|
||||||
|
|
||||||
|
export function OpenInV0Button({
|
||||||
|
name,
|
||||||
|
className,
|
||||||
|
}: { name: string } & React.ComponentProps<typeof Button>) {
|
||||||
|
return (
|
||||||
|
<Button
|
||||||
|
aria-label="Open in v0"
|
||||||
|
className={cn(
|
||||||
|
"h-7 gap-1 rounded-lg shadow-none bg-black px-3 text-xs text-white hover:bg-black hover:text-white dark:bg-white dark:text-black",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
asChild
|
||||||
|
>
|
||||||
|
<a
|
||||||
|
href={`https://v0.dev/chat/api/open?url=${process.env.NEXT_PUBLIC_BASE_URL}/r/${name}.json`}
|
||||||
|
target="_blank"
|
||||||
|
rel="noreferrer"
|
||||||
|
>
|
||||||
|
Open in{" "}
|
||||||
|
<svg
|
||||||
|
viewBox="0 0 40 20"
|
||||||
|
fill="none"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
className="h-5 w-5 text-current"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
d="M23.3919 0H32.9188C36.7819 0 39.9136 3.13165 39.9136 6.99475V16.0805H36.0006V6.99475C36.0006 6.90167 35.9969 6.80925 35.9898 6.71766L26.4628 16.079C26.4949 16.08 26.5272 16.0805 26.5595 16.0805H36.0006V19.7762H26.5595C22.6964 19.7762 19.4788 16.6139 19.4788 12.7508V3.68923H23.3919V12.7508C23.3919 12.9253 23.4054 13.0977 23.4316 13.2668L33.1682 3.6995C33.0861 3.6927 33.003 3.68923 32.9188 3.68923H23.3919V0Z"
|
||||||
|
fill="currentColor"
|
||||||
|
></path>
|
||||||
|
<path
|
||||||
|
d="M13.7688 19.0956L0 3.68759H5.53933L13.6231 12.7337V3.68759H17.7535V17.5746C17.7535 19.6705 15.1654 20.6584 13.7688 19.0956Z"
|
||||||
|
fill="currentColor"
|
||||||
|
></path>
|
||||||
|
</svg>
|
||||||
|
</a>
|
||||||
|
</Button>
|
||||||
|
)
|
||||||
|
}
|
16
registry-template/components/providers.tsx
Normal file
16
registry-template/components/providers.tsx
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
"use client"
|
||||||
|
|
||||||
|
import { ThemeProvider } from "next-themes"
|
||||||
|
|
||||||
|
export function Providers({ children }: { children: React.ReactNode }) {
|
||||||
|
return (
|
||||||
|
<ThemeProvider
|
||||||
|
attribute="class"
|
||||||
|
defaultTheme="system"
|
||||||
|
disableTransitionOnChange
|
||||||
|
enableSystem
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</ThemeProvider>
|
||||||
|
)
|
||||||
|
}
|
18
registry-template/components/tailwind-indicator.tsx
Normal file
18
registry-template/components/tailwind-indicator.tsx
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
export function TailwindIndicator() {
|
||||||
|
if (
|
||||||
|
process.env.NODE_ENV === "production" ||
|
||||||
|
process.env.HIDE_TAILWIND_INDICATOR === "1"
|
||||||
|
)
|
||||||
|
return null
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="fixed bottom-1 left-1 z-50 flex h-6 w-6 items-center justify-center rounded-full bg-zinc-800 p-3 font-mono text-xs text-white">
|
||||||
|
<div className="block sm:hidden">xs</div>
|
||||||
|
<div className="hidden sm:block md:hidden">sm</div>
|
||||||
|
<div className="hidden md:block lg:hidden">md</div>
|
||||||
|
<div className="hidden lg:block xl:hidden">lg</div>
|
||||||
|
<div className="hidden xl:block 2xl:hidden">xl</div>
|
||||||
|
<div className="hidden 2xl:block">2xl</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
57
registry-template/components/ui/button.tsx
Normal file
57
registry-template/components/ui/button.tsx
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
import * as React from "react"
|
||||||
|
import { Slot } from "@radix-ui/react-slot"
|
||||||
|
import { cva, type VariantProps } from "class-variance-authority"
|
||||||
|
|
||||||
|
import { cn } from "@/lib/utils"
|
||||||
|
|
||||||
|
const buttonVariants = cva(
|
||||||
|
"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",
|
||||||
|
{
|
||||||
|
variants: {
|
||||||
|
variant: {
|
||||||
|
default:
|
||||||
|
"bg-primary text-primary-foreground shadow hover:bg-primary/90",
|
||||||
|
destructive:
|
||||||
|
"bg-destructive text-destructive-foreground shadow-sm hover:bg-destructive/90",
|
||||||
|
outline:
|
||||||
|
"border border-input bg-background shadow-sm hover:bg-accent hover:text-accent-foreground",
|
||||||
|
secondary:
|
||||||
|
"bg-secondary text-secondary-foreground shadow-sm hover:bg-secondary/80",
|
||||||
|
ghost: "hover:bg-accent hover:text-accent-foreground",
|
||||||
|
link: "text-primary underline-offset-4 hover:underline",
|
||||||
|
},
|
||||||
|
size: {
|
||||||
|
default: "h-9 px-4 py-2",
|
||||||
|
sm: "h-8 rounded-md px-3 text-xs",
|
||||||
|
lg: "h-10 rounded-md px-8",
|
||||||
|
icon: "h-9 w-9",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
defaultVariants: {
|
||||||
|
variant: "default",
|
||||||
|
size: "default",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
export interface ButtonProps
|
||||||
|
extends React.ButtonHTMLAttributes<HTMLButtonElement>,
|
||||||
|
VariantProps<typeof buttonVariants> {
|
||||||
|
asChild?: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
|
||||||
|
({ className, variant, size, asChild = false, ...props }, ref) => {
|
||||||
|
const Comp = asChild ? Slot : "button"
|
||||||
|
return (
|
||||||
|
<Comp
|
||||||
|
className={cn(buttonVariants({ variant, size, className }))}
|
||||||
|
ref={ref}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
Button.displayName = "Button"
|
||||||
|
|
||||||
|
export { Button, buttonVariants }
|
76
registry-template/components/ui/card.tsx
Normal file
76
registry-template/components/ui/card.tsx
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
import * as React from "react"
|
||||||
|
|
||||||
|
import { cn } from "@/lib/utils"
|
||||||
|
|
||||||
|
const Card = React.forwardRef<
|
||||||
|
HTMLDivElement,
|
||||||
|
React.HTMLAttributes<HTMLDivElement>
|
||||||
|
>(({ className, ...props }, ref) => (
|
||||||
|
<div
|
||||||
|
ref={ref}
|
||||||
|
className={cn(
|
||||||
|
"rounded-xl border bg-card text-card-foreground shadow",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
))
|
||||||
|
Card.displayName = "Card"
|
||||||
|
|
||||||
|
const CardHeader = React.forwardRef<
|
||||||
|
HTMLDivElement,
|
||||||
|
React.HTMLAttributes<HTMLDivElement>
|
||||||
|
>(({ className, ...props }, ref) => (
|
||||||
|
<div
|
||||||
|
ref={ref}
|
||||||
|
className={cn("flex flex-col space-y-1.5 p-6", className)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
))
|
||||||
|
CardHeader.displayName = "CardHeader"
|
||||||
|
|
||||||
|
const CardTitle = React.forwardRef<
|
||||||
|
HTMLDivElement,
|
||||||
|
React.HTMLAttributes<HTMLDivElement>
|
||||||
|
>(({ className, ...props }, ref) => (
|
||||||
|
<div
|
||||||
|
ref={ref}
|
||||||
|
className={cn("font-semibold leading-none tracking-tight", className)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
))
|
||||||
|
CardTitle.displayName = "CardTitle"
|
||||||
|
|
||||||
|
const CardDescription = React.forwardRef<
|
||||||
|
HTMLDivElement,
|
||||||
|
React.HTMLAttributes<HTMLDivElement>
|
||||||
|
>(({ className, ...props }, ref) => (
|
||||||
|
<div
|
||||||
|
ref={ref}
|
||||||
|
className={cn("text-sm text-muted-foreground", className)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
))
|
||||||
|
CardDescription.displayName = "CardDescription"
|
||||||
|
|
||||||
|
const CardContent = React.forwardRef<
|
||||||
|
HTMLDivElement,
|
||||||
|
React.HTMLAttributes<HTMLDivElement>
|
||||||
|
>(({ className, ...props }, ref) => (
|
||||||
|
<div ref={ref} className={cn("p-6 pt-0", className)} {...props} />
|
||||||
|
))
|
||||||
|
CardContent.displayName = "CardContent"
|
||||||
|
|
||||||
|
const CardFooter = React.forwardRef<
|
||||||
|
HTMLDivElement,
|
||||||
|
React.HTMLAttributes<HTMLDivElement>
|
||||||
|
>(({ className, ...props }, ref) => (
|
||||||
|
<div
|
||||||
|
ref={ref}
|
||||||
|
className={cn("flex items-center p-6 pt-0", className)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
))
|
||||||
|
CardFooter.displayName = "CardFooter"
|
||||||
|
|
||||||
|
export { Card, CardHeader, CardFooter, CardTitle, CardDescription, CardContent }
|
22
registry-template/components/ui/input.tsx
Normal file
22
registry-template/components/ui/input.tsx
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
import * as React from "react"
|
||||||
|
|
||||||
|
import { cn } from "@/lib/utils"
|
||||||
|
|
||||||
|
const Input = React.forwardRef<HTMLInputElement, React.ComponentProps<"input">>(
|
||||||
|
({ className, type, ...props }, ref) => {
|
||||||
|
return (
|
||||||
|
<input
|
||||||
|
type={type}
|
||||||
|
className={cn(
|
||||||
|
"flex h-9 w-full rounded-md border border-input bg-transparent px-3 py-1 text-base shadow-sm transition-colors file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50 md:text-sm",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
ref={ref}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
Input.displayName = "Input"
|
||||||
|
|
||||||
|
export { Input }
|
26
registry-template/components/ui/label.tsx
Normal file
26
registry-template/components/ui/label.tsx
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
"use client"
|
||||||
|
|
||||||
|
import * as React from "react"
|
||||||
|
import * as LabelPrimitive from "@radix-ui/react-label"
|
||||||
|
import { cva, type VariantProps } from "class-variance-authority"
|
||||||
|
|
||||||
|
import { cn } from "@/lib/utils"
|
||||||
|
|
||||||
|
const labelVariants = cva(
|
||||||
|
"text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
|
||||||
|
)
|
||||||
|
|
||||||
|
const Label = React.forwardRef<
|
||||||
|
React.ElementRef<typeof LabelPrimitive.Root>,
|
||||||
|
React.ComponentPropsWithoutRef<typeof LabelPrimitive.Root> &
|
||||||
|
VariantProps<typeof labelVariants>
|
||||||
|
>(({ className, ...props }, ref) => (
|
||||||
|
<LabelPrimitive.Root
|
||||||
|
ref={ref}
|
||||||
|
className={cn(labelVariants(), className)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
))
|
||||||
|
Label.displayName = LabelPrimitive.Root.displayName
|
||||||
|
|
||||||
|
export { Label }
|
22
registry-template/components/ui/textarea.tsx
Normal file
22
registry-template/components/ui/textarea.tsx
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
import * as React from "react"
|
||||||
|
|
||||||
|
import { cn } from "@/lib/utils"
|
||||||
|
|
||||||
|
const Textarea = React.forwardRef<
|
||||||
|
HTMLTextAreaElement,
|
||||||
|
React.ComponentProps<"textarea">
|
||||||
|
>(({ className, ...props }, ref) => {
|
||||||
|
return (
|
||||||
|
<textarea
|
||||||
|
className={cn(
|
||||||
|
"flex min-h-[60px] w-full rounded-md border border-input bg-transparent px-3 py-2 text-base shadow-sm placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50 md:text-sm",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
ref={ref}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
})
|
||||||
|
Textarea.displayName = "Textarea"
|
||||||
|
|
||||||
|
export { Textarea }
|
16
registry-template/eslint.config.mjs
Normal file
16
registry-template/eslint.config.mjs
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
import { dirname } from "path";
|
||||||
|
import { fileURLToPath } from "url";
|
||||||
|
import { FlatCompat } from "@eslint/eslintrc";
|
||||||
|
|
||||||
|
const __filename = fileURLToPath(import.meta.url);
|
||||||
|
const __dirname = dirname(__filename);
|
||||||
|
|
||||||
|
const compat = new FlatCompat({
|
||||||
|
baseDirectory: __dirname,
|
||||||
|
});
|
||||||
|
|
||||||
|
const eslintConfig = [
|
||||||
|
...compat.extends("next/core-web-vitals", "next/typescript"),
|
||||||
|
];
|
||||||
|
|
||||||
|
export default eslintConfig;
|
6
registry-template/lib/utils.ts
Normal file
6
registry-template/lib/utils.ts
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
import { clsx, type ClassValue } from "clsx"
|
||||||
|
import { twMerge } from "tailwind-merge"
|
||||||
|
|
||||||
|
export function cn(...inputs: ClassValue[]) {
|
||||||
|
return twMerge(clsx(inputs))
|
||||||
|
}
|
10
registry-template/next.config.ts
Normal file
10
registry-template/next.config.ts
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
import type { NextConfig } from "next";
|
||||||
|
|
||||||
|
const nextConfig: NextConfig = {
|
||||||
|
outputFileTracingIncludes: {
|
||||||
|
registry: ["./registry/**/*"],
|
||||||
|
},
|
||||||
|
/* config options here */
|
||||||
|
};
|
||||||
|
|
||||||
|
export default nextConfig;
|
39
registry-template/package.json
Normal file
39
registry-template/package.json
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
{
|
||||||
|
"name": "registry-template",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"private": true,
|
||||||
|
"scripts": {
|
||||||
|
"dev": "next dev --turbopack",
|
||||||
|
"build": "next build",
|
||||||
|
"start": "next start",
|
||||||
|
"lint": "next lint",
|
||||||
|
"registry:build": "shadcn build"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@radix-ui/react-label": "^2.1.1",
|
||||||
|
"@radix-ui/react-slot": "^1.1.1",
|
||||||
|
"@tanstack/react-query": "^5.64.1",
|
||||||
|
"class-variance-authority": "^0.7.1",
|
||||||
|
"clsx": "^2.1.1",
|
||||||
|
"lucide-react": "^0.471.1",
|
||||||
|
"next": "15.1.4",
|
||||||
|
"next-themes": "^0.4.4",
|
||||||
|
"react": "^19.0.0",
|
||||||
|
"react-dom": "^19.0.0",
|
||||||
|
"shadcn": "2.4.0-canary.12",
|
||||||
|
"tailwind-merge": "^2.6.0",
|
||||||
|
"tailwindcss-animate": "^1.0.7",
|
||||||
|
"zod": "^3.24.1"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@eslint/eslintrc": "^3",
|
||||||
|
"@types/node": "^20",
|
||||||
|
"@types/react": "^19",
|
||||||
|
"@types/react-dom": "^19",
|
||||||
|
"eslint": "^9",
|
||||||
|
"eslint-config-next": "15.1.4",
|
||||||
|
"postcss": "^8",
|
||||||
|
"tailwindcss": "^3.4.1",
|
||||||
|
"typescript": "^5"
|
||||||
|
}
|
||||||
|
}
|
5029
registry-template/pnpm-lock.yaml
generated
Normal file
5029
registry-template/pnpm-lock.yaml
generated
Normal file
File diff suppressed because it is too large
Load Diff
8
registry-template/postcss.config.mjs
Normal file
8
registry-template/postcss.config.mjs
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
/** @type {import('postcss-load-config').Config} */
|
||||||
|
const config = {
|
||||||
|
plugins: {
|
||||||
|
tailwindcss: {},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export default config;
|
1
registry-template/public/file.svg
Normal file
1
registry-template/public/file.svg
Normal file
@ -0,0 +1 @@
|
|||||||
|
<svg fill="none" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path d="M14.5 13.5V5.41a1 1 0 0 0-.3-.7L9.8.29A1 1 0 0 0 9.08 0H1.5v13.5A2.5 2.5 0 0 0 4 16h8a2.5 2.5 0 0 0 2.5-2.5m-1.5 0v-7H8v-5H3v12a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1M9.5 5V2.12L12.38 5zM5.13 5h-.62v1.25h2.12V5zm-.62 3h7.12v1.25H4.5zm.62 3h-.62v1.25h7.12V11z" clip-rule="evenodd" fill="#666" fill-rule="evenodd"/></svg>
|
After Width: | Height: | Size: 391 B |
1
registry-template/public/globe.svg
Normal file
1
registry-template/public/globe.svg
Normal file
@ -0,0 +1 @@
|
|||||||
|
<svg fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><g clip-path="url(#a)"><path fill-rule="evenodd" clip-rule="evenodd" d="M10.27 14.1a6.5 6.5 0 0 0 3.67-3.45q-1.24.21-2.7.34-.31 1.83-.97 3.1M8 16A8 8 0 1 0 8 0a8 8 0 0 0 0 16m.48-1.52a7 7 0 0 1-.96 0H7.5a4 4 0 0 1-.84-1.32q-.38-.89-.63-2.08a40 40 0 0 0 3.92 0q-.25 1.2-.63 2.08a4 4 0 0 1-.84 1.31zm2.94-4.76q1.66-.15 2.95-.43a7 7 0 0 0 0-2.58q-1.3-.27-2.95-.43a18 18 0 0 1 0 3.44m-1.27-3.54a17 17 0 0 1 0 3.64 39 39 0 0 1-4.3 0 17 17 0 0 1 0-3.64 39 39 0 0 1 4.3 0m1.1-1.17q1.45.13 2.69.34a6.5 6.5 0 0 0-3.67-3.44q.65 1.26.98 3.1M8.48 1.5l.01.02q.41.37.84 1.31.38.89.63 2.08a40 40 0 0 0-3.92 0q.25-1.2.63-2.08a4 4 0 0 1 .85-1.32 7 7 0 0 1 .96 0m-2.75.4a6.5 6.5 0 0 0-3.67 3.44 29 29 0 0 1 2.7-.34q.31-1.83.97-3.1M4.58 6.28q-1.66.16-2.95.43a7 7 0 0 0 0 2.58q1.3.27 2.95.43a18 18 0 0 1 0-3.44m.17 4.71q-1.45-.12-2.69-.34a6.5 6.5 0 0 0 3.67 3.44q-.65-1.27-.98-3.1" fill="#666"/></g><defs><clipPath id="a"><path fill="#fff" d="M0 0h16v16H0z"/></clipPath></defs></svg>
|
After Width: | Height: | Size: 1.0 KiB |
1
registry-template/public/next.svg
Normal file
1
registry-template/public/next.svg
Normal file
@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 394 80"><path fill="#000" d="M262 0h68.5v12.7h-27.2v66.6h-13.6V12.7H262V0ZM149 0v12.7H94v20.4h44.3v12.6H94v21h55v12.6H80.5V0h68.7zm34.3 0h-17.8l63.8 79.4h17.9l-32-39.7 32-39.6h-17.9l-23 28.6-23-28.6zm18.3 56.7-9-11-27.1 33.7h17.8l18.3-22.7z"/><path fill="#000" d="M81 79.3 17 0H0v79.3h13.6V17l50.2 62.3H81Zm252.6-.4c-1 0-1.8-.4-2.5-1s-1.1-1.6-1.1-2.6.3-1.8 1-2.5 1.6-1 2.6-1 1.8.3 2.5 1a3.4 3.4 0 0 1 .6 4.3 3.7 3.7 0 0 1-3 1.8zm23.2-33.5h6v23.3c0 2.1-.4 4-1.3 5.5a9.1 9.1 0 0 1-3.8 3.5c-1.6.8-3.5 1.3-5.7 1.3-2 0-3.7-.4-5.3-1s-2.8-1.8-3.7-3.2c-.9-1.3-1.4-3-1.4-5h6c.1.8.3 1.6.7 2.2s1 1.2 1.6 1.5c.7.4 1.5.5 2.4.5 1 0 1.8-.2 2.4-.6a4 4 0 0 0 1.6-1.8c.3-.8.5-1.8.5-3V45.5zm30.9 9.1a4.4 4.4 0 0 0-2-3.3 7.5 7.5 0 0 0-4.3-1.1c-1.3 0-2.4.2-3.3.5-.9.4-1.6 1-2 1.6a3.5 3.5 0 0 0-.3 4c.3.5.7.9 1.3 1.2l1.8 1 2 .5 3.2.8c1.3.3 2.5.7 3.7 1.2a13 13 0 0 1 3.2 1.8 8.1 8.1 0 0 1 3 6.5c0 2-.5 3.7-1.5 5.1a10 10 0 0 1-4.4 3.5c-1.8.8-4.1 1.2-6.8 1.2-2.6 0-4.9-.4-6.8-1.2-2-.8-3.4-2-4.5-3.5a10 10 0 0 1-1.7-5.6h6a5 5 0 0 0 3.5 4.6c1 .4 2.2.6 3.4.6 1.3 0 2.5-.2 3.5-.6 1-.4 1.8-1 2.4-1.7a4 4 0 0 0 .8-2.4c0-.9-.2-1.6-.7-2.2a11 11 0 0 0-2.1-1.4l-3.2-1-3.8-1c-2.8-.7-5-1.7-6.6-3.2a7.2 7.2 0 0 1-2.4-5.7 8 8 0 0 1 1.7-5 10 10 0 0 1 4.3-3.5c2-.8 4-1.2 6.4-1.2 2.3 0 4.4.4 6.2 1.2 1.8.8 3.2 2 4.3 3.4 1 1.4 1.5 3 1.5 5h-5.8z"/></svg>
|
After Width: | Height: | Size: 1.3 KiB |
38
registry-template/public/r/complex-component.json
Normal file
38
registry-template/public/r/complex-component.json
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
{
|
||||||
|
"$schema": "https://ui.shadcn.com/schema/registry-item.json",
|
||||||
|
"name": "complex-component",
|
||||||
|
"type": "registry:component",
|
||||||
|
"title": "Complex Component",
|
||||||
|
"description": "A complex component showing hooks, libs and components.",
|
||||||
|
"registryDependencies": [
|
||||||
|
"card"
|
||||||
|
],
|
||||||
|
"files": [
|
||||||
|
{
|
||||||
|
"path": "registry/new-york/complex-component/page.tsx",
|
||||||
|
"content": "import { cache } from \"react\"\nimport { PokemonCard } from \"@/registry/new-york/complex-component/components/pokemon-card\"\nimport { getPokemonList } from \"@/registry/new-york/complex-component/lib/pokemon\"\n\nconst getCachedPokemonList = cache(getPokemonList)\n\nexport default async function Page() {\n const pokemons = await getCachedPokemonList({ limit: 12 })\n\n if (!pokemons) {\n return null\n }\n\n return (\n <div className=\"mx-auto w-full max-w-2xl px-4\">\n <div className=\"grid grid-cols-2 gap-4 py-10 sm:grid-cols-3 md:grid-cols-4\">\n {pokemons.results.map((p) => (\n <PokemonCard key={p.name} name={p.name} />\n ))}\n </div>\n </div>\n )\n}\n",
|
||||||
|
"type": "registry:page",
|
||||||
|
"target": "app/pokemon/page.tsx"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "registry/new-york/complex-component/components/pokemon-card.tsx",
|
||||||
|
"content": "import { cache } from \"react\"\nimport { getPokemon } from \"@/registry/new-york/complex-component/lib/pokemon\"\nimport { Card, CardContent } from \"@/components/ui/card\"\nimport { PokemonImage } from \"@/registry/new-york/complex-component/components/pokemon-image\"\n\nconst cachedGetPokemon = cache(getPokemon)\n\nexport async function PokemonCard({ name }: { name: string }) {\n const pokemon = await cachedGetPokemon(name)\n\n if (!pokemon) {\n return null\n }\n\n return (\n <Card>\n <CardContent className=\"flex flex-col items-center p-2\">\n <div>\n <PokemonImage name={pokemon.name} number={pokemon.id} />\n </div>\n <div className=\"text-center font-medium\">{pokemon.name}</div>\n </CardContent>\n </Card>\n )\n}\n",
|
||||||
|
"type": "registry:component"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "registry/new-york/complex-component/components/pokemon-image.tsx",
|
||||||
|
"content": "\"use client\"\n\n/* eslint-disable @next/next/no-img-element */\nimport { usePokemonImage } from \"@/registry/new-york/complex-component/hooks/use-pokemon\"\n\nexport function PokemonImage({\n name,\n number,\n}: {\n name: string\n number: number\n}) {\n const imageUrl = usePokemonImage(number)\n\n if (!imageUrl) {\n return null\n }\n\n return <img src={imageUrl} alt={name} />\n}\n",
|
||||||
|
"type": "registry:component"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "registry/new-york/complex-component/lib/pokemon.ts",
|
||||||
|
"content": "import { z } from \"zod\"\n\nexport async function getPokemonList({ limit = 10 }: { limit?: number }) {\n try {\n const response = await fetch(\n `https://pokeapi.co/api/v2/pokemon?limit=${limit}`\n )\n return z\n .object({\n results: z.array(z.object({ name: z.string() })),\n })\n .parse(await response.json())\n } catch (error) {\n console.error(error)\n return null\n }\n}\n\nexport async function getPokemon(name: string) {\n try {\n const response = await fetch(`https://pokeapi.co/api/v2/pokemon/${name}`)\n\n if (!response.ok) {\n throw new Error(\"Failed to fetch pokemon\")\n }\n\n return z\n .object({\n name: z.string(),\n id: z.number(),\n sprites: z.object({\n front_default: z.string(),\n }),\n stats: z.array(\n z.object({\n base_stat: z.number(),\n stat: z.object({\n name: z.string(),\n }),\n })\n ),\n })\n .parse(await response.json())\n } catch (error) {\n console.error(error)\n return null\n }\n}\n",
|
||||||
|
"type": "registry:lib"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "registry/new-york/complex-component/hooks/use-pokemon.ts",
|
||||||
|
"content": "\"use client\"\n\n// Totally unnecessary hook, but it's a good example of how to use a hook in a custom registry.\n\nexport function usePokemonImage(number: number) {\n return `https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/${number}.png`\n}\n",
|
||||||
|
"type": "registry:hook"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
24
registry-template/public/r/example-form.json
Normal file
24
registry-template/public/r/example-form.json
Normal file
File diff suppressed because one or more lines are too long
17
registry-template/public/r/hello-world.json
Normal file
17
registry-template/public/r/hello-world.json
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
{
|
||||||
|
"$schema": "https://ui.shadcn.com/schema/registry-item.json",
|
||||||
|
"name": "hello-world",
|
||||||
|
"type": "registry:component",
|
||||||
|
"title": "Hello World",
|
||||||
|
"description": "A simple hello world component",
|
||||||
|
"registryDependencies": [
|
||||||
|
"button"
|
||||||
|
],
|
||||||
|
"files": [
|
||||||
|
{
|
||||||
|
"path": "registry/new-york/hello-world/hello-world.tsx",
|
||||||
|
"content": "export function HelloWorld() {\n return <h1 className=\"text-2xl font-bold\">Hello World</h1>\n}\n",
|
||||||
|
"type": "registry:component"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
1
registry-template/public/vercel.svg
Normal file
1
registry-template/public/vercel.svg
Normal file
@ -0,0 +1 @@
|
|||||||
|
<svg fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1155 1000"><path d="m577.3 0 577.4 1000H0z" fill="#fff"/></svg>
|
After Width: | Height: | Size: 128 B |
1
registry-template/public/window.svg
Normal file
1
registry-template/public/window.svg
Normal file
@ -0,0 +1 @@
|
|||||||
|
<svg fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><path fill-rule="evenodd" clip-rule="evenodd" d="M1.5 2.5h13v10a1 1 0 0 1-1 1h-11a1 1 0 0 1-1-1zM0 1h16v11.5a2.5 2.5 0 0 1-2.5 2.5h-11A2.5 2.5 0 0 1 0 12.5zm3.75 4.5a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5M7 4.75a.75.75 0 1 1-1.5 0 .75.75 0 0 1 1.5 0m1.75.75a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5" fill="#666"/></svg>
|
After Width: | Height: | Size: 385 B |
64
registry-template/registry.json
Normal file
64
registry-template/registry.json
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
{
|
||||||
|
"$schema": "https://ui.shadcn.com/schema/registry.json",
|
||||||
|
"name": "acme",
|
||||||
|
"homepage": "https://acme.com",
|
||||||
|
"items": [
|
||||||
|
{
|
||||||
|
"name": "hello-world",
|
||||||
|
"type": "registry:component",
|
||||||
|
"title": "Hello World",
|
||||||
|
"description": "A simple hello world component",
|
||||||
|
"registryDependencies": ["button"],
|
||||||
|
"files": [
|
||||||
|
{
|
||||||
|
"path": "registry/new-york/hello-world/hello-world.tsx",
|
||||||
|
"type": "registry:component"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "example-form",
|
||||||
|
"type": "registry:component",
|
||||||
|
"title": "Example Form",
|
||||||
|
"description": "A contact form with Zod validation.",
|
||||||
|
"dependencies": ["zod"],
|
||||||
|
"registryDependencies": ["button", "input", "label", "textarea", "card"],
|
||||||
|
"files": [
|
||||||
|
{
|
||||||
|
"path": "registry/new-york/example-form/example-form.tsx",
|
||||||
|
"type": "registry:component"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "complex-component",
|
||||||
|
"type": "registry:component",
|
||||||
|
"title": "Complex Component",
|
||||||
|
"description": "A complex component showing hooks, libs and components.",
|
||||||
|
"registryDependencies": ["card"],
|
||||||
|
"files": [
|
||||||
|
{
|
||||||
|
"path": "registry/new-york/complex-component/page.tsx",
|
||||||
|
"type": "registry:page",
|
||||||
|
"target": "app/pokemon/page.tsx"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "registry/new-york/complex-component/components/pokemon-card.tsx",
|
||||||
|
"type": "registry:component"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "registry/new-york/complex-component/components/pokemon-image.tsx",
|
||||||
|
"type": "registry:component"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "registry/new-york/complex-component/lib/pokemon.ts",
|
||||||
|
"type": "registry:lib"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "registry/new-york/complex-component/hooks/use-pokemon.ts",
|
||||||
|
"type": "registry:hook"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
5
registry-template/registry/layout.tsx
Normal file
5
registry-template/registry/layout.tsx
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
import "@/app/globals.css"
|
||||||
|
|
||||||
|
export default function Layout({ children }: { children: React.ReactNode }) {
|
||||||
|
return <>{children}</>
|
||||||
|
}
|
@ -0,0 +1,25 @@
|
|||||||
|
import { cache } from "react"
|
||||||
|
import { getPokemon } from "@/registry/new-york/complex-component/lib/pokemon"
|
||||||
|
import { Card, CardContent } from "@/components/ui/card"
|
||||||
|
import { PokemonImage } from "@/registry/new-york/complex-component/components/pokemon-image"
|
||||||
|
|
||||||
|
const cachedGetPokemon = cache(getPokemon)
|
||||||
|
|
||||||
|
export async function PokemonCard({ name }: { name: string }) {
|
||||||
|
const pokemon = await cachedGetPokemon(name)
|
||||||
|
|
||||||
|
if (!pokemon) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Card>
|
||||||
|
<CardContent className="flex flex-col items-center p-2">
|
||||||
|
<div>
|
||||||
|
<PokemonImage name={pokemon.name} number={pokemon.id} />
|
||||||
|
</div>
|
||||||
|
<div className="text-center font-medium">{pokemon.name}</div>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
)
|
||||||
|
}
|
@ -0,0 +1,20 @@
|
|||||||
|
"use client"
|
||||||
|
|
||||||
|
/* eslint-disable @next/next/no-img-element */
|
||||||
|
import { usePokemonImage } from "@/registry/new-york/complex-component/hooks/use-pokemon"
|
||||||
|
|
||||||
|
export function PokemonImage({
|
||||||
|
name,
|
||||||
|
number,
|
||||||
|
}: {
|
||||||
|
name: string
|
||||||
|
number: number
|
||||||
|
}) {
|
||||||
|
const imageUrl = usePokemonImage(number)
|
||||||
|
|
||||||
|
if (!imageUrl) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
return <img src={imageUrl} alt={name} />
|
||||||
|
}
|
@ -0,0 +1,7 @@
|
|||||||
|
"use client"
|
||||||
|
|
||||||
|
// Totally unnecessary hook, but it's a good example of how to use a hook in a custom registry.
|
||||||
|
|
||||||
|
export function usePokemonImage(number: number) {
|
||||||
|
return `https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/${number}.png`
|
||||||
|
}
|
@ -0,0 +1,48 @@
|
|||||||
|
import { z } from "zod"
|
||||||
|
|
||||||
|
export async function getPokemonList({ limit = 10 }: { limit?: number }) {
|
||||||
|
try {
|
||||||
|
const response = await fetch(
|
||||||
|
`https://pokeapi.co/api/v2/pokemon?limit=${limit}`
|
||||||
|
)
|
||||||
|
return z
|
||||||
|
.object({
|
||||||
|
results: z.array(z.object({ name: z.string() })),
|
||||||
|
})
|
||||||
|
.parse(await response.json())
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error)
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getPokemon(name: string) {
|
||||||
|
try {
|
||||||
|
const response = await fetch(`https://pokeapi.co/api/v2/pokemon/${name}`)
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error("Failed to fetch pokemon")
|
||||||
|
}
|
||||||
|
|
||||||
|
return z
|
||||||
|
.object({
|
||||||
|
name: z.string(),
|
||||||
|
id: z.number(),
|
||||||
|
sprites: z.object({
|
||||||
|
front_default: z.string(),
|
||||||
|
}),
|
||||||
|
stats: z.array(
|
||||||
|
z.object({
|
||||||
|
base_stat: z.number(),
|
||||||
|
stat: z.object({
|
||||||
|
name: z.string(),
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
),
|
||||||
|
})
|
||||||
|
.parse(await response.json())
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error)
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,23 @@
|
|||||||
|
import { cache } from "react"
|
||||||
|
import { PokemonCard } from "@/registry/new-york/complex-component/components/pokemon-card"
|
||||||
|
import { getPokemonList } from "@/registry/new-york/complex-component/lib/pokemon"
|
||||||
|
|
||||||
|
const getCachedPokemonList = cache(getPokemonList)
|
||||||
|
|
||||||
|
export default async function Page() {
|
||||||
|
const pokemons = await getCachedPokemonList({ limit: 12 })
|
||||||
|
|
||||||
|
if (!pokemons) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="mx-auto w-full max-w-2xl px-4">
|
||||||
|
<div className="grid grid-cols-2 gap-4 py-10 sm:grid-cols-3 md:grid-cols-4">
|
||||||
|
{pokemons.results.map((p) => (
|
||||||
|
<PokemonCard key={p.name} name={p.name} />
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
@ -0,0 +1,164 @@
|
|||||||
|
"use client"
|
||||||
|
|
||||||
|
import * as React from "react"
|
||||||
|
import {
|
||||||
|
Card,
|
||||||
|
CardTitle,
|
||||||
|
CardHeader,
|
||||||
|
CardDescription,
|
||||||
|
CardContent,
|
||||||
|
CardFooter,
|
||||||
|
} from "@/components/ui/card"
|
||||||
|
import { Input } from "@/components/ui/input"
|
||||||
|
import { Label } from "@/components/ui/label"
|
||||||
|
import { Button } from "@/components/ui/button"
|
||||||
|
import { Textarea } from "@/components/ui/textarea"
|
||||||
|
import { z } from "zod"
|
||||||
|
|
||||||
|
const exampleFormSchema = z.object({
|
||||||
|
name: z.string().min(1),
|
||||||
|
email: z.string().email(),
|
||||||
|
message: z.string().min(1),
|
||||||
|
})
|
||||||
|
|
||||||
|
export function ExampleForm() {
|
||||||
|
const [pending, setPending] = React.useState(false)
|
||||||
|
const [state, setState] = React.useState({
|
||||||
|
defaultValues: {
|
||||||
|
name: "",
|
||||||
|
email: "",
|
||||||
|
message: "",
|
||||||
|
},
|
||||||
|
success: false,
|
||||||
|
errors: {
|
||||||
|
name: "",
|
||||||
|
email: "",
|
||||||
|
message: "",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
const handleSubmit = React.useCallback(
|
||||||
|
(e: React.FormEvent<HTMLFormElement>) => {
|
||||||
|
e.preventDefault()
|
||||||
|
setPending(true)
|
||||||
|
|
||||||
|
const formData = new FormData(e.target as HTMLFormElement)
|
||||||
|
const data = Object.fromEntries(formData.entries())
|
||||||
|
const result = exampleFormSchema.safeParse(data)
|
||||||
|
|
||||||
|
if (!result.success) {
|
||||||
|
setState({
|
||||||
|
...state,
|
||||||
|
errors: Object.fromEntries(
|
||||||
|
Object.entries(result.error.flatten().fieldErrors).map(
|
||||||
|
([key, value]) => [key, value?.[0] ?? ""]
|
||||||
|
)
|
||||||
|
) as Record<keyof typeof state.errors, string>,
|
||||||
|
})
|
||||||
|
setPending(false)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
setPending(false)
|
||||||
|
},
|
||||||
|
[state]
|
||||||
|
)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Card className="w-full max-w-sm">
|
||||||
|
<CardHeader>
|
||||||
|
<CardTitle>How can we help?</CardTitle>
|
||||||
|
<CardDescription>
|
||||||
|
Need help with your project? We're here to assist you.
|
||||||
|
</CardDescription>
|
||||||
|
</CardHeader>
|
||||||
|
<form onSubmit={handleSubmit}>
|
||||||
|
<CardContent className="flex flex-col gap-6">
|
||||||
|
<div
|
||||||
|
className="group/field grid gap-2"
|
||||||
|
data-invalid={!!state.errors?.name}
|
||||||
|
>
|
||||||
|
<Label
|
||||||
|
htmlFor="name"
|
||||||
|
className="group-data-[invalid=true]/field:text-destructive"
|
||||||
|
>
|
||||||
|
Name <span aria-hidden="true">*</span>
|
||||||
|
</Label>
|
||||||
|
<Input
|
||||||
|
id="name"
|
||||||
|
name="name"
|
||||||
|
placeholder="Lee Robinson"
|
||||||
|
className="group-data-[invalid=true]/field:border-destructive focus-visible:group-data-[invalid=true]/field:ring-destructive"
|
||||||
|
disabled={pending}
|
||||||
|
aria-invalid={!!state.errors?.name}
|
||||||
|
aria-errormessage="error-name"
|
||||||
|
defaultValue={state.defaultValues.name}
|
||||||
|
/>
|
||||||
|
{state.errors?.name && (
|
||||||
|
<p id="error-name" className="text-destructive text-sm">
|
||||||
|
{state.errors.name}
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
className="group/field grid gap-2"
|
||||||
|
data-invalid={!!state.errors?.email}
|
||||||
|
>
|
||||||
|
<Label
|
||||||
|
htmlFor="email"
|
||||||
|
className="group-data-[invalid=true]/field:text-destructive"
|
||||||
|
>
|
||||||
|
Email <span aria-hidden="true">*</span>
|
||||||
|
</Label>
|
||||||
|
<Input
|
||||||
|
id="email"
|
||||||
|
name="email"
|
||||||
|
placeholder="leerob@acme.com"
|
||||||
|
className="group-data-[invalid=true]/field:border-destructive focus-visible:group-data-[invalid=true]/field:ring-destructive"
|
||||||
|
disabled={pending}
|
||||||
|
aria-invalid={!!state.errors?.email}
|
||||||
|
aria-errormessage="error-email"
|
||||||
|
defaultValue={state.defaultValues.email}
|
||||||
|
/>
|
||||||
|
{state.errors?.email && (
|
||||||
|
<p id="error-email" className="text-destructive text-sm">
|
||||||
|
{state.errors.email}
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
className="group/field grid gap-2"
|
||||||
|
data-invalid={!!state.errors?.message}
|
||||||
|
>
|
||||||
|
<Label
|
||||||
|
htmlFor="message"
|
||||||
|
className="group-data-[invalid=true]/field:text-destructive"
|
||||||
|
>
|
||||||
|
Message <span aria-hidden="true">*</span>
|
||||||
|
</Label>
|
||||||
|
<Textarea
|
||||||
|
id="message"
|
||||||
|
name="message"
|
||||||
|
placeholder="Type your message here..."
|
||||||
|
className="group-data-[invalid=true]/field:border-destructive focus-visible:group-data-[invalid=true]/field:ring-destructive"
|
||||||
|
disabled={pending}
|
||||||
|
aria-invalid={!!state.errors?.message}
|
||||||
|
aria-errormessage="error-message"
|
||||||
|
defaultValue={state.defaultValues.message}
|
||||||
|
/>
|
||||||
|
{state.errors?.message && (
|
||||||
|
<p id="error-message" className="text-destructive text-sm">
|
||||||
|
{state.errors.message}
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</CardContent>
|
||||||
|
<CardFooter>
|
||||||
|
<Button type="submit" size="sm" disabled={pending}>
|
||||||
|
{pending ? "Sending..." : "Send Message"}
|
||||||
|
</Button>
|
||||||
|
</CardFooter>
|
||||||
|
</form>
|
||||||
|
</Card>
|
||||||
|
)
|
||||||
|
}
|
@ -0,0 +1,3 @@
|
|||||||
|
export function HelloWorld() {
|
||||||
|
return <h1 className="text-2xl font-bold">Hello World</h1>
|
||||||
|
}
|
64
registry-template/tailwind.config.ts
Normal file
64
registry-template/tailwind.config.ts
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
import type { Config } from "tailwindcss"
|
||||||
|
import tailwindAnimate from "tailwindcss-animate"
|
||||||
|
|
||||||
|
export default {
|
||||||
|
darkMode: ["class"],
|
||||||
|
content: [
|
||||||
|
"./pages/**/*.{js,ts,jsx,tsx,mdx}",
|
||||||
|
"./components/**/*.{js,ts,jsx,tsx,mdx}",
|
||||||
|
"./app/**/*.{js,ts,jsx,tsx,mdx}",
|
||||||
|
"./registry/**/*.{js,ts,jsx,tsx,mdx}",
|
||||||
|
],
|
||||||
|
theme: {
|
||||||
|
extend: {
|
||||||
|
colors: {
|
||||||
|
background: "hsl(var(--background))",
|
||||||
|
foreground: "hsl(var(--foreground))",
|
||||||
|
card: {
|
||||||
|
DEFAULT: "hsl(var(--card))",
|
||||||
|
foreground: "hsl(var(--card-foreground))",
|
||||||
|
},
|
||||||
|
popover: {
|
||||||
|
DEFAULT: "hsl(var(--popover))",
|
||||||
|
foreground: "hsl(var(--popover-foreground))",
|
||||||
|
},
|
||||||
|
primary: {
|
||||||
|
DEFAULT: "hsl(var(--primary))",
|
||||||
|
foreground: "hsl(var(--primary-foreground))",
|
||||||
|
},
|
||||||
|
secondary: {
|
||||||
|
DEFAULT: "hsl(var(--secondary))",
|
||||||
|
foreground: "hsl(var(--secondary-foreground))",
|
||||||
|
},
|
||||||
|
muted: {
|
||||||
|
DEFAULT: "hsl(var(--muted))",
|
||||||
|
foreground: "hsl(var(--muted-foreground))",
|
||||||
|
},
|
||||||
|
accent: {
|
||||||
|
DEFAULT: "hsl(var(--accent))",
|
||||||
|
foreground: "hsl(var(--accent-foreground))",
|
||||||
|
},
|
||||||
|
destructive: {
|
||||||
|
DEFAULT: "hsl(var(--destructive))",
|
||||||
|
foreground: "hsl(var(--destructive-foreground))",
|
||||||
|
},
|
||||||
|
border: "hsl(var(--border))",
|
||||||
|
input: "hsl(var(--input))",
|
||||||
|
ring: "hsl(var(--ring))",
|
||||||
|
chart: {
|
||||||
|
"1": "hsl(var(--chart-1))",
|
||||||
|
"2": "hsl(var(--chart-2))",
|
||||||
|
"3": "hsl(var(--chart-3))",
|
||||||
|
"4": "hsl(var(--chart-4))",
|
||||||
|
"5": "hsl(var(--chart-5))",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
borderRadius: {
|
||||||
|
lg: "var(--radius)",
|
||||||
|
md: "calc(var(--radius) - 2px)",
|
||||||
|
sm: "calc(var(--radius) - 4px)",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
plugins: [tailwindAnimate],
|
||||||
|
} satisfies Config
|
27
registry-template/tsconfig.json
Normal file
27
registry-template/tsconfig.json
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "ES2017",
|
||||||
|
"lib": ["dom", "dom.iterable", "esnext"],
|
||||||
|
"allowJs": true,
|
||||||
|
"skipLibCheck": true,
|
||||||
|
"strict": true,
|
||||||
|
"noEmit": true,
|
||||||
|
"esModuleInterop": true,
|
||||||
|
"module": "esnext",
|
||||||
|
"moduleResolution": "bundler",
|
||||||
|
"resolveJsonModule": true,
|
||||||
|
"isolatedModules": true,
|
||||||
|
"jsx": "preserve",
|
||||||
|
"incremental": true,
|
||||||
|
"plugins": [
|
||||||
|
{
|
||||||
|
"name": "next"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"paths": {
|
||||||
|
"@/*": ["./*"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
|
||||||
|
"exclude": ["node_modules"]
|
||||||
|
}
|
0
registry-test/components.json
Normal file
0
registry-test/components.json
Normal file
20
registry-test/package.json
Normal file
20
registry-test/package.json
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
"name": "registry",
|
||||||
|
"version": "0.0.1",
|
||||||
|
"description": "",
|
||||||
|
"main": "index.js",
|
||||||
|
"scripts": {
|
||||||
|
"test": "echo \"Error: no test specified\" && exit 1",
|
||||||
|
|
||||||
|
"init": "shadcn init",
|
||||||
|
"registry:build": "shadcn registry:build"
|
||||||
|
},
|
||||||
|
"keywords": [],
|
||||||
|
"author": "abearxiong <xiongxiao@xiongxiao.me> (https://www.xiongxiao.me)",
|
||||||
|
"license": "MIT",
|
||||||
|
"packageManager": "pnpm@10.6.2",
|
||||||
|
"type": "module",
|
||||||
|
"dependencies": {
|
||||||
|
"shadcn": "2.6.0-canary.2"
|
||||||
|
}
|
||||||
|
}
|
2335
registry-test/pnpm-lock.yaml
generated
Normal file
2335
registry-test/pnpm-lock.yaml
generated
Normal file
File diff suppressed because it is too large
Load Diff
19
registry-test/registry.json
Normal file
19
registry-test/registry.json
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
{
|
||||||
|
"$schema": "https://ui.shadcn.com/schema/registry.json",
|
||||||
|
"name": "xiongxiao",
|
||||||
|
"homepage": "https://xiongxiao.me",
|
||||||
|
"items": [
|
||||||
|
{
|
||||||
|
"name": "hello-world",
|
||||||
|
"type": "registry:block",
|
||||||
|
"title": "Hello World",
|
||||||
|
"description": "A simple hello world component.",
|
||||||
|
"files": [
|
||||||
|
{
|
||||||
|
"path": "registry/test/hello-world/hello-world.tsx",
|
||||||
|
"type": "registry:component"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
5
registry-test/registry/test/hello-wrold/hello-world.tsx
Normal file
5
registry-test/registry/test/hello-wrold/hello-world.tsx
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
import { Button } from "@/components/ui/button"
|
||||||
|
|
||||||
|
export function HelloWorld() {
|
||||||
|
return <Button>Hello World</Button>
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user