slashh-ui 1.3.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (97) hide show
  1. package/README.md +36 -0
  2. package/dist/__registry__/index.d.ts +1 -0
  3. package/dist/__registry__/index.js +492 -0
  4. package/dist/app/(auth)/layout.d.ts +5 -0
  5. package/dist/app/(auth)/layout.js +8 -0
  6. package/dist/app/(auth)/login/page.d.ts +1 -0
  7. package/dist/app/(auth)/login/page.js +45 -0
  8. package/dist/app/(protected)/component/[id]/page.d.ts +5 -0
  9. package/dist/app/(protected)/component/[id]/page.js +13 -0
  10. package/dist/app/(protected)/component/page.d.ts +2 -0
  11. package/dist/app/(protected)/component/page.js +26 -0
  12. package/dist/app/(protected)/docs/page.d.ts +2 -0
  13. package/dist/app/(protected)/docs/page.js +43 -0
  14. package/dist/app/account/page.d.ts +1 -0
  15. package/dist/app/account/page.js +38 -0
  16. package/dist/app/api/me/route.d.ts +8 -0
  17. package/dist/app/api/me/route.js +20 -0
  18. package/dist/app/layout.d.ts +6 -0
  19. package/dist/app/layout.js +21 -0
  20. package/dist/app/page.d.ts +2 -0
  21. package/dist/app/page.js +11 -0
  22. package/dist/app/pricing/page.d.ts +2 -0
  23. package/dist/app/pricing/page.js +5 -0
  24. package/dist/bin/index.d.ts +2 -0
  25. package/dist/bin/index.js +27 -0
  26. package/dist/components/smooth-scroll.d.ts +4 -0
  27. package/dist/components/smooth-scroll.js +21 -0
  28. package/dist/components/toast.d.ts +11 -0
  29. package/dist/components/toast.js +39 -0
  30. package/dist/components/ui/IndustryProof.d.ts +1 -0
  31. package/dist/components/ui/IndustryProof.js +55 -0
  32. package/dist/components/ui/ShowcaseContainer.d.ts +8 -0
  33. package/dist/components/ui/ShowcaseContainer.js +139 -0
  34. package/dist/components/ui/dot-cursor.d.ts +2 -0
  35. package/dist/components/ui/dot-cursor.js +70 -0
  36. package/dist/components/ui/featuredComponents.d.ts +2 -0
  37. package/dist/components/ui/featuredComponents.js +14 -0
  38. package/dist/components/ui/footer.d.ts +2 -0
  39. package/dist/components/ui/footer.js +5 -0
  40. package/dist/components/ui/hero.d.ts +3 -0
  41. package/dist/components/ui/hero.js +21 -0
  42. package/dist/components/ui/navbar.d.ts +3 -0
  43. package/dist/components/ui/navbar.js +102 -0
  44. package/dist/components/ui/pricing.d.ts +2 -0
  45. package/dist/components/ui/pricing.js +26 -0
  46. package/dist/hooks/use-component-search.d.ts +5 -0
  47. package/dist/hooks/use-component-search.js +37 -0
  48. package/dist/lib/actions/auth.action.d.ts +8 -0
  49. package/dist/lib/actions/auth.action.js +66 -0
  50. package/dist/lib/auth.d.ts +1 -0
  51. package/dist/lib/auth.js +15 -0
  52. package/dist/lib/email.d.ts +1 -0
  53. package/dist/lib/email.js +42 -0
  54. package/dist/lib/prisma.d.ts +2 -0
  55. package/dist/lib/prisma.js +8 -0
  56. package/dist/lib/registry.d.ts +1 -0
  57. package/dist/lib/registry.js +16 -0
  58. package/dist/lib/utils.d.ts +2 -0
  59. package/dist/lib/utils.js +5 -0
  60. package/dist/middleware/middleware.d.ts +5 -0
  61. package/dist/middleware/middleware.js +19 -0
  62. package/dist/next.config.d.ts +3 -0
  63. package/dist/next.config.js +4 -0
  64. package/dist/prisma.config.d.ts +3 -0
  65. package/dist/prisma.config.js +13 -0
  66. package/dist/registry/details/buttons/neubrutal-button-details.d.ts +2 -0
  67. package/dist/registry/details/buttons/neubrutal-button-details.js +25 -0
  68. package/dist/registry/details/cursor/dot-cursor-details.d.ts +2 -0
  69. package/dist/registry/details/cursor/dot-cursor-details.js +5 -0
  70. package/dist/registry/details/navbar/floating-navbar-details.d.ts +2 -0
  71. package/dist/registry/details/navbar/floating-navbar-details.js +5 -0
  72. package/dist/registry/details/scrollbars/minimal-scrollbar-details.d.ts +0 -0
  73. package/dist/registry/details/scrollbars/minimal-scrollbar-details.js +1 -0
  74. package/dist/registry/index.d.ts +7 -0
  75. package/dist/registry/index.js +31 -0
  76. package/dist/registry/ui/buttons/neubrutal-button.d.ts +6 -0
  77. package/dist/registry/ui/buttons/neubrutal-button.js +18 -0
  78. package/dist/registry/ui/cursors/dot-cursor.d.ts +2 -0
  79. package/dist/registry/ui/cursors/dot-cursor.js +70 -0
  80. package/dist/registry/ui/navbars/floating-navbar.d.ts +2 -0
  81. package/dist/registry/ui/navbars/floating-navbar.js +35 -0
  82. package/dist/registry/ui/scrollbars/minimal-scrollbar.d.ts +2 -0
  83. package/dist/registry/ui/scrollbars/minimal-scrollbar.js +171 -0
  84. package/dist/scripts/build-registry.d.ts +1 -0
  85. package/dist/scripts/build-registry.js +47 -0
  86. package/dist/src/commands/add.d.ts +1 -0
  87. package/dist/src/commands/add.js +64 -0
  88. package/dist/src/commands/init.d.ts +1 -0
  89. package/dist/src/commands/init.js +88 -0
  90. package/dist/src/commands/list.d.ts +1 -0
  91. package/dist/src/commands/list.js +37 -0
  92. package/dist/src/index.d.ts +2 -0
  93. package/dist/src/index.js +28 -0
  94. package/dist/src/utils/get-pkg-manager.d.ts +1 -0
  95. package/dist/src/utils/get-pkg-manager.js +10 -0
  96. package/dist/tsconfig.tsbuildinfo +1 -0
  97. package/package.json +71 -0
@@ -0,0 +1,31 @@
1
+ // registry/index.ts
2
+ export const Index = [
3
+ {
4
+ name: 'neubrutal-button',
5
+ type: 'ui',
6
+ files: ['ui/buttons/neubrutal-button.tsx'],
7
+ category: 'buttons',
8
+ install: 'npm install framer-motion lucide-react',
9
+ },
10
+ {
11
+ name: 'dot-cursor',
12
+ type: 'ui',
13
+ files: ['ui/cursors/dot-cursor.tsx'],
14
+ category: 'cursors',
15
+ install: 'npm install framer-motion',
16
+ },
17
+ {
18
+ name: 'flaoting-navbar',
19
+ type: 'ui',
20
+ files: ['ui/navbars/floating-navbar.tsx'],
21
+ category: 'navbars',
22
+ install: 'npm install lucide-react framer-motion gsap',
23
+ },
24
+ {
25
+ name: 'minimal-scrollbar',
26
+ type: 'ui',
27
+ files: ['ui/scrollbars/minimal-scrollbar.tsx'],
28
+ category: 'scrollbars',
29
+ install: '',
30
+ }
31
+ ];
@@ -0,0 +1,6 @@
1
+ import React from 'react';
2
+ interface NeubrutalButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
3
+ children: React.ReactNode;
4
+ }
5
+ export default function NeubrutalButton({ children, className, ...props }: NeubrutalButtonProps): import("react/jsx-runtime").JSX.Element;
6
+ export {};
@@ -0,0 +1,18 @@
1
+ var __rest = (this && this.__rest) || function (s, e) {
2
+ var t = {};
3
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
4
+ t[p] = s[p];
5
+ if (s != null && typeof Object.getOwnPropertySymbols === "function")
6
+ for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
7
+ if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
8
+ t[p[i]] = s[p[i]];
9
+ }
10
+ return t;
11
+ };
12
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
13
+ import { cn } from '@/lib/utils';
14
+ // 1. CHANGE THIS TO DEFAULT EXPORT
15
+ export default function NeubrutalButton(_a) {
16
+ var { children = 'Click Me!', className } = _a, props = __rest(_a, ["children", "className"]);
17
+ return (_jsxs("button", Object.assign({}, props, { className: cn('group relative w-[140px] h-[50px] bg-none outline-none border-none p-0 cursor-pointer active:translate-y-[2px] transition-transform', className), children: [_jsx("div", { className: 'absolute top-[14px] -left-[1px] w-[calc(100%+2px)] h-full bg-[#8c8c8c] rounded-[7mm] outline outline-2 outline-[#242622] -z-10' }), _jsxs("div", { className: 'absolute top-[10px] left-0 w-full h-full bg-[#e5e5c7] rounded-[7mm] outline outline-2 outline-[#242622] -z-10', children: [_jsx("div", { className: 'absolute bottom-0 left-[15%] w-[2px] h-[9px] bg-[#242622]' }), _jsx("div", { className: 'absolute bottom-0 left-[85%] w-[2px] h-[9px] bg-[#242622]' })] }), _jsxs("div", { className: 'relative w-full h-full flex items-center justify-center bg-[#ffffee] rounded-[7mm] outline outline-2 outline-[#242622] text-[#242622] font-semibold text-base overflow-hidden transition-all duration-200 group-active:translate-y-[10px]', children: [_jsx("div", { className: 'absolute top-0 -left-[20px] w-[15px] h-full bg-black/10 skew-x-[30deg] transition-all duration-300 group-active:left-[calc(100%+20px)]' }), children] })] })));
18
+ }
@@ -0,0 +1,2 @@
1
+ declare const CustomCursor: () => import("react/jsx-runtime").JSX.Element;
2
+ export default CustomCursor;
@@ -0,0 +1,70 @@
1
+ 'use client';
2
+ import { jsx as _jsx } from "react/jsx-runtime";
3
+ import { useState, useEffect } from 'react';
4
+ const CustomCursor = () => {
5
+ const [position, setPosition] = useState({ x: 0, y: 0 });
6
+ const [isHovering, setIsHovering] = useState(false);
7
+ const [isDarkBackground, setIsDarkBackground] = useState(false);
8
+ useEffect(() => {
9
+ // document.body.style.cursor = 'none';
10
+ const updatePosition = (e) => {
11
+ setPosition({ x: e.clientX, y: e.clientY });
12
+ const element = document.elementFromPoint(e.clientX, e.clientY);
13
+ if (element) {
14
+ const bgColor = window.getComputedStyle(element).backgroundColor;
15
+ const brightness = getBrightness(bgColor);
16
+ setIsDarkBackground(brightness < 128);
17
+ }
18
+ };
19
+ const handleMouseOver = (e) => {
20
+ const target = e.target;
21
+ if (target.tagName === 'A' ||
22
+ target.tagName === 'BUTTON' ||
23
+ target.onclick !== null ||
24
+ window.getComputedStyle(target).cursor === 'pointer') {
25
+ setIsHovering(true);
26
+ }
27
+ };
28
+ const handleMouseOut = () => {
29
+ setIsHovering(false);
30
+ };
31
+ window.addEventListener('mousemove', updatePosition);
32
+ document.addEventListener('mouseover', handleMouseOver);
33
+ document.addEventListener('mouseout', handleMouseOut);
34
+ return () => {
35
+ window.removeEventListener('mousemove', updatePosition);
36
+ document.removeEventListener('mouseover', handleMouseOver);
37
+ document.removeEventListener('mouseout', handleMouseOut);
38
+ };
39
+ }, []);
40
+ const getBrightness = (color) => {
41
+ const rgb = color.match(/\d+/g);
42
+ if (!rgb)
43
+ return 255;
44
+ const r = parseInt(rgb[0]);
45
+ const g = parseInt(rgb[1]);
46
+ const b = parseInt(rgb[2]);
47
+ return (r * 299 + g * 587 + b * 114) / 1000;
48
+ };
49
+ return (_jsx("div", { style: {
50
+ cursor: 'none',
51
+ width: '100vw',
52
+ height: '100vh',
53
+ position: 'fixed',
54
+ top: 0,
55
+ left: 0,
56
+ pointerEvents: 'none'
57
+ }, children: _jsx("div", { style: {
58
+ position: 'fixed',
59
+ left: `${position.x}px`,
60
+ top: `${position.y}px`,
61
+ pointerEvents: 'none',
62
+ transform: `translate(-50%, -50%) scale(${isHovering ? 1.5 : 1})`,
63
+ transition: 'transform 0.2s ease',
64
+ zIndex: 9999,
65
+ }, children: _jsx("svg", { xmlns: "http://www.w3.org/2000/svg", width: "24", height: "24", viewBox: "0 0 24 24", style: {
66
+ display: 'block',
67
+ transition: 'all 0.2s ease',
68
+ }, children: _jsx("path", { fill: isDarkBackground ? '#ffffff' : '#000000', stroke: isDarkBackground ? '#000000' : '#ffffff', strokeWidth: "2", d: "M5.5 3.21V20.8c0 .45.54.67.85.35l4.86-4.86a.5.5 0 0 1 .35-.15h6.87a.5.5 0 0 0 .35-.85L6.35 2.85a.5.5 0 0 0-.85.35Z" }) }) }) }));
69
+ };
70
+ export default CustomCursor;
@@ -0,0 +1,2 @@
1
+ declare const Navbar: () => import("react/jsx-runtime").JSX.Element | null;
2
+ export default Navbar;
@@ -0,0 +1,35 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { useState, useEffect } from 'react';
4
+ import Link from 'next/link';
5
+ import { motion, AnimatePresence } from 'framer-motion';
6
+ import { Sun, Moon } from 'lucide-react';
7
+ const Navbar = () => {
8
+ const [mounted, setMounted] = useState(false);
9
+ const [isDark, setIsDark] = useState(true);
10
+ const [scrolled, setScrolled] = useState(false);
11
+ useEffect(() => {
12
+ setMounted(true);
13
+ const handleScroll = () => setScrolled(window.scrollY > 20);
14
+ window.addEventListener('scroll', handleScroll);
15
+ return () => window.removeEventListener('scroll', handleScroll);
16
+ }, []);
17
+ const toggleTheme = () => {
18
+ setIsDark(!isDark);
19
+ document.documentElement.classList.toggle('dark');
20
+ };
21
+ const navLinks = [
22
+ { name: 'Archives', slug: 'work' },
23
+ { name: 'Capabilities', slug: 'service' },
24
+ { name: 'Ethos', slug: 'about' },
25
+ ];
26
+ if (!mounted)
27
+ return null;
28
+ return (_jsx("nav", { className: 'fixed top-0 left-0 w-full z-[100] flex justify-center pt-6 px-6 pointer-events-none mt-10', children: _jsxs(motion.div, { initial: { y: -100 }, animate: { y: 0 }, className: `
29
+ flex items-center justify-between px-6 transition-all duration-500 pointer-events-auto
30
+ ${scrolled
31
+ ? 'w-full md:w-[800px] h-14 bg-white/5 backdrop-blur-md border border-white/10 rounded-full shadow-2xl'
32
+ : 'w-full h-20 bg-transparent border-transparent'}
33
+ `, children: [_jsx(Link, { href: '/', className: 'group flex items-center', children: _jsx("span", { className: ' text-3xl uppercase tracking-wide font-bold', children: "Renoh" }) }), _jsx("div", { className: 'hidden md:flex items-center space-x-8', children: navLinks.map((link) => (_jsxs(Link, { href: `/${link.slug}`, className: 'text-[10px] font-plex uppercase tracking-[0.2em] text-zinc-400 hover:text-white transition-colors relative group', children: [link.name, _jsx("span", { className: 'absolute -bottom-1 left-0 w-0 h-px bg-white transition-all duration-500 group-hover:w-full' })] }, link.slug))) }), _jsxs("div", { className: 'flex items-center space-x-4', children: [_jsx("button", { onClick: toggleTheme, className: 'p-2 hover:bg-white/10 rounded-full transition-colors text-white', children: _jsx(AnimatePresence, { mode: 'wait', children: _jsx(motion.div, { initial: { opacity: 0, rotate: -90 }, animate: { opacity: 1, rotate: 0 }, exit: { opacity: 0, rotate: 90 }, transition: { duration: 0.2 }, children: isDark ? _jsx(Sun, { size: 16 }) : _jsx(Moon, { size: 16 }) }, isDark ? 'dark' : 'light') }) }), _jsx(Link, { href: '/contact', children: _jsx(motion.div, { whileHover: { scale: 1.05 }, whileTap: { scale: 0.95 }, className: 'px-4 py-2 bg-white text-black text-[10px] font-plex uppercase tracking-widest rounded-full', children: "Inquire" }) })] })] }) }));
34
+ };
35
+ export default Navbar;
@@ -0,0 +1,2 @@
1
+ declare const VisualScrollbar: () => import("react/jsx-runtime").JSX.Element | null;
2
+ export default VisualScrollbar;
@@ -0,0 +1,171 @@
1
+ 'use client';
2
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { useState, useEffect, useRef, useCallback } from 'react';
4
+ import { usePathname } from 'next/navigation';
5
+ const VisualScrollbar = () => {
6
+ const [thumbTop, setThumbTop] = useState(0);
7
+ const channelRef = useRef(null);
8
+ const thumbRef = useRef(null);
9
+ const tickingRef = useRef(false);
10
+ const measurementsRef = useRef({
11
+ height: 0,
12
+ thumbH: 0,
13
+ paddingTop: 0,
14
+ paddingBottom: 0,
15
+ available: 0,
16
+ });
17
+ // Hide scrollbar on contact page
18
+ const pathname = usePathname();
19
+ const shouldHide = pathname === '/contact';
20
+ const measure = useCallback(() => {
21
+ if (!channelRef.current || !thumbRef.current)
22
+ return;
23
+ const chRect = channelRef.current.getBoundingClientRect();
24
+ const thumbRect = thumbRef.current.getBoundingClientRect();
25
+ const style = window.getComputedStyle(channelRef.current);
26
+ const height = chRect.height;
27
+ const thumbH = thumbRect.height;
28
+ const paddingTop = parseFloat(style.paddingTop) || 0;
29
+ const paddingBottom = parseFloat(style.paddingBottom) || 0;
30
+ const available = Math.max(height - thumbH - (paddingTop + paddingBottom), 0);
31
+ measurementsRef.current = {
32
+ height,
33
+ thumbH,
34
+ paddingTop,
35
+ paddingBottom,
36
+ available,
37
+ };
38
+ }, []);
39
+ const updateThumb = useCallback(() => {
40
+ const scrollTop = window.scrollY;
41
+ const scrollHeight = document.documentElement.scrollHeight;
42
+ const clientHeight = document.documentElement.clientHeight;
43
+ const maxScroll = Math.max(scrollHeight - clientHeight, 1);
44
+ const scrollPercent = Math.min(Math.max(scrollTop / maxScroll, 0), 1);
45
+ const topPx = measurementsRef.current.paddingTop +
46
+ scrollPercent * measurementsRef.current.available;
47
+ setThumbTop(topPx);
48
+ }, []);
49
+ useEffect(() => {
50
+ const handleScroll = () => {
51
+ if (!tickingRef.current) {
52
+ tickingRef.current = true;
53
+ requestAnimationFrame(() => {
54
+ updateThumb();
55
+ tickingRef.current = false;
56
+ });
57
+ }
58
+ };
59
+ let resizeTimeout = null;
60
+ const handleResize = () => {
61
+ if (resizeTimeout)
62
+ clearTimeout(resizeTimeout);
63
+ resizeTimeout = setTimeout(() => {
64
+ measure();
65
+ updateThumb();
66
+ }, 150);
67
+ };
68
+ // Initial setup with small delay to ensure DOM is ready
69
+ const initTimeout = setTimeout(() => {
70
+ measure();
71
+ updateThumb();
72
+ }, 100);
73
+ window.addEventListener('scroll', handleScroll, { passive: true });
74
+ window.addEventListener('resize', handleResize, { passive: true });
75
+ return () => {
76
+ clearTimeout(initTimeout);
77
+ if (resizeTimeout)
78
+ clearTimeout(resizeTimeout);
79
+ window.removeEventListener('scroll', handleScroll);
80
+ window.removeEventListener('resize', handleResize);
81
+ };
82
+ }, [measure, updateThumb]);
83
+ // Return null if on contact page
84
+ if (shouldHide)
85
+ return null;
86
+ return (_jsxs(_Fragment, { children: [_jsx("style", { children: `
87
+ :root {
88
+ --left-offset: 20px;
89
+ --track-h: 50vh;
90
+ --track-w: 3px;
91
+ --thumb-w: 2px;
92
+ --thumb-h: 50px;
93
+ --track-bg: #212121;
94
+ --channel-bg: rgba(255,255,255,0.15);
95
+ --thumb-gradient: linear-gradient(180deg,#ffffff,#d7d7d7);
96
+ --track-radius: 0px;
97
+ --thumb-radius: 0px;
98
+ --transition-speed: 1ms;
99
+ }
100
+
101
+ * {
102
+ box-sizing: border-box;
103
+ }
104
+
105
+ body {
106
+ background: #0a0a0a;
107
+ color: #e6e6e6;
108
+ margin: 0;
109
+ padding: 0;
110
+ }
111
+
112
+ .vs {
113
+ position: fixed;
114
+ left: var(--left-offset);
115
+ top: 50%;
116
+ transform: translateY(-50%);
117
+ width: var(--track-w);
118
+ height: var(--track-h);
119
+ background: var(--track-bg);
120
+ border-radius: var(--track-radius);
121
+ overflow: hidden;
122
+ box-shadow:
123
+ inset 0 1px 0 rgba(255,255,255,0.08),
124
+ 0 10px 24px rgba(0,0,0,0.6);
125
+ backdrop-filter: blur(6px);
126
+ pointer-events: none;
127
+ z-index: 9999;
128
+ display: flex;
129
+ justify-content: center;
130
+ will-change: transform;
131
+ }
132
+
133
+ .vs__channel {
134
+ position: relative;
135
+ width: 100%;
136
+ height: 100%;
137
+ background: var(--channel-bg);
138
+ border-radius: var(--track-radius);
139
+ overflow: hidden;
140
+ }
141
+
142
+ .vs__thumb {
143
+ position: absolute;
144
+ left: 50%;
145
+ transform: translateX(-50%) translateZ(0);
146
+ width: var(--thumb-w);
147
+ height: var(--thumb-h);
148
+ border-radius: var(--thumb-radius);
149
+ background: var(--thumb-gradient);
150
+ box-shadow:
151
+ 0 4px 14px rgba(0,0,0,0.6),
152
+ inset 0 1px 0 rgba(255,255,255,0.4);
153
+ will-change: top;
154
+ }
155
+
156
+ .vs:hover .vs__thumb {
157
+ transform: translateX(-50%) translateZ(0) scaleY(1.05);
158
+ transition: transform 180ms ease;
159
+ }
160
+
161
+ @media (max-width:720px) {
162
+ :root {
163
+ --left-offset: 10px;
164
+ --thumb-h: 30px;
165
+ --track-w: 3px;
166
+ --track-h: 30vh;
167
+ }
168
+ }
169
+ ` }), _jsx("div", { className: 'vs', children: _jsx("div", { className: 'vs__channel', ref: channelRef, children: _jsx("div", { className: 'vs__thumb', ref: thumbRef, style: { top: `${thumbTop}px` }, "aria-hidden": 'true' }) }) })] }));
170
+ };
171
+ export default VisualScrollbar;
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,47 @@
1
+ import { writeFileSync, existsSync, mkdirSync, readFileSync } from 'fs';
2
+ import path from 'path';
3
+ import { Index } from '../registry/index';
4
+ const OUTPUT_DIR = path.join(process.cwd(), '__registry__');
5
+ const OUTPUT_PATH = path.join(OUTPUT_DIR, 'index.ts');
6
+ async function buildRegistry() {
7
+ if (!existsSync(OUTPUT_DIR)) {
8
+ mkdirSync(OUTPUT_DIR);
9
+ }
10
+ let indexContent = `// @ts-nocheck
11
+ // This file is autogenerated by scripts/build-registry.ts
12
+ import * as React from "react"
13
+
14
+ export const Index: Record<string, any> = {
15
+ "default": {
16
+ `;
17
+ Index.forEach((item) => {
18
+ const componentPath = item.files[0].replace(/\.tsx?$/, '');
19
+ const sourceFilePath = path.join(process.cwd(), 'registry', item.files[0]);
20
+ const detailsFilePath = path.join(process.cwd(), 'registry', `details/${item.name}.tsx`);
21
+ const hasDetails = existsSync(detailsFilePath);
22
+ const rawContent = existsSync(sourceFilePath)
23
+ ? readFileSync(sourceFilePath, 'utf8')
24
+ : '';
25
+ const safeContent = rawContent.replace(/`/g, '\\`').replace(/\$/g, '\\$');
26
+ const safeDescription = (item.description || '')
27
+ .replace(/`/g, '\\`')
28
+ .replace(/\$/g, '\\$');
29
+ indexContent += ` "${item.name}": {
30
+ name: "${item.name}",
31
+ type: "${item.type}",
32
+ component: React.lazy(() => import("../registry/${componentPath}")),
33
+ details: ${hasDetails ? `React.lazy(() => import("../registry/details/${item.name}"))` : 'null'},
34
+ files: ${JSON.stringify(item.files)},
35
+ category: "${item.category || 'undefined'}",
36
+ content: \`${safeContent}\`,
37
+ description: \`${safeDescription}\`,
38
+ install: "${item.install || ''}",
39
+ },
40
+ `;
41
+ });
42
+ indexContent += ` }
43
+ };`;
44
+ writeFileSync(OUTPUT_PATH, indexContent);
45
+ console.log('✅ Slash UI Registry updated!');
46
+ }
47
+ buildRegistry();
@@ -0,0 +1 @@
1
+ export declare const addCommand: (componentName: string) => Promise<void>;
@@ -0,0 +1,64 @@
1
+ import fs from "fs-extra";
2
+ import path from "path";
3
+ import chalk from "chalk";
4
+ import prompts from "prompts";
5
+ import { execSync } from "child_process";
6
+ // Import the Index that YOUR script just generated
7
+ import { Index } from "../../__registry__/index.js";
8
+ /**
9
+ * Helper to check and install missing peer dependencies
10
+ */
11
+ async function installMissingDeps(dependenciesString) {
12
+ try {
13
+ const packageJsonPath = path.join(process.cwd(), "package.json");
14
+ if (!await fs.pathExists(packageJsonPath))
15
+ return;
16
+ const packageJson = await fs.readJSON(packageJsonPath);
17
+ const allDeps = Object.assign(Object.assign({}, packageJson.dependencies), packageJson.devDependencies);
18
+ // Split the space-separated string from your registry (e.g., "framer-motion lucide-react")
19
+ const requiredDeps = dependenciesString.split(" ");
20
+ const missing = requiredDeps.filter(dep => !allDeps[dep]);
21
+ if (missing.length > 0) {
22
+ console.log(chalk.yellow(`\nMissing dependencies: ${chalk.bold(missing.join(", "))}`));
23
+ const { confirm } = await prompts({
24
+ type: "confirm",
25
+ name: "confirm",
26
+ message: "Would you like to install them now?",
27
+ initial: true
28
+ });
29
+ if (confirm) {
30
+ console.log(chalk.cyan("Installing..."));
31
+ // 'stdio: inherit' lets the user see the npm progress bar
32
+ execSync(`npm install ${missing.join(" ")}`, { stdio: "inherit" });
33
+ console.log(chalk.green("Dependencies installed.\n"));
34
+ }
35
+ }
36
+ }
37
+ catch (error) {
38
+ console.error(chalk.red("Could not check/install dependencies:"), error);
39
+ }
40
+ }
41
+ export const addCommand = async (componentName) => {
42
+ try {
43
+ const component = Index.default[componentName];
44
+ if (!component) {
45
+ console.error(chalk.red(`\nError: Component "${componentName}" not found in registry.`));
46
+ return;
47
+ }
48
+ const targetDir = path.join(process.cwd(), "components", "ui");
49
+ await fs.ensureDir(targetDir);
50
+ const fileName = component.files[0] ? path.basename(component.files[0]) : `${componentName}.tsx`;
51
+ const filePath = path.join(targetDir, fileName);
52
+ await fs.writeFile(filePath, component.content);
53
+ // --- Premium Success Message ---
54
+ console.log(`\n${chalk.bgCyan.black(" DONE ")} ${chalk.green(`Component ${chalk.bold(componentName)} has been added.`)}`);
55
+ console.log(`${chalk.dim("Location:")} ${chalk.cyan(filePath)}`);
56
+ // --- Automated Dependency Check ---
57
+ if (component.install) {
58
+ await installMissingDeps(component.install);
59
+ }
60
+ }
61
+ catch (error) {
62
+ console.error(chalk.red("\nFailed to add component:"), error);
63
+ }
64
+ };
@@ -0,0 +1 @@
1
+ export declare const initCommand: () => Promise<void>;
@@ -0,0 +1,88 @@
1
+ import fs from 'fs-extra';
2
+ import path from 'path';
3
+ import chalk from 'chalk';
4
+ import prompts from 'prompts';
5
+ export const initCommand = async () => {
6
+ try {
7
+ console.log(chalk.bold.blue('\n🚀 Initializing Slash UI...\n'));
8
+ const response = await prompts([
9
+ {
10
+ type: 'text',
11
+ name: 'componentsPath',
12
+ message: 'Where would you like to install your components?',
13
+ initial: 'components/ui',
14
+ },
15
+ {
16
+ type: 'confirm',
17
+ name: 'proceed',
18
+ message: 'This will create directories and neubrutal configurations. Proceed?',
19
+ initial: true,
20
+ },
21
+ ]);
22
+ if (!response.proceed) {
23
+ console.log(chalk.yellow('Aborted initialization.'));
24
+ return;
25
+ }
26
+ // 1. Create Directories
27
+ const targetDir = path.join(process.cwd(), response.componentsPath);
28
+ const utilsDir = path.join(process.cwd(), "lib");
29
+ await fs.ensureDir(targetDir);
30
+ await fs.ensureDir(utilsDir);
31
+ console.log(chalk.green(`✔ Created directories.`));
32
+ // 2. Create lib/utils.ts (CRITICAL for your components)
33
+ const utilsContent = `import { type ClassValue, clsx } from "clsx";
34
+ import { twMerge } from "tailwind-merge";
35
+
36
+ export function cn(...inputs: ClassValue[]) {
37
+ return twMerge(clsx(inputs));
38
+ }
39
+ `;
40
+ await fs.writeFile(path.join(utilsDir, "utils.ts"), utilsContent);
41
+ console.log(chalk.green('✔ Created lib/utils.ts'));
42
+ // 3. Create components.json
43
+ const config = {
44
+ style: 'default',
45
+ tailwind: {
46
+ config: 'tailwind.config.ts',
47
+ css: 'app/globals.css',
48
+ baseColor: 'zinc',
49
+ },
50
+ paths: {
51
+ components: response.componentsPath,
52
+ utils: 'lib/utils',
53
+ },
54
+ };
55
+ await fs.writeJSON(path.join(process.cwd(), 'components.json'), config, { spaces: 2 });
56
+ console.log(chalk.green('✔ Created components.json'));
57
+ // 4. Advanced Tailwind Config Injection
58
+ const tailwindConfigPath = path.join(process.cwd(), 'tailwind.config.ts');
59
+ if (fs.existsSync(tailwindConfigPath)) {
60
+ let content = fs.readFileSync(tailwindConfigPath, 'utf-8');
61
+ // Add Neubrutal Theme extensions if they don't exist
62
+ if (!content.includes('boxShadow')) {
63
+ const neubrutalTheme = `
64
+ extend: {
65
+ boxShadow: {
66
+ 'neubrutal': '4px 4px 0px 0px rgba(0,0,0,1)',
67
+ 'neubrutal-lg': '8px 8px 0px 0px rgba(0,0,0,1)',
68
+ },
69
+ borderWidth: {
70
+ '3': '3px',
71
+ },
72
+ },`;
73
+ // This is a simple regex to inject into the theme object
74
+ content = content.replace(/theme: \{([\s\S]*?)(\s*)\}/, `theme: {$1 ${neubrutalTheme}$2}`);
75
+ }
76
+ // Add the content path
77
+ if (!content.includes(response.componentsPath)) {
78
+ content = content.replace(/content: \[([\s\S]*?)\]/, `content: [$1, "./${response.componentsPath}/**/*.{ts,tsx}"]`);
79
+ }
80
+ fs.writeFileSync(tailwindConfigPath, content);
81
+ console.log(chalk.dim('✔ Injected neubrutal theme into tailwind.config.ts'));
82
+ }
83
+ console.log(chalk.bgCyan.black("\n SUCCESS ") + " Project initialized. Ready to add components!");
84
+ }
85
+ catch (error) {
86
+ console.error(chalk.red('\nFailed to initialize project:'), error);
87
+ }
88
+ };
@@ -0,0 +1 @@
1
+ export declare const listCommand: () => Promise<void>;
@@ -0,0 +1,37 @@
1
+ import chalk from "chalk";
2
+ import prompts from "prompts";
3
+ import { Index } from "../../__registry__/index.js";
4
+ import { addCommand } from "./add.js";
5
+ export const listCommand = async () => {
6
+ try {
7
+ const registryData = Index.default;
8
+ const components = Object.keys(registryData);
9
+ if (components.length === 0) {
10
+ console.log(chalk.red("\n✖ No components found. Run 'npm run build:registry' first."));
11
+ return;
12
+ }
13
+ // 1. Create the interactive menu choices
14
+ const choices = components.map((name) => ({
15
+ title: name,
16
+ value: name,
17
+ description: chalk.dim(registryData[name].category || "ui"),
18
+ }));
19
+ console.log(chalk.bold.cyan("\n─── Slash UI Explorer ───"));
20
+ // 2. Launch the autocomplete search menu
21
+ const response = await prompts({
22
+ type: "autocomplete",
23
+ name: "selected",
24
+ message: "Search components",
25
+ choices: choices,
26
+ // This allows filtering as you type
27
+ suggest: (input, choices) => Promise.resolve(choices.filter(i => i.title.toLowerCase().includes(input.toLowerCase()))),
28
+ });
29
+ // 3. If they select one, run the add command immediately
30
+ if (response.selected) {
31
+ await addCommand(response.selected);
32
+ }
33
+ }
34
+ catch (error) {
35
+ console.error(chalk.red("\nFailed to display component list:"), error);
36
+ }
37
+ };
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
@@ -0,0 +1,28 @@
1
+ #!/usr/bin/env node
2
+ import { Command } from 'commander';
3
+ import { addCommand } from './commands/add.js';
4
+ import { initCommand } from './commands/init.js';
5
+ import { listCommand } from './commands/list.js';
6
+ const program = new Command();
7
+ program
8
+ .name('slash-ui')
9
+ .description('CLI for Slash UI components')
10
+ .version('1.0.0');
11
+ // 1. Init
12
+ program
13
+ .command('init')
14
+ .description('Initialize your project for Slash UI')
15
+ .action(initCommand);
16
+ // 2. List
17
+ program
18
+ .command('list')
19
+ .description('List all available components')
20
+ .action(listCommand);
21
+ // 3. Add
22
+ program
23
+ .command('add')
24
+ .description('Add a component to your project')
25
+ .argument('<component>', 'the component name to add')
26
+ .action(addCommand);
27
+ // ONLY ONE PARSE CALL AT THE VERY END
28
+ program.parse();
@@ -0,0 +1 @@
1
+ export declare function getPkgManager(): "yarn" | "pnpm" | "bun" | "npm";
@@ -0,0 +1,10 @@
1
+ export function getPkgManager() {
2
+ const userAgent = process.env.npm_config_user_agent || "";
3
+ if (userAgent.includes("yarn"))
4
+ return "yarn";
5
+ if (userAgent.includes("pnpm"))
6
+ return "pnpm";
7
+ if (userAgent.includes("bun"))
8
+ return "bun";
9
+ return "npm";
10
+ }