@ghatak/slash-ui 1.0.0
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.
- package/README.md +36 -0
- package/__registry__/index.ts +493 -0
- package/app/(auth)/layout.tsx +18 -0
- package/app/(auth)/login/page.tsx +152 -0
- package/app/(protected)/component/[id]/page.tsx +48 -0
- package/app/(protected)/component/page.tsx +151 -0
- package/app/(protected)/docs/page.tsx +222 -0
- package/app/account/page.tsx +109 -0
- package/app/api/me/route.ts +24 -0
- package/app/globals.css +68 -0
- package/app/icon.png +0 -0
- package/app/layout.tsx +43 -0
- package/app/page.tsx +22 -0
- package/app/pricing/page.tsx +12 -0
- package/bin/intex.ts +19 -0
- package/components/smooth-scroll.tsx +26 -0
- package/components/toast.tsx +101 -0
- package/components/ui/IndustryProof.tsx +159 -0
- package/components/ui/ShowcaseContainer.tsx +497 -0
- package/components/ui/dot-cursor.tsx +108 -0
- package/components/ui/featuredComponents.tsx +126 -0
- package/components/ui/footer.tsx +59 -0
- package/components/ui/hero.tsx +85 -0
- package/components/ui/navbar.tsx +337 -0
- package/components/ui/pricing.tsx +163 -0
- package/eslint.config.mjs +18 -0
- package/hooks/use-component-search.tsx +52 -0
- package/lib/actions/auth.action.ts +88 -0
- package/lib/auth.ts +18 -0
- package/lib/email.ts +46 -0
- package/lib/prisma.ts +14 -0
- package/lib/registry.ts +17 -0
- package/lib/utils.ts +6 -0
- package/middleware/middleware.ts +21 -0
- package/next.config.ts +7 -0
- package/package.json +61 -0
- package/postcss.config.mjs +7 -0
- package/prisma/migrations/20260303172729_init/migration.sql +21 -0
- package/prisma/migrations/migration_lock.toml +3 -0
- package/prisma/schema.prisma +22 -0
- package/prisma.config.ts +14 -0
- package/public/compVideos/neubrutal-button.mp4 +0 -0
- package/public/fonts/BeVietnamPro-ExtraBold.otf +0 -0
- package/public/fonts/CartographCF-Regular.ttf +0 -0
- package/public/fonts/Hoshiko-Satsuki.ttf +0 -0
- package/public/fonts/Switzer-Regular.otf +0 -0
- package/public/images/PricingSlash.svg +58 -0
- package/public/images/slash_1.svg +59 -0
- package/public/images/slash_2.svg +18 -0
- package/public/video/hero_video.mp4 +0 -0
- package/registry/details/buttons/neubrutal-button-details.tsx +146 -0
- package/registry/details/cursor/dot-cursor-details.tsx +11 -0
- package/registry/details/navbar/floating-navbar-details.tsx +11 -0
- package/registry/details/scrollbars/minimal-scrollbar-details.tsx +0 -0
- package/registry/index.ts +35 -0
- package/registry/ui/buttons/neubrutal-button.tsx +33 -0
- package/registry/ui/cursors/dot-cursor.tsx +108 -0
- package/registry/ui/navbars/floating-navbar.tsx +99 -0
- package/registry/ui/scrollbars/minimal-scrollbar.tsx +203 -0
- package/scripts/build-registry.ts +60 -0
- package/src/commands/add.ts +40 -0
- package/src/commands/init.ts +75 -0
- package/src/commands/list.ts +44 -0
- package/src/index.ts +35 -0
- package/src/utils/get-pkg-manager.ts +7 -0
- package/tsconfig.json +34 -0
package/app/page.tsx
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import Navbar from '@/components/ui/navbar';
|
|
3
|
+
import Hero from '@/components/ui/hero';
|
|
4
|
+
import Footer from '@/components/ui/footer';
|
|
5
|
+
import FeaturedComponents from '@/components/ui/featuredComponents';
|
|
6
|
+
import Pricing from '@/components/ui/pricing';
|
|
7
|
+
import IndustryProof from '@/components/ui/IndustryProof';
|
|
8
|
+
|
|
9
|
+
const page = () => {
|
|
10
|
+
return (
|
|
11
|
+
<div>
|
|
12
|
+
<Navbar />
|
|
13
|
+
<Hero />
|
|
14
|
+
<FeaturedComponents />
|
|
15
|
+
<IndustryProof />
|
|
16
|
+
<Pricing />
|
|
17
|
+
<Footer />
|
|
18
|
+
</div>
|
|
19
|
+
);
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
export default page;
|
package/bin/intex.ts
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { Command } from 'commander';
|
|
4
|
+
import { addCommand } from '@/src/commands/add'
|
|
5
|
+
|
|
6
|
+
const program = new Command();
|
|
7
|
+
|
|
8
|
+
program
|
|
9
|
+
.name('slash-ui')
|
|
10
|
+
.description('CLI for Slash-UI component library')
|
|
11
|
+
.version('0.1.0');
|
|
12
|
+
|
|
13
|
+
program
|
|
14
|
+
.command('add')
|
|
15
|
+
.description('Add a component to your project')
|
|
16
|
+
.argument('<component>', 'the component to add')
|
|
17
|
+
.action(addCommand);
|
|
18
|
+
|
|
19
|
+
program.parse();
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import { ReactNode, useEffect } from 'react'
|
|
4
|
+
import Lenis from 'lenis'
|
|
5
|
+
|
|
6
|
+
export default function SmoothScroll({ children }: { children: ReactNode }) {
|
|
7
|
+
useEffect(() => {
|
|
8
|
+
const lenis = new Lenis({
|
|
9
|
+
duration: 1.2,
|
|
10
|
+
smoothWheel: true,
|
|
11
|
+
})
|
|
12
|
+
|
|
13
|
+
function raf(time: number) {
|
|
14
|
+
lenis.raf(time)
|
|
15
|
+
requestAnimationFrame(raf)
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
requestAnimationFrame(raf)
|
|
19
|
+
|
|
20
|
+
return () => {
|
|
21
|
+
lenis.destroy()
|
|
22
|
+
}
|
|
23
|
+
}, [])
|
|
24
|
+
|
|
25
|
+
return <>{children}</>
|
|
26
|
+
}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { useState, useEffect, useCallback } from "react";
|
|
4
|
+
|
|
5
|
+
interface ToastProps {
|
|
6
|
+
message: string;
|
|
7
|
+
duration?: number;
|
|
8
|
+
onClose?: () => void;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function Toast({ message, duration = 3000, onClose }: ToastProps) {
|
|
12
|
+
const [visible, setVisible] = useState(false);
|
|
13
|
+
|
|
14
|
+
useEffect(() => {
|
|
15
|
+
const enterTimer = setTimeout(() => setVisible(true), 10);
|
|
16
|
+
|
|
17
|
+
const exitTimer = setTimeout(() => {
|
|
18
|
+
setVisible(false);
|
|
19
|
+
setTimeout(() => onClose?.(), 300);
|
|
20
|
+
}, duration);
|
|
21
|
+
|
|
22
|
+
return () => {
|
|
23
|
+
clearTimeout(enterTimer);
|
|
24
|
+
clearTimeout(exitTimer);
|
|
25
|
+
};
|
|
26
|
+
}, [duration, onClose]);
|
|
27
|
+
|
|
28
|
+
return (
|
|
29
|
+
<div
|
|
30
|
+
className="fixed top-6 left-1/2 -translate-x-1/2 z-50 pointer-events-none"
|
|
31
|
+
role="status"
|
|
32
|
+
aria-live="polite"
|
|
33
|
+
>
|
|
34
|
+
<div
|
|
35
|
+
className={`
|
|
36
|
+
inline-flex items-center gap-2.5
|
|
37
|
+
bg-[#1c1c1e] text-[#f5f5f5]
|
|
38
|
+
px-5 py-3 rounded-full
|
|
39
|
+
text-sm font-medium tracking-wide
|
|
40
|
+
shadow-[0_2px_8px_rgba(0,0,0,0.35),0_0_0_1px_rgba(255,255,255,0.06)]
|
|
41
|
+
whitespace-nowrap
|
|
42
|
+
transition-all duration-300 ease-out
|
|
43
|
+
${visible ? "opacity-100 translate-y-0 scale-100" : "opacity-0 -translate-y-2 scale-95"}
|
|
44
|
+
`}
|
|
45
|
+
>
|
|
46
|
+
<span className="w-[18px] h-[18px] rounded-full bg-white flex items-center justify-center flex-shrink-0">
|
|
47
|
+
<svg width="10" height="10" viewBox="0 0 10 10" fill="none">
|
|
48
|
+
<path
|
|
49
|
+
d="M2 5.5L4 7.5L8 3"
|
|
50
|
+
stroke="white"
|
|
51
|
+
strokeWidth="1.5"
|
|
52
|
+
strokeLinecap="round"
|
|
53
|
+
strokeLinejoin="round"
|
|
54
|
+
/>
|
|
55
|
+
</svg>
|
|
56
|
+
</span>
|
|
57
|
+
{message}
|
|
58
|
+
</div>
|
|
59
|
+
</div>
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// ─── useToast hook ────────────────────────────────────────────────────────────
|
|
64
|
+
|
|
65
|
+
interface ToastState {
|
|
66
|
+
id: number;
|
|
67
|
+
message: string;
|
|
68
|
+
duration?: number;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export function useToast() {
|
|
72
|
+
const [toasts, setToasts] = useState<ToastState[]>([]);
|
|
73
|
+
|
|
74
|
+
const showToast = useCallback((message: string, duration = 3000) => {
|
|
75
|
+
const id = Date.now();
|
|
76
|
+
setToasts((prev) => [...prev, { id, message, duration }]);
|
|
77
|
+
}, []);
|
|
78
|
+
|
|
79
|
+
const removeToast = useCallback((id: number) => {
|
|
80
|
+
setToasts((prev) => prev.filter((t) => t.id !== id));
|
|
81
|
+
}, []);
|
|
82
|
+
|
|
83
|
+
const ToastContainer = useCallback(
|
|
84
|
+
() => (
|
|
85
|
+
<>
|
|
86
|
+
{toasts.map((t) => (
|
|
87
|
+
<Toast
|
|
88
|
+
key={t.id}
|
|
89
|
+
message={t.message}
|
|
90
|
+
duration={t.duration}
|
|
91
|
+
onClose={() => removeToast(t.id)}
|
|
92
|
+
/>
|
|
93
|
+
))}
|
|
94
|
+
</>
|
|
95
|
+
),
|
|
96
|
+
[toasts, removeToast]
|
|
97
|
+
);
|
|
98
|
+
|
|
99
|
+
return { showToast, ToastContainer };
|
|
100
|
+
}
|
|
101
|
+
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { ChevronRight } from 'lucide-react';
|
|
4
|
+
import { useRef, useState, useEffect } from 'react';
|
|
5
|
+
import Link from 'next/link';
|
|
6
|
+
|
|
7
|
+
interface TechItem {
|
|
8
|
+
name: string;
|
|
9
|
+
icon: string;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
type MarqueeItem = string | TechItem;
|
|
13
|
+
|
|
14
|
+
const inspirations: string[] = [
|
|
15
|
+
'Codegrid',
|
|
16
|
+
'Manu Arora',
|
|
17
|
+
'Shadcn',
|
|
18
|
+
'Skiper-Ui',
|
|
19
|
+
];
|
|
20
|
+
|
|
21
|
+
const techStack: TechItem[] = [
|
|
22
|
+
{ name: 'Tailwind CSS', icon: '' },
|
|
23
|
+
{ name: 'Next.js', icon: '' },
|
|
24
|
+
{ name: 'Motion.dev', icon: '' },
|
|
25
|
+
{ name: 'Framer Motion', icon: '' },
|
|
26
|
+
{ name: 'GSAP', icon: '' },
|
|
27
|
+
{ name: 'TypeScript', icon: '' },
|
|
28
|
+
{ name: 'React', icon: '' },
|
|
29
|
+
{ name: 'Vercel', icon: '' },
|
|
30
|
+
];
|
|
31
|
+
|
|
32
|
+
interface ScrollMarqueeProps {
|
|
33
|
+
items: MarqueeItem[];
|
|
34
|
+
speed?: number;
|
|
35
|
+
direction?: 1 | -1;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const ScrollMarquee = ({ items, direction = 1 }: ScrollMarqueeProps) => {
|
|
39
|
+
const trackRef = useRef<HTMLDivElement>(null);
|
|
40
|
+
const animRef = useRef<number>(0);
|
|
41
|
+
const posRef = useRef<number>(0);
|
|
42
|
+
|
|
43
|
+
useEffect(() => {
|
|
44
|
+
const track = trackRef.current;
|
|
45
|
+
if (!track) return;
|
|
46
|
+
|
|
47
|
+
const step = () => {
|
|
48
|
+
posRef.current -= direction * 0.5;
|
|
49
|
+
const halfWidth = track.scrollWidth / 2;
|
|
50
|
+
if (direction > 0 && Math.abs(posRef.current) >= halfWidth) {
|
|
51
|
+
posRef.current = 0;
|
|
52
|
+
} else if (direction < 0 && posRef.current >= 0) {
|
|
53
|
+
posRef.current = -halfWidth;
|
|
54
|
+
}
|
|
55
|
+
track.style.transform = `translateX(${posRef.current}px)`;
|
|
56
|
+
animRef.current = requestAnimationFrame(step);
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
animRef.current = requestAnimationFrame(step);
|
|
60
|
+
return () => cancelAnimationFrame(animRef.current);
|
|
61
|
+
}, [direction]);
|
|
62
|
+
|
|
63
|
+
return (
|
|
64
|
+
<div className='overflow-hidden w-full [mask-image:linear-gradient(to_right,transparent,black_10%,black_90%,transparent)]'>
|
|
65
|
+
<div
|
|
66
|
+
ref={trackRef}
|
|
67
|
+
className='flex gap-12 whitespace-nowrap will-change-transform'
|
|
68
|
+
style={{ width: 'max-content' }}
|
|
69
|
+
>
|
|
70
|
+
{[...items, ...items].map((item, i) => (
|
|
71
|
+
<span
|
|
72
|
+
key={i}
|
|
73
|
+
className='inline-flex items-center gap-3 text-base font-medium text-white/75 tracking-widest'
|
|
74
|
+
>
|
|
75
|
+
{typeof item === 'string' ? (
|
|
76
|
+
<>
|
|
77
|
+
<span className='text-white/75 text-[10px]'>◆</span>
|
|
78
|
+
{item}
|
|
79
|
+
</>
|
|
80
|
+
) : (
|
|
81
|
+
<>
|
|
82
|
+
<span className='text-white/75 text-[13px]'>{item.icon}</span>
|
|
83
|
+
{item.name}
|
|
84
|
+
</>
|
|
85
|
+
)}
|
|
86
|
+
</span>
|
|
87
|
+
))}
|
|
88
|
+
</div>
|
|
89
|
+
</div>
|
|
90
|
+
);
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
interface DashedDividerProps {
|
|
94
|
+
label: string;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
const DashedDivider = ({ label }: DashedDividerProps) => (
|
|
98
|
+
<div className='flex items-center gap-4 w-full my-10'>
|
|
99
|
+
|
|
100
|
+
<div className='flex-1 h-px bg-gradient-to-r from-transparent to-white/20' />
|
|
101
|
+
<span className='text-[9px] tracking-[0.2em] text-white/30 font-mono uppercase shrink-0'>
|
|
102
|
+
{label}
|
|
103
|
+
</span>
|
|
104
|
+
<div className='flex-1 h-px bg-gradient-to-l from-transparent to-white/20' />
|
|
105
|
+
</div>
|
|
106
|
+
);
|
|
107
|
+
|
|
108
|
+
export default function IndustryProof() {
|
|
109
|
+
const [visible, setVisible] = useState<boolean>(false);
|
|
110
|
+
|
|
111
|
+
useEffect(() => {
|
|
112
|
+
const t = setTimeout(() => setVisible(true), 100);
|
|
113
|
+
return () => clearTimeout(t);
|
|
114
|
+
}, []);
|
|
115
|
+
|
|
116
|
+
return (
|
|
117
|
+
<section className='relative min-h-screen flex flex-col items-center justify-center px-6 overflow-hidden '>
|
|
118
|
+
{/* 2. Bottom fade-out (Transitions to the next section) */}
|
|
119
|
+
{/* <div className='absolute bottom-0 left-0 right-0 h-74 bg-gradient-to-t from-white/[0.1] to-transparent pointer-events-none' /> */}
|
|
120
|
+
|
|
121
|
+
{/* --- CONTENT --- */}
|
|
122
|
+
<div
|
|
123
|
+
className={`relative z-10 w-full max-w-[860px] transition-all duration-[1000ms] ease-out ${
|
|
124
|
+
visible ? 'opacity-100 translate-y-0' : 'opacity-0 translate-y-4'
|
|
125
|
+
}`}
|
|
126
|
+
>
|
|
127
|
+
<DashedDivider label='Inspired & Legends' />
|
|
128
|
+
|
|
129
|
+
<div className='mb-4'>
|
|
130
|
+
<ScrollMarquee items={inspirations} direction={1} />
|
|
131
|
+
</div>
|
|
132
|
+
<div className='mb-12'>
|
|
133
|
+
<ScrollMarquee items={[...inspirations].reverse()} direction={-1} />
|
|
134
|
+
</div>
|
|
135
|
+
|
|
136
|
+
<DashedDivider label='Tools & Stack' />
|
|
137
|
+
|
|
138
|
+
<div className='mb-4'>
|
|
139
|
+
<ScrollMarquee items={techStack} direction={1} />
|
|
140
|
+
</div>
|
|
141
|
+
<div className='mb-12'>
|
|
142
|
+
<ScrollMarquee items={[...techStack].reverse()} direction={-1} />
|
|
143
|
+
</div>
|
|
144
|
+
|
|
145
|
+
<div className='mt-8 flex justify-center'>
|
|
146
|
+
<Link
|
|
147
|
+
href='/pricing'
|
|
148
|
+
className='relative z-30 h-12 px-6 rounded-lg text-sm text-white transition-all flex items-center gap-2 group'
|
|
149
|
+
>
|
|
150
|
+
Be a part now
|
|
151
|
+
<div className='flex items-center justify-center transition-transform group-hover:translate-x-1'>
|
|
152
|
+
<ChevronRight size={18} />
|
|
153
|
+
</div>
|
|
154
|
+
</Link>
|
|
155
|
+
</div>
|
|
156
|
+
</div>
|
|
157
|
+
</section>
|
|
158
|
+
);
|
|
159
|
+
}
|