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
15 changes: 14 additions & 1 deletion ARCHITECTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,20 @@ Icons are distributed via shadcn CLI:
npx shadcn@latest add https://itshover.com/r/[icon-name].json
```

The `registry.json` file is auto-generated by `npm run registry:build`.
### Registry Generation

The registry is auto-generated in two steps:

1. **`scripts/generate-registry.ts`** - Reads `icons/index.ts` and generates `registry.json`
2. **`shadcn build`** - Reads `registry.json` and generates individual JSON files in `public/r/`

Run both with:

```bash
npm run registry:build
```

This ensures the registry stays in sync with `icons/index.ts` and prevents missing icons.

## Code of Conduct

Expand Down
8 changes: 7 additions & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -249,6 +249,12 @@ Auto-generated when you run `npm run registry:build` in the next step.
npm run registry:build
```

This command does three things:

1. **Generates `registry.json`** from `icons/index.ts` (ensures all icons are included)
2. **Builds individual JSON files** in `public/r/` for the shadcn CLI
3. **Formats** `registry.json` and `public/r/*.json` files

Wait for it to succeed before proceeding.

### Step 4: Run All Checks
Expand Down Expand Up @@ -350,7 +356,7 @@ To verify icons work when installed via the shadcn CLI:
# Run all checks before submitting
npm run check

# Verify registry builds successfully
# Verify registry syncs successfully (generates + builds)
npm run registry:build
```

Expand Down
52 changes: 52 additions & 0 deletions icons/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import AlarmClockPlusIcon from "./alarm-clock-plus-icon";
import AlignCenterIcon from "./align-center-icon";
import AlignVerticalSpaceAroundIcon from "./align-vertical-space-around-icon";
import AmbulanceIcon from "./ambulance-icon";
import AmpersandIcon from "./ampersand-icon";
import AngryIcon from "./angry-icon";
import AnnoyedIcon from "./annoyed-icon";
import ArrowBackIcon from "./arrow-back-icon";
Expand All @@ -14,7 +15,10 @@ import ArrowBigRightIcon from "./arrow-big-right-icon";
import ArrowBigRightDashIcon from "./arrow-big-right-dash-icon";
import ArrowBigUpIcon from "./arrow-big-up-icon";
import ArrowBigUpDashIcon from "./arrow-big-up-dash-icon";
import ArrowBigDownIcon from "./arrow-big-down-icon";
import ArrowBigDownDashIcon from "./arrow-big-down-dash-icon";
import ArrowDown10Icon from "./arrow-down-1-0-icon";
import ArrowDown01Icon from "./arrow-down-0-1-icon";
import ArrowNarrowDownDashedIcon from "./arrow-narrow-down-dashed-icon";
import ArrowNarrowDownIcon from "./arrow-narrow-down-icon";
import ArrowNarrowLeftDashedIcon from "./arrow-narrow-left-dashed-icon";
Expand All @@ -34,6 +38,7 @@ import BluetoothConnectedIcon from "./bluetooth-connected-icon";
import BookIcon from "./book-icon";
import BookmarkIcon from "./bookmark-icon";
import BrandGoogleIcon from "./brand-google-icon";
import BrandNextjsIcon from "./brand-nextjs-icon";
import BrightnessDownIcon from "./brightness-down-icon";
import BulbSvg from "./bulb-svg";
import CameraIcon from "./camera-icon";
Expand All @@ -50,6 +55,7 @@ import ClockIcon from "./clock-icon";
import CodeIcon from "./code-icon";
import CodeXmlIcon from "./code-xml-icon";
import CoffeeIcon from "./coffee-icon";
import CoinBitcoinIcon from "./coin-bitcoin-icon";
import CreditCard from "./credit-card";
import CopyIcon from "./copy-icon";
import DiscordIcon from "./discord-icon";
Expand Down Expand Up @@ -229,6 +235,11 @@ const ICON_LIST: IconType[] = [
icon: AngryIcon,
keywords: ["angry", "mad", "emotion", "face", "emoji", "upset"],
},
{
name: "ampersand-icon",
icon: AmpersandIcon,
keywords: ["ampersand", "and", "symbol", "typography", "character"],
},
{
name: "annoyed-icon",
icon: AnnoyedIcon,
Expand Down Expand Up @@ -286,11 +297,26 @@ const ICON_LIST: IconType[] = [
icon: ArrowBigUpDashIcon,
keywords: ["arrow", "big", "up", "dash", "upload", "top"],
},
{
name: "arrow-big-down-icon",
icon: ArrowBigDownIcon,
keywords: ["arrow", "big", "down", "download", "bottom", "navigation"],
},
{
name: "arrow-big-down-dash-icon",
icon: ArrowBigDownDashIcon,
keywords: ["arrow", "big", "down", "dash", "download", "bottom"],
},
{
name: "arrow-down-1-0-icon",
icon: ArrowDown10Icon,
keywords: ["arrow", "down", "sort", "descending", "number", "order"],
},
{
name: "arrow-down-0-1-icon",
icon: ArrowDown01Icon,
keywords: ["arrow", "down", "sort", "ascending", "number", "order"],
},
{
name: "arrow-narrow-down-dashed-icon",
icon: ArrowNarrowDownDashedIcon,
Expand Down Expand Up @@ -388,6 +414,19 @@ const ICON_LIST: IconType[] = [
icon: BrandGoogleIcon,
keywords: ["google", "brand", "logo", "search", "social"],
},
{
name: "brand-nextjs-icon",
icon: BrandNextjsIcon,
keywords: [
"nextjs",
"next",
"brand",
"logo",
"react",
"framework",
"vercel",
],
},
{
name: "brightness-down-icon",
icon: BrightnessDownIcon,
Expand Down Expand Up @@ -475,6 +514,19 @@ const ICON_LIST: IconType[] = [
icon: CoffeeIcon,
keywords: ["coffee", "cup", "drink", "cafe", "steam", "hot", "beverage"],
},
{
name: "coin-bitcoin-icon",
icon: CoinBitcoinIcon,
keywords: [
"coin",
"bitcoin",
"crypto",
"btc",
"cryptocurrency",
"money",
"digital",
],
},
{
name: "credit-card",
icon: CreditCard,
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
"dev": "next dev",
"build": "next build",
"start": "next start",
"registry:build": "shadcn build",
"registry:build": "npx tsx scripts/generate-registry.ts && shadcn build && prettier --write registry.json public/r",
"lint": "eslint",
"lint:fix": "eslint --fix",
"format": "prettier --write .",
Expand Down
20 changes: 20 additions & 0 deletions public/r/accessibility-icon.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{
"$schema": "https://ui.shadcn.com/schema/registry-item.json",
"name": "accessibility-icon",
"dependencies": ["motion"],
"devDependencies": [],
"registryDependencies": [],
"files": [
{
"path": "icons/accessibility-icon.tsx",
"content": "import { forwardRef, useImperativeHandle } from \"react\";\nimport { AnimatedIconHandle, AnimatedIconProps } from \"./types\";\nimport { motion, useAnimate } from \"motion/react\";\n\nconst AccessibilityIcon = forwardRef<AnimatedIconHandle, AnimatedIconProps>(\n (\n { size = 24, color = \"currentColor\", strokeWidth = 2, className = \"\" },\n ref,\n ) => {\n const [scope, animate] = useAnimate();\n\n const start = async () => {\n animate(\n \".wheel\",\n { rotate: [0, 360] },\n { duration: 1, ease: \"easeInOut\", repeat: Infinity },\n );\n animate(\n \".person\",\n { y: [0, -2, 0] },\n { duration: 0.6, ease: \"easeInOut\" },\n );\n };\n\n const stop = () => {\n animate(\".wheel\", { rotate: 0 }, { duration: 0.3 });\n animate(\".person\", { y: 0 }, { duration: 0.2 });\n };\n\n useImperativeHandle(ref, () => {\n return {\n startAnimation: start,\n stopAnimation: stop,\n };\n });\n\n const handleHoverStart = () => {\n start();\n };\n\n const handleHoverEnd = () => {\n stop();\n };\n\n return (\n <motion.svg\n ref={scope}\n onHoverStart={handleHoverStart}\n onHoverEnd={handleHoverEnd}\n xmlns=\"http://www.w3.org/2000/svg\"\n width={size}\n height={size}\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke={color}\n strokeWidth={strokeWidth}\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n className={`cursor - pointer ${className} `}\n >\n <motion.circle className=\"person\" cx=\"16\" cy=\"4\" r=\"1\" />\n <motion.path className=\"person\" d=\"m18 19 1-7-6 1\" />\n <motion.path className=\"person\" d=\"m5 8 3-3 5.5 3-2.36 3.5\" />\n <motion.g className=\"wheel\" style={{ transformOrigin: \"8.5px 17.5px\" }}>\n <path d=\"M4.24 14.5a5 5 0 0 0 6.88 6\" />\n <path d=\"M13.76 17.5a5 5 0 0 0-6.88-6\" />\n </motion.g>\n </motion.svg>\n );\n },\n);\n\nAccessibilityIcon.displayName = \"AccessibilityIcon\";\n\nexport default AccessibilityIcon;\n",
Comment thread
luth-v marked this conversation as resolved.
"type": "registry:ui"
},
{
"path": "icons/types.ts",
"content": "export interface AnimatedIconProps {\n /** Icon size in pixels or CSS string */\n size?: number | string;\n /** Icon color (defaults to currentColor) */\n color?: string;\n /** SVG stroke width */\n strokeWidth?: number;\n /** Additional CSS classes */\n className?: string;\n}\n\nexport interface AnimatedIconHandle {\n startAnimation: () => void;\n stopAnimation: () => void;\n}\n",
"type": "registry:ui"
}
],
"type": "registry:ui"
}
20 changes: 20 additions & 0 deletions public/r/ampersand-icon.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{
"$schema": "https://ui.shadcn.com/schema/registry-item.json",
"name": "ampersand-icon",
"dependencies": ["motion"],
"devDependencies": [],
"registryDependencies": [],
"files": [
{
"path": "icons/ampersand-icon.tsx",
"content": "import { forwardRef, useImperativeHandle } from \"react\";\nimport { AnimatedIconHandle, AnimatedIconProps } from \"./types\";\nimport { motion, useAnimate } from \"motion/react\";\n\nconst AmpersandIcon = forwardRef<AnimatedIconHandle, AnimatedIconProps>(\n ({ size = 40, className = \"\" }, ref) => {\n const [scope, animate] = useAnimate();\n\n const start = async () => {\n animate(\"path\", { pathLength: [0, 1] }, { duration: 0.6 });\n };\n\n const stop = async () => {\n animate(\"path\", { pathLength: 1 }, { duration: 0.3 });\n };\n\n useImperativeHandle(ref, () => {\n return {\n startAnimation: start,\n stopAnimation: stop,\n };\n });\n\n const handleHoverStart = () => {\n start();\n };\n\n const handleHoverEnd = () => {\n stop();\n };\n\n return (\n <motion.svg\n ref={scope}\n onHoverStart={handleHoverStart}\n onHoverEnd={handleHoverEnd}\n xmlns=\"http://www.w3.org/2000/svg\"\n width={size}\n height={size}\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n className={`cursor-pointer ${className}`}\n >\n <path d=\"M16 12h3\" />\n <path d=\"M17.5 12a8 8 0 0 1-8 8A4.5 4.5 0 0 1 5 15.5c0-6 8-4 8-8.5a3 3 0 1 0-6 0c0 3 2.5 8.5 12 13\" />\n </motion.svg>\n );\n },\n);\n\nAmpersandIcon.displayName = \"AmpersandIcon\";\n\nexport default AmpersandIcon;\n",
Comment thread
luth-v marked this conversation as resolved.
"type": "registry:ui"
},
{
"path": "icons/types.ts",
"content": "export interface AnimatedIconProps {\n /** Icon size in pixels or CSS string */\n size?: number | string;\n /** Icon color (defaults to currentColor) */\n color?: string;\n /** SVG stroke width */\n strokeWidth?: number;\n /** Additional CSS classes */\n className?: string;\n}\n\nexport interface AnimatedIconHandle {\n startAnimation: () => void;\n stopAnimation: () => void;\n}\n",
"type": "registry:ui"
}
],
"type": "registry:ui"
}
20 changes: 20 additions & 0 deletions public/r/arrow-big-down-dash-icon.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{
"$schema": "https://ui.shadcn.com/schema/registry-item.json",
"name": "arrow-big-down-dash-icon",
"dependencies": ["motion"],
"devDependencies": [],
"registryDependencies": [],
"files": [
{
"path": "icons/arrow-big-down-dash-icon.tsx",
"content": "import { forwardRef, useImperativeHandle } from \"react\";\nimport { AnimatedIconHandle, AnimatedIconProps } from \"./types\";\nimport { motion, useAnimate } from \"motion/react\";\n\nconst ArrowBigDownDashIcon = forwardRef<AnimatedIconHandle, AnimatedIconProps>(\n ({ size = 40, className = \"\" }, ref) => {\n const [scope, animate] = useAnimate();\n\n const start = async () => {\n animate(\".arrow\", { y: [0, 4, 0] }, { duration: 0.5 });\n animate(\".dash\", { opacity: [1, 0.5, 1] }, { duration: 0.5 });\n };\n\n const stop = async () => {\n animate(\".arrow\", { y: 0 }, { duration: 0.3 });\n animate(\".dash\", { opacity: 1 }, { duration: 0.3 });\n };\n\n useImperativeHandle(ref, () => {\n return {\n startAnimation: start,\n stopAnimation: stop,\n };\n });\n\n const handleHoverStart = () => {\n start();\n };\n\n const handleHoverEnd = () => {\n stop();\n };\n\n return (\n <motion.svg\n ref={scope}\n onHoverStart={handleHoverStart}\n onHoverEnd={handleHoverEnd}\n xmlns=\"http://www.w3.org/2000/svg\"\n width={size}\n height={size}\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n className={`cursor-pointer ${className}`}\n >\n <motion.path\n className=\"arrow\"\n d=\"M15 11a1 1 0 0 0 1 1h2.939a1 1 0 0 1 .75 1.811l-6.835 6.836a1.207 1.207 0 0 1-1.707 0L4.31 13.81a1 1 0 0 1 .75-1.811H8a1 1 0 0 0 1-1V9a1 1 0 0 1 1-1h4a1 1 0 0 1 1 1z\"\n />\n <motion.path className=\"dash\" d=\"M9 4h6\" />\n </motion.svg>\n );\n },\n);\n\nArrowBigDownDashIcon.displayName = \"ArrowBigDownDashIcon\";\n\nexport default ArrowBigDownDashIcon;\n",
"type": "registry:ui"
},
{
"path": "icons/types.ts",
"content": "export interface AnimatedIconProps {\n /** Icon size in pixels or CSS string */\n size?: number | string;\n /** Icon color (defaults to currentColor) */\n color?: string;\n /** SVG stroke width */\n strokeWidth?: number;\n /** Additional CSS classes */\n className?: string;\n}\n\nexport interface AnimatedIconHandle {\n startAnimation: () => void;\n stopAnimation: () => void;\n}\n",
"type": "registry:ui"
}
],
"type": "registry:ui"
}
20 changes: 20 additions & 0 deletions public/r/arrow-big-down-icon.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{
"$schema": "https://ui.shadcn.com/schema/registry-item.json",
"name": "arrow-big-down-icon",
"dependencies": ["motion"],
"devDependencies": [],
"registryDependencies": [],
"files": [
{
"path": "icons/arrow-big-down-icon.tsx",
"content": "import { forwardRef, useImperativeHandle } from \"react\";\nimport { AnimatedIconHandle, AnimatedIconProps } from \"./types\";\nimport { motion, useAnimate } from \"motion/react\";\n\nconst ArrowBigDownIcon = forwardRef<AnimatedIconHandle, AnimatedIconProps>(\n ({ size = 40, className = \"\" }, ref) => {\n const [scope, animate] = useAnimate();\n\n const start = async () => {\n animate(scope.current, { y: [0, 4, 0] }, { duration: 0.5 });\n };\n\n const stop = async () => {\n animate(scope.current, { y: 0 }, { duration: 0.3 });\n };\n\n useImperativeHandle(ref, () => {\n return {\n startAnimation: start,\n stopAnimation: stop,\n };\n });\n\n const handleHoverStart = () => {\n start();\n };\n\n const handleHoverEnd = () => {\n stop();\n };\n\n return (\n <motion.svg\n ref={scope}\n onHoverStart={handleHoverStart}\n onHoverEnd={handleHoverEnd}\n xmlns=\"http://www.w3.org/2000/svg\"\n width={size}\n height={size}\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n className={`cursor-pointer ${className}`}\n >\n <path d=\"M15 11a1 1 0 0 0 1 1h2.939a1 1 0 0 1 .75 1.811l-6.835 6.836a1.207 1.207 0 0 1-1.707 0L4.31 13.81a1 1 0 0 1 .75-1.811H8a1 1 0 0 0 1-1V5a1 1 0 0 1 1-1h4a1 1 0 0 1 1 1z\" />\n </motion.svg>\n );\n },\n);\n\nArrowBigDownIcon.displayName = \"ArrowBigDownIcon\";\n\nexport default ArrowBigDownIcon;\n",
Comment thread
luth-v marked this conversation as resolved.
"type": "registry:ui"
},
{
"path": "icons/types.ts",
"content": "export interface AnimatedIconProps {\n /** Icon size in pixels or CSS string */\n size?: number | string;\n /** Icon color (defaults to currentColor) */\n color?: string;\n /** SVG stroke width */\n strokeWidth?: number;\n /** Additional CSS classes */\n className?: string;\n}\n\nexport interface AnimatedIconHandle {\n startAnimation: () => void;\n stopAnimation: () => void;\n}\n",
"type": "registry:ui"
}
],
"type": "registry:ui"
}
20 changes: 20 additions & 0 deletions public/r/arrow-down-0-1-icon.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{
"$schema": "https://ui.shadcn.com/schema/registry-item.json",
"name": "arrow-down-0-1-icon",
"dependencies": ["motion"],
"devDependencies": [],
"registryDependencies": [],
"files": [
{
"path": "icons/arrow-down-0-1-icon.tsx",
"content": "import { forwardRef, useImperativeHandle } from \"react\";\nimport { AnimatedIconHandle, AnimatedIconProps } from \"./types\";\nimport { motion, useAnimate } from \"motion/react\";\n\nconst ArrowDown01Icon = forwardRef<AnimatedIconHandle, AnimatedIconProps>(\n ({ size = 40, className = \"\" }, ref) => {\n const [scope, animate] = useAnimate();\n\n const swapDistance = 10;\n\n const start = async () => {\n animate(\n \".zero\",\n { y: swapDistance },\n { duration: 0.3, ease: \"easeInOut\" },\n );\n animate(\n \".one\",\n { y: -swapDistance },\n { duration: 0.3, ease: \"easeInOut\" },\n );\n };\n\n const stop = async () => {\n animate(\".zero, .one\", { y: 0 }, { duration: 0.3, ease: \"easeInOut\" });\n };\n\n useImperativeHandle(ref, () => {\n return {\n startAnimation: start,\n stopAnimation: stop,\n };\n });\n\n const handleHoverStart = () => {\n start();\n };\n\n const handleHoverEnd = () => {\n stop();\n };\n\n return (\n <motion.svg\n ref={scope}\n onHoverStart={handleHoverStart}\n onHoverEnd={handleHoverEnd}\n xmlns=\"http://www.w3.org/2000/svg\"\n width={size}\n height={size}\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n className={`cursor-pointer ${className}`}\n >\n <path d=\"m3 16 4 4 4-4\" />\n <path d=\"M7 20V4\" />\n <motion.rect\n className=\"zero\"\n x=\"15\"\n y=\"4\"\n width=\"4\"\n height=\"6\"\n ry=\"2\"\n />\n <motion.g className=\"one\">\n <path d=\"M17 20v-6h-2\" />\n <path d=\"M15 20h4\" />\n </motion.g>\n </motion.svg>\n );\n },\n);\n\nArrowDown01Icon.displayName = \"ArrowDown01Icon\";\n\nexport default ArrowDown01Icon;\n",
"type": "registry:ui"
},
{
"path": "icons/types.ts",
"content": "export interface AnimatedIconProps {\n /** Icon size in pixels or CSS string */\n size?: number | string;\n /** Icon color (defaults to currentColor) */\n color?: string;\n /** SVG stroke width */\n strokeWidth?: number;\n /** Additional CSS classes */\n className?: string;\n}\n\nexport interface AnimatedIconHandle {\n startAnimation: () => void;\n stopAnimation: () => void;\n}\n",
"type": "registry:ui"
}
],
"type": "registry:ui"
}
Loading