Registry
How the shadcn registry endpoint and the full index are generated.
What a registry is
A registry is shadcn’s source-distribution format. This project produces two kinds of output:
- Installable endpoint
https://tennantite.rikka.cc/r/<name>.json: one file per core component with all source inlined ascontentplustarget, directly consumable by the shadcn CLI. - Full index
registry/registry.json: covers@tennantite/uiand every mod package (311 entries) for catalog and documentation reference; mod entries keeppathonly and do not participate in CLI copying.
Generate
pnpm registry
# Equivalent to
node registry/build.mjs
Coverage
The installable endpoint covers 5 component groups inside @tennantite/ui (primitives, containers, screens/containers, screens/hud, screens/menus) — 63 component items — plus tennantite-base and 4 shared items:
tennantite-base: only declares the@tennantite/tokensand@tennantite/texturesnpm dependencies and drops abase.cssrecording the asset CSS to import; the CLI installs it alongside the first component you add.tennantite-utils: sharedcnandguihelpers, placed atcomponents/tennantite/lib/tennantite-utils.tswithout touching your own@/lib/utils.screen-types: shared screen types such asScreenSlotandpadSlots.screen-parts/screen-slot: shared slot and part components used by container screens.
Mod packages (mekanism, ic2, ae2, …) are outside the CLI copy scope: they import heavily through the @tennantite/ui barrel, which makes copy-and-rewrite disproportionately expensive. They keep the pnpm add @tennantite/<mod> npm-install mode.
Consuming it
Option 1: configure a namespace registry in components.json and install by name:
{
"registries": {
"@tennantite": "https://tennantite.rikka.cc/r/{name}.json"
}
}
npx shadcn add @tennantite/button
Option 2: direct URL, no configuration needed:
npx shadcn add https://tennantite.rikka.cc/r/button.json
Item shape
Every item inlines its source so the CLI never resolves repo paths:
{
"$schema": "https://ui.shadcn.com/schema/registry-item.json",
"name": "card",
"type": "registry:component",
"dependencies": ["cn"],
"registryDependencies": ["@tennantite/tennantite-base", "@tennantite/panel", "@tennantite/title-bar"],
"files": [
{
"content": "import { Panel } from \"@/components/tennantite/panel\";\n// …",
"target": "components/tennantite/card/index.tsx",
"type": "registry:component"
}
]
}
Import rewrite rules
For every inlined file the generator rewrites imports with these rules (a failed rewrite aborts the build instead of producing a broken item):
- Relative imports resolving inside the component’s own directory (
./types,./styles) are kept as-is. - Imports pointing at another component directory (e.g.
../panel/index) become@/components/tennantite/paneland are recorded inregistryDependencies. - Imports of
lib/utils.tsand shared screen types become@/components/tennantite/lib/tennantite-utilsand@/components/tennantite/lib/screen-types. @tennantite/texturesis kept untouched and appended to the item’s npmdependencies.
Every entry in registryDependencies automatically gains @tennantite/tennantite-base, so the L0 asset packages (tokens/textures) are installed on first add. Cross-item dependencies must be fully namespaced (@tennantite/<name>); unqualified names are resolved against the official shadcn registry by the CLI.