From f5fa2e739fcb68766c46ca8b9a56c948b135beaa Mon Sep 17 00:00:00 2001 From: phev8 Date: Tue, 24 Feb 2026 09:55:01 +0100 Subject: [PATCH 1/3] chore: remove generated component registry files for Alert, Button, Confirm Dialog, Loading Button, and main registry --- public/r/alert.json | 18 -------- public/r/button.json | 18 -------- public/r/confirm.json | 26 ------------ public/r/loading-button.json | 22 ---------- public/r/registry.json | 82 ------------------------------------ 5 files changed, 166 deletions(-) delete mode 100644 public/r/alert.json delete mode 100644 public/r/button.json delete mode 100644 public/r/confirm.json delete mode 100644 public/r/loading-button.json delete mode 100644 public/r/registry.json diff --git a/public/r/alert.json b/public/r/alert.json deleted file mode 100644 index de3ab7c..0000000 --- a/public/r/alert.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "$schema": "https://ui.shadcn.com/schema/registry-item.json", - "name": "alert", - "title": "Alert", - "description": "An alert component built on top of AlertDialog.", - "registryDependencies": [ - "alert-dialog" - ], - "files": [ - { - "path": "registry/new-york/alert.tsx", - "content": "\"use client\";\n\nimport { createContext, useContext, useState, useCallback, useMemo, type ReactNode } from \"react\";\nimport { AlertDialog, AlertDialogAction, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle } from \"@/components/ui/alert-dialog\";\n\nexport interface AlertOptions {\n\ttitle?: string;\n\tdescription?: string;\n\tbuttonLabel?: string;\n\tchildren?: ReactNode;\n}\n\nexport interface AlertApi {\n\t(options: AlertOptions): Promise;\n\tdismiss: () => void;\n}\n\ninterface AlertContextType {\n\talert: AlertApi;\n}\n\nconst AlertContext = createContext(undefined);\n\nfunction AlertDialogContent_({\n\tisOpen,\n\ttitle,\n\tdescription,\n\tbuttonLabel,\n\tchildren,\n\tonDismiss,\n}: {\n\tisOpen: boolean;\n\ttitle: string;\n\tdescription: string;\n\tbuttonLabel: string;\n\tchildren?: ReactNode;\n\tonDismiss: () => void;\n}) {\n\treturn (\n\t\t {\n\t\t\t\tif (!open) onDismiss();\n\t\t\t}}\n\t\t>\n\t\t\t\n\t\t\t\t\n\t\t\t\t\t{title}\n\t\t\t\t\t{description ? {description} : null}\n\t\t\t\t\n\t\t\t\t{children != null ?
{children}
: null}\n\t\t\t\t\n\t\t\t\t\t{buttonLabel}\n\t\t\t\t\n\t\t\t
\n\t\t\n\t);\n}\n\nexport const AlertDialogProvider = ({ children }: { children: ReactNode }) => {\n\tconst [options, setOptions] = useState({});\n\tconst [isOpen, setIsOpen] = useState(false);\n\tconst [, setResolver] = useState<(() => void) | null>(null);\n\n\tconst handleDismiss = useCallback(() => {\n\t\tsetResolver((prev) => {\n\t\t\tif (prev) prev();\n\t\t\treturn null;\n\t\t});\n\t\tsetIsOpen(false);\n\t\tsetOptions({});\n\t}, []);\n\n\tconst alertFn = useCallback((alertOptions: AlertOptions) => {\n\t\tsetResolver((prev) => {\n\t\t\tif (prev) prev();\n\t\t\treturn null;\n\t\t});\n\t\tsetOptions(alertOptions);\n\t\tsetIsOpen(true);\n\t\treturn new Promise((resolve) => {\n\t\t\tsetResolver(() => resolve);\n\t\t});\n\t}, []);\n\n\tconst alert = useMemo(\n\t\t() => Object.assign(alertFn, { dismiss: handleDismiss }),\n\t\t[alertFn, handleDismiss]\n\t);\n\n\treturn (\n\t\t\n\t\t\t{children}\n\t\t\t\n\t\t\t\t{options.children}\n\t\t\t\n\t\t\n\t);\n};\n\nexport const useAlert = () => {\n\tconst context = useContext(AlertContext);\n\tif (!context) {\n\t\tthrow new Error(\"useAlert must be used within an AlertDialogProvider\");\n\t}\n\treturn context.alert;\n};\n", - "type": "registry:component", - "target": "components/c-ui/alert.tsx" - } - ], - "type": "registry:component" -} \ No newline at end of file diff --git a/public/r/button.json b/public/r/button.json deleted file mode 100644 index 9216eba..0000000 --- a/public/r/button.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "$schema": "https://ui.shadcn.com/schema/registry-item.json", - "name": "button", - "title": "Button", - "description": "A base button with a subtle press animation.", - "registryDependencies": [ - "@shadcn/button" - ], - "files": [ - { - "path": "registry/new-york/button.tsx", - "content": "import * as React from \"react\"\nimport { Button, buttonVariants } from \"@/components/ui/button\"\nimport { cn } from \"@/lib/utils\"\n\ntype CuiButtonProps = React.ComponentProps\n\nconst CuiButton = React.forwardRef(\n ({ className, ...props }, ref) => {\n return (\n \n )\n }\n)\n\nCuiButton.displayName = \"Button\"\n\nexport { CuiButton as Button, buttonVariants }\n", - "type": "registry:component", - "target": "components/c-ui/button.tsx" - } - ], - "type": "registry:component" -} \ No newline at end of file diff --git a/public/r/confirm.json b/public/r/confirm.json deleted file mode 100644 index 4cf9db2..0000000 --- a/public/r/confirm.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "$schema": "https://ui.shadcn.com/schema/registry-item.json", - "name": "confirm", - "title": "Confirm Dialog", - "description": "A confirmation dialog service with provider and hook.", - "registryDependencies": [ - "alert-dialog", - "input", - "label" - ], - "files": [ - { - "path": "registry/new-york/confirm-provider.tsx", - "content": "\"use client\";\n\nimport { createContext, useContext, useState, useCallback, type ReactNode } from \"react\";\nimport ConfirmDialog, { type RequireConfirmationInput } from \"./confirm-dialog\";\n\nexport type { RequireConfirmationInput };\n\ninterface ConfirmOptions {\n\ttitle?: string;\n\tdescription?: string;\n\tconfirmButtonText?: string;\n\tcancelButtonText?: string;\n\tvariant?: \"default\" | \"destructive\";\n\trequireConfirmationInput?: RequireConfirmationInput;\n}\n\ninterface ConfirmContextType {\n\tconfirm: (options: ConfirmOptions) => Promise;\n}\n\nconst ConfirmContext = createContext(undefined);\n\nexport const ConfirmDialogProvider = ({ children }: { children: ReactNode }) => {\n\tconst [options, setOptions] = useState({});\n\tconst [isOpen, setIsOpen] = useState(false);\n\tconst [resolver, setResolver] = useState<((value: boolean) => void) | null>(null);\n\n\tconst confirm = useCallback((confirmOptions: ConfirmOptions) => {\n\t\tsetResolver((prev: ((value: boolean) => void) | null) => {\n\t\t\tif (prev) {\n\t\t\t\tprev(false);\n\t\t\t}\n\t\t\treturn null;\n\t\t});\n\n\t\tsetOptions(confirmOptions);\n\t\tsetIsOpen(true);\n\t\treturn new Promise((resolve) => {\n\t\t\tsetResolver(() => resolve);\n\t\t});\n\t}, []);\n\n\tconst handleConfirm = () => {\n\t\tif (resolver) {\n\t\t\tresolver(true);\n\t\t}\n\t\tsetIsOpen(false);\n\t\tsetResolver(null);\n\t};\n\n\tconst handleCancel = () => {\n\t\tif (resolver) {\n\t\t\tresolver(false);\n\t\t}\n\t\tsetIsOpen(false);\n\t\tsetResolver(null);\n\t};\n\n\treturn (\n\t\t\n\t\t\t{children}\n\t\t\t\n\t\t\n\t);\n};\n\nexport const useConfirm = () => {\n\tconst context = useContext(ConfirmContext);\n\tif (!context) {\n\t\tthrow new Error(\"useConfirm must be used within a ConfirmDialogProvider\");\n\t}\n\treturn context.confirm;\n};\n", - "type": "registry:component", - "target": "components/c-ui/confirm-provider.tsx" - }, - { - "path": "registry/new-york/confirm-dialog.tsx", - "content": "\"use client\";\n\nimport { useState } from \"react\";\nimport { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle } from \"@/components/ui/alert-dialog\"\nimport { Input } from \"@/components/ui/input\";\nimport { Label } from \"@/components/ui/label\";\n\nexport interface RequireConfirmationInput {\n\tconfirmTerm: string;\n\t/** Custom label with {{confirmTerm}} placeholder for the required term (bold). Default: \"Type {{confirmTerm}} to confirm\" */\n\tlabel?: string;\n\thint?: string;\n}\n\ninterface ConfirmDialogProps {\n\ttitle: string;\n\tdescription: string;\n\tconfirmButtonText: string;\n\tcancelButtonText: string;\n\tonConfirm: () => void;\n\tonCancel: () => void;\n\tvariant: \"default\" | \"destructive\";\n\tisOpen: boolean;\n\trequireConfirmationInput?: RequireConfirmationInput;\n}\n\nconst ConfirmDialog = (props: ConfirmDialogProps) => {\n\tconst [typedValue, setTypedValue] = useState(\"\");\n\n\tconst requireTyped = props.requireConfirmationInput;\n\tconst requiredTerm = requireTyped?.confirmTerm ?? \"\";\n\tconst canConfirm = !requireTyped || typedValue === requiredTerm;\n\n\tconst labelMessage = requireTyped?.label ?? \"Type {{confirmTerm}} to confirm\";\n\tconst labelParts = labelMessage.split(\"{{confirmTerm}}\");\n\n\treturn (\n\t\t {\n\t\t\t\tif (!open) {\n\t\t\t\t\tsetTypedValue(\"\");\n\t\t\t\t\tprops.onCancel();\n\t\t\t\t}\n\t\t\t}}\n\t\t>\n\t\t\t\n\t\t\t\t\n\t\t\t\t\t{props.title}\n\t\t\t\t\t{props.description}\n\t\t\t\t\n\t\t\t\t{requireTyped && (\n\t\t\t\t\t
\n\t\t\t\t\t\t\n\t\t\t\t\t\t setTypedValue(e.target.value)}\n\t\t\t\t\t\t\tonKeyDown={(e) => {\n\t\t\t\t\t\t\t\tif (e.key === \"Enter\" && canConfirm) {\n\t\t\t\t\t\t\t\t\te.preventDefault();\n\t\t\t\t\t\t\t\t\tsetTypedValue(\"\");\n\t\t\t\t\t\t\t\t\tprops.onConfirm();\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\tplaceholder={requiredTerm}\n\t\t\t\t\t\t\tautoComplete=\"off\"\n\t\t\t\t\t\t/>\n\t\t\t\t\t\t{requireTyped.hint && (\n\t\t\t\t\t\t\t

{requireTyped.hint}

\n\t\t\t\t\t\t)}\n\t\t\t\t\t
\n\t\t\t\t)}\n\t\t\t\t\n\t\t\t\t\t {\n\t\t\t\t\t\t\te.preventDefault();\n\t\t\t\t\t\t\tsetTypedValue(\"\");\n\t\t\t\t\t\t\tprops.onCancel();\n\t\t\t\t\t\t}}\n\t\t\t\t\t>\n\t\t\t\t\t\t{props.cancelButtonText}\n\t\t\t\t\t\n\t\t\t\t\t {\n\t\t\t\t\t\t\te.preventDefault();\n\t\t\t\t\t\t\tsetTypedValue(\"\");\n\t\t\t\t\t\t\tprops.onConfirm();\n\t\t\t\t\t\t}}\n\t\t\t\t\t>\n\t\t\t\t\t\t{props.confirmButtonText}\n\t\t\t\t\t\n\t\t\t\t\n\t\t\t
\n\t\t\n\t);\n};\n\nexport default ConfirmDialog;\n", - "type": "registry:component", - "target": "components/c-ui/confirm-dialog.tsx" - } - ], - "type": "registry:component" -} \ No newline at end of file diff --git a/public/r/loading-button.json b/public/r/loading-button.json deleted file mode 100644 index a2de4db..0000000 --- a/public/r/loading-button.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "$schema": "https://ui.shadcn.com/schema/registry-item.json", - "name": "loading-button", - "title": "Loading Button", - "description": "A button with a loading state.", - "dependencies": [ - "lucide-react", - "class-variance-authority" - ], - "registryDependencies": [ - "@c-ui/button" - ], - "files": [ - { - "path": "registry/new-york/loading-button.tsx", - "content": "import * as React from \"react\"\nimport { Loader2Icon } from \"lucide-react\"\nimport { Button, buttonVariants } from \"./button\"\n\nimport type { VariantProps } from \"class-variance-authority\"\n\ntype LoadingButtonProps = React.ComponentProps<\"button\"> &\n\tVariantProps & {\n\t\tasChild?: boolean\n\t\tisLoading?: boolean\n\t}\n\nconst LoadingButton = React.forwardRef(\n\t({ isLoading, disabled, children, ...props }, ref) => {\n\t\treturn (\n\t\t\t\n\t\t\t\t{isLoading && (\n\t\t\t\t\t\n\t\t\t\t)}\n\t\t\t\t{children}\n\t\t\t\n\t\t)\n\t}\n)\n\nLoadingButton.displayName = \"LoadingButton\"\n\nexport { LoadingButton }\n", - "type": "registry:component", - "target": "components/c-ui/loading-button.tsx" - } - ], - "type": "registry:component" -} \ No newline at end of file diff --git a/public/r/registry.json b/public/r/registry.json deleted file mode 100644 index d2f5948..0000000 --- a/public/r/registry.json +++ /dev/null @@ -1,82 +0,0 @@ -{ - "$schema": "https://ui.shadcn.com/schema/registry.json", - "name": "c-ui", - "homepage": "https://coneno.github.io/c-ui", - "items": [ - { - "name": "alert", - "type": "registry:component", - "title": "Alert", - "description": "An alert component built on top of AlertDialog.", - "registryDependencies": [ - "alert-dialog" - ], - "files": [ - { - "path": "registry/new-york/alert.tsx", - "type": "registry:component", - "target": "components/c-ui/alert.tsx" - } - ] - }, - { - "name": "confirm", - "type": "registry:component", - "title": "Confirm Dialog", - "description": "A confirmation dialog service with provider and hook.", - "registryDependencies": [ - "alert-dialog", - "input", - "label" - ], - "files": [ - { - "path": "registry/new-york/confirm-provider.tsx", - "type": "registry:component", - "target": "components/c-ui/confirm-provider.tsx" - }, - { - "path": "registry/new-york/confirm-dialog.tsx", - "type": "registry:component", - "target": "components/c-ui/confirm-dialog.tsx" - } - ] - }, - { - "name": "button", - "type": "registry:component", - "title": "Button", - "description": "A base button with a subtle press animation.", - "registryDependencies": [ - "@shadcn/button" - ], - "files": [ - { - "path": "registry/new-york/button.tsx", - "type": "registry:component", - "target": "components/c-ui/button.tsx" - } - ] - }, - { - "name": "loading-button", - "type": "registry:component", - "title": "Loading Button", - "description": "A button with a loading state.", - "registryDependencies": [ - "@c-ui/button" - ], - "dependencies": [ - "lucide-react", - "class-variance-authority" - ], - "files": [ - { - "path": "registry/new-york/loading-button.tsx", - "type": "registry:component", - "target": "components/c-ui/loading-button.tsx" - } - ] - } - ] -} From 220851e38558d6bf4ccf71f6ad760a8e584d5ff9 Mon Sep 17 00:00:00 2001 From: phev8 Date: Tue, 24 Feb 2026 11:30:12 +0100 Subject: [PATCH 2/3] config update --- .gitignore | 3 +++ eslint.config.mjs | 33 +++++++++++++++++---------------- tsconfig.json | 6 +++++- 3 files changed, 25 insertions(+), 17 deletions(-) diff --git a/.gitignore b/.gitignore index 5ef6a52..7f4fd4f 100644 --- a/.gitignore +++ b/.gitignore @@ -39,3 +39,6 @@ yarn-error.log* # typescript *.tsbuildinfo next-env.d.ts + +# registry +public/r/ diff --git a/eslint.config.mjs b/eslint.config.mjs index 0fe137f..74f101b 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -1,17 +1,18 @@ -import js from "@eslint/js"; -import tseslint from "typescript-eslint"; -import react from "eslint-plugin-react"; +import { defineConfig, globalIgnores } from "eslint/config"; +import nextVitals from "eslint-config-next/core-web-vitals"; +import nextTs from "eslint-config-next/typescript"; -export default tseslint.config( - js.configs.recommended, - ...tseslint.configs.recommended, - { - files: ["**/*.{ts,tsx}"], - plugins: { - react, - }, - rules: { - "react/react-in-jsx-scope": "off", - }, - } -); +const eslintConfig = defineConfig([ + ...nextVitals, + ...nextTs, + // Override default ignores of eslint-config-next. + globalIgnores([ + // Default ignores of eslint-config-next: + ".next/**", + "out/**", + "build/**", + "next-env.d.ts", + ]), +]); + +export default eslintConfig; diff --git a/tsconfig.json b/tsconfig.json index ce2ec69..18237b6 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -18,6 +18,10 @@ "jsx": "react-jsx", "incremental": true, "paths": { + "@/components/c-ui/*": [ + "./components/c-ui/*", + "./registry/new-york/*" + ], "@/*": [ "./*" ] @@ -38,4 +42,4 @@ "node_modules", "public" ] -} +} \ No newline at end of file From 4c922596b5ee551646ced64b15d8fa146808fe01 Mon Sep 17 00:00:00 2001 From: phev8 Date: Tue, 24 Feb 2026 13:57:44 +0100 Subject: [PATCH 3/3] feat: update component registry to use 'radix-nova' style, add new AlertProvider and ConfirmProvider components, and enhance README with usage examples --- README.md | 42 +++-- app/interactive-examples.tsx | 98 +++++++++++ app/page.tsx | 136 ++++++++++++--- components.json | 4 +- components/ui/alert-dialog.tsx | 50 +++--- components/ui/button.tsx | 65 +------ components/ui/dialog.tsx | 1 + components/ui/input.tsx | 4 +- components/ui/label.tsx | 2 +- package.json | 5 +- pnpm-lock.yaml | 3 + registry.json | 60 ++++--- registry/new-york/button.tsx | 24 --- .../alert-provider.tsx} | 0 registry/radix-nova/button.tsx | 60 +++++++ .../confirm-dialog.tsx | 0 .../confirm-provider.tsx | 0 registry/radix-nova/dialog.tsx | 159 ++++++++++++++++++ .../loading-button.tsx | 2 +- tsconfig.json | 2 +- 20 files changed, 527 insertions(+), 190 deletions(-) create mode 100644 app/interactive-examples.tsx create mode 100644 components/ui/dialog.tsx delete mode 100644 registry/new-york/button.tsx rename registry/{new-york/alert.tsx => radix-nova/alert-provider.tsx} (100%) create mode 100644 registry/radix-nova/button.tsx rename registry/{new-york => radix-nova}/confirm-dialog.tsx (100%) rename registry/{new-york => radix-nova}/confirm-provider.tsx (100%) create mode 100644 registry/radix-nova/dialog.tsx rename registry/{new-york => radix-nova}/loading-button.tsx (91%) diff --git a/README.md b/README.md index fd43a68..a63aa32 100644 --- a/README.md +++ b/README.md @@ -2,12 +2,13 @@ Custom [shadcn/ui](https://ui.shadcn.com) registry for common coneno components. -This repo builds registry JSON files into `public/r` and deploys them via the static export in `out/` (GitHub Pages). +This repo builds registry JSON files into `public/r/radix-nova` and deploys them via the static export in `out/` (GitHub Pages). ## Hosted registry URLs -- Registry index: `https://coneno.github.io/c-ui/r/registry.json` -- Component entries: `https://coneno.github.io/c-ui/r/.json` +- Registry index: `https://coneno.github.io/c-ui/r/radix-nova/registry.json` +- Style-aware component entries: `https://coneno.github.io/c-ui/r/