Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .github/workflows/deploy-registry-pages.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ concurrency:
jobs:
build:
runs-on: ubuntu-latest
env:
NEXT_PUBLIC_BASE_PATH: /c-ui
steps:
- name: Checkout
uses: actions/checkout@v4
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
# next.js
/.next/
/out/
/.source/

# production
/build
Expand Down
198 changes: 65 additions & 133 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,162 +1,94 @@
# c-ui registry

Custom [shadcn/ui](https://ui.shadcn.com) registry for common coneno components.

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
Repository for the coneno shadcn registry and its documentation site.

- Public docs: `https://coneno.github.io/c-ui/docs/`
- Registry index: `https://coneno.github.io/c-ui/r/radix-nova/registry.json`
- Style-aware component entries: `https://coneno.github.io/c-ui/r/<style>/<component-name>.json`
- Canonical style: `radix-nova`

## Install via namespace (recommended)

Configure the registry once in your consumer app's `components.json`:

```json
{
"style": "radix-nova",
"registries": {
"@c-ui": "https://coneno.github.io/c-ui/r/{style}/{name}.json"
}
}
```

This registry currently supports only `radix-nova`. Set `"style": "radix-nova"` in the consumer app before installing components.

Then install components without repeating full URLs:

```bash
npx shadcn@latest add @c-ui/loading-button
```

You can also install multiple components in one command:

```bash
npx shadcn@latest add @c-ui/alert-provider @c-ui/confirm
```

## Local registry testing
This README is intentionally contributor-focused. End-user installation and component usage live in the docs site.

For local development/testing of this registry, point `@c-ui` to localhost in the consumer app:
## What this repo contains

```json
{
"style": "radix-nova",
"registries": {
"@c-ui": "http://localhost:3000/c-ui/r/{style}/{name}.json"
}
}
```

Then install as usual:
- `registry/radix-nova/*`: source component files used to generate registry JSON.
- `registry.json`: registry manifest consumed by `shadcn build`.
- `public/r/radix-nova/*`: generated registry JSON artifacts (committed).
- `content/docs/*`: Fumadocs content for the documentation pages.
- `app/docs/*`: Fumadocs app routes/layout.
- `.github/workflows/deploy-registry-pages.yml`: GitHub Pages build/deploy workflow.

```bash
npx shadcn@latest add @c-ui/loading-button
```
## Prerequisites

## Direct URL install (optional)
- Node.js 22 (matches CI)
- pnpm 10.28.2

If needed, you can still install directly from a component URL:
## Common commands

```bash
npx shadcn@latest add https://coneno.github.io/c-ui/r/radix-nova/loading-button.json
pnpm install # install dependencies
pnpm dev # run local site at http://localhost:3000/c-ui
pnpm registry:build # generate registry JSON into public/r/radix-nova
pnpm build # GitHub Pages build (basePath=/c-ui) + static export into out/
pnpm start # serve ./out with a static file server
```

Current components in this registry:

- `alert-provider`: Alert dialog service with provider + hook for promise-based alerts.
- `button`: Press-animated base button used across components.
- `confirm`: Confirmation dialog service with provider + hook.
- `dialog`: Customized replacement for shadcn `components/ui/dialog.tsx` with overridable close labels to support i18n and screen-reader accessibility.
- `loading-button`: Button with a built-in loading state.
## Contributor workflow

## Usage examples
1. Install dependencies with `pnpm install`.
2. Make your code/docs changes.
3. Regenerate registry artifacts with `pnpm registry:build` if registry items changed.
4. Validate with `pnpm build`.
5. Commit source changes and generated `public/r/radix-nova/*` output together.

```tsx
import { Button } from "@/components/ui/button"
## Add a new registry item

export function SaveAction() {
return <Button>Save changes</Button>
}
```
1. Add the component source file(s) under `registry/radix-nova/`.
2. Add an item in `registry.json`:
- required baseline: `name`, `type`, `title`, `description`, `files`, `style`
- optional: `registryDependencies`, `dependencies`
3. Ensure each `files[]` entry has:
- `path`: source file in this repo
- `target`: destination path in consumer projects
- `type`: usually `registry:component`
4. Run:

```tsx
import { LoadingButton } from "@/components/c-ui/loading-button"
```bash
pnpm registry:build
```

export function SubmitAction() {
return <LoadingButton isLoading>Submitting</LoadingButton>
}
```
5. Confirm generated files:
- `public/r/radix-nova/<name>.json`
- `public/r/radix-nova/registry.json` includes the item
6. Add docs page for the new item:
- create `content/docs/components/<name>.mdx`
- add the page slug to `content/docs/components/meta.json`
7. Run `pnpm build` and verify the docs route and static export succeed.

## Deployment (GitHub Pages)
## Update existing items

The workflow in `.github/workflows/deploy-registry-pages.yml` deploys on pushes to `main`.
When component APIs or behavior change:

First-time setup:
1. Update source files in `registry/radix-nova/`.
2. Update metadata in `registry.json` if title/description/dependencies changed.
3. Update the corresponding docs page in `content/docs/components/`.
4. Regenerate registry JSON with `pnpm registry:build`.

1. Open GitHub repository settings.
2. Go to **Pages**.
3. Set **Source** to **GitHub Actions**.
4. Push to `main` (or run the workflow manually from the Actions tab).
## Docs authoring notes

The workflow does:
- Docs are built with Fumadocs and rendered from `content/docs`.
- Sidebar order is controlled by `meta.json` files.
- Interactive demos used in docs live in `components/docs/interactive-examples.tsx`.

1. Install dependencies with pnpm.
2. Build registry output with `pnpm registry:build`.
3. Build the static site with `pnpm build`.
4. Publish `out/` to GitHub Pages.
## Deployment

## Local development
Deploy runs automatically on pushes to `main` via GitHub Actions:

Install dependencies:
1. `pnpm install --frozen-lockfile`
2. `pnpm registry:build`
3. `pnpm build`
4. publish `out/` to GitHub Pages

```bash
pnpm install
```

Run the app locally (registry files available at `/c-ui/r/radix-nova/*`):

```bash
pnpm dev
```

Build registry JSON output:

```bash
pnpm registry:build
```

## Add a new component to the registry

1. Create the component source file in `registry/radix-nova/` (example: `registry/radix-nova/my-component.tsx`).
2. Add a new item to `registry.json`.
3. Set `name`, `type`, `title`, `description`, `registryDependencies`, and `files`.
- You can organize component files in subfolders as well.
- Example source path: `registry/radix-nova/forms/my-component.tsx`
- Example target path: `components/c-ui/forms/my-component.tsx`
4. Rebuild output:

```bash
pnpm registry:build
```

5. Confirm generated files exist in `public/r/radix-nova/`.
6. Validate install in a consumer app:

```bash
npx shadcn@latest add @c-ui/my-component
```

7. Commit both source files and generated registry output.

## Build output details

`pnpm registry:build` runs:

```bash
shadcn build --output public/r/radix-nova
```
## Important conventions

That command reads `registry.json` and writes distributable registry JSON files to `public/r/radix-nova`.
- Do not edit `public/r/radix-nova/*` manually.
- Always rebuild artifacts after changing `registry.json` or files in `registry/radix-nova/`.
- Keep docs in sync with component APIs to avoid stale installation/configuration guidance.
44 changes: 44 additions & 0 deletions app/docs/[[...slug]]/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import type { Metadata } from "next";
import { notFound } from "next/navigation";
import defaultMdxComponents from "fumadocs-ui/mdx";
import { DocsBody, DocsDescription, DocsPage, DocsTitle } from "fumadocs-ui/page";
import { source } from "@/lib/source";

type PageProps = {
params: Promise<{ slug?: string[] }>;
};

export default async function Page(props: PageProps) {
const params = await props.params;
const page = source.getPage(params.slug);

if (!page) notFound();

const Mdx = page.data.body;

return (
<DocsPage toc={page.data.toc} full={page.data.full}>
<DocsTitle>{page.data.title}</DocsTitle>
<DocsDescription>{page.data.description}</DocsDescription>
<DocsBody>
<Mdx components={defaultMdxComponents} />
</DocsBody>
</DocsPage>
);
}

export function generateStaticParams() {
return source.generateParams();
}

export async function generateMetadata(props: PageProps): Promise<Metadata> {
const params = await props.params;
const page = source.getPage(params.slug);

if (!page) notFound();

return {
title: `${page.data.title} | c-ui registry docs`,
description: page.data.description,
};
}
15 changes: 15 additions & 0 deletions app/docs/layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import type { ReactNode } from "react";
import { DocsLayout } from "fumadocs-ui/layouts/docs";
import { baseOptions } from "@/app/layout.config";
import { source } from "@/lib/source";

export default function Layout({ children }: { children: ReactNode }) {
return (
<DocsLayout
tree={source.pageTree}
{...baseOptions}
>
{children}
</DocsLayout>
);
}
2 changes: 1 addition & 1 deletion app/globals.css
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
@import "tw-animate-css";
@import "shadcn/tailwind.css";
@import "fumadocs-ui/style.css";
@import 'fumadocs-ui/css/neutral.css';
@import 'fumadocs-ui/css/shadcn.css';
@import 'fumadocs-ui/css/preset.css';

@custom-variant dark (&:is(.dark *));
Expand Down
10 changes: 10 additions & 0 deletions app/layout.config.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import type { BaseLayoutProps } from "fumadocs-ui/layouts/shared";

export const baseOptions: BaseLayoutProps = {
nav: {
title: "c-ui registry docs",
},
searchToggle: {
enabled: false,
},
};
8 changes: 6 additions & 2 deletions app/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
import "./globals.css";
import { RootProvider } from "fumadocs-ui/provider/next";


export default function RootLayout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
return (
<html lang="en">
<body>{children}</body>
<html lang="en" suppressHydrationWarning>
<body className="min-h-screen bg-background text-foreground">
<RootProvider search={{ enabled: false }}>{children}</RootProvider>
</body>
</html>
);
}
Loading