roadmap-kit 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.
Files changed (39) hide show
  1. package/INSTALL.md +358 -0
  2. package/LICENSE +21 -0
  3. package/README.md +503 -0
  4. package/cli.js +548 -0
  5. package/dashboard/dist/assets/index-BzYzLB7u.css +1 -0
  6. package/dashboard/dist/assets/index-DIonhzlK.js +506 -0
  7. package/dashboard/dist/index.html +18 -0
  8. package/dashboard/dist/roadmap.json +268 -0
  9. package/dashboard/index.html +17 -0
  10. package/dashboard/package-lock.json +4172 -0
  11. package/dashboard/package.json +37 -0
  12. package/dashboard/postcss.config.js +6 -0
  13. package/dashboard/public/roadmap.json +268 -0
  14. package/dashboard/server.js +1366 -0
  15. package/dashboard/src/App.jsx +6979 -0
  16. package/dashboard/src/components/CircularProgress.jsx +55 -0
  17. package/dashboard/src/components/ProgressBar.jsx +33 -0
  18. package/dashboard/src/components/ProjectSettings.jsx +420 -0
  19. package/dashboard/src/components/SharedResources.jsx +239 -0
  20. package/dashboard/src/components/TaskList.jsx +273 -0
  21. package/dashboard/src/components/TechnicalDebt.jsx +170 -0
  22. package/dashboard/src/components/ui/accordion.jsx +46 -0
  23. package/dashboard/src/components/ui/badge.jsx +38 -0
  24. package/dashboard/src/components/ui/card.jsx +60 -0
  25. package/dashboard/src/components/ui/progress.jsx +22 -0
  26. package/dashboard/src/components/ui/tabs.jsx +47 -0
  27. package/dashboard/src/index.css +440 -0
  28. package/dashboard/src/lib/utils.js +6 -0
  29. package/dashboard/src/main.jsx +10 -0
  30. package/dashboard/tailwind.config.js +142 -0
  31. package/dashboard/vite.config.js +18 -0
  32. package/docker/Dockerfile +35 -0
  33. package/docker/docker-compose.yml +30 -0
  34. package/docker/entrypoint.sh +31 -0
  35. package/package.json +68 -0
  36. package/scanner.js +351 -0
  37. package/setup.sh +354 -0
  38. package/templates/clinerules.template +130 -0
  39. package/templates/roadmap.template.json +30 -0
@@ -0,0 +1,46 @@
1
+ import * as React from "react"
2
+ import * as AccordionPrimitive from "@radix-ui/react-accordion"
3
+ import { ChevronDown } from "lucide-react"
4
+ import { cn } from "../../lib/utils"
5
+
6
+ const Accordion = AccordionPrimitive.Root
7
+
8
+ const AccordionItem = React.forwardRef(({ className, ...props }, ref) => (
9
+ <AccordionPrimitive.Item
10
+ ref={ref}
11
+ className={cn("border-b border-slate-700", className)}
12
+ {...props}
13
+ />
14
+ ))
15
+ AccordionItem.displayName = "AccordionItem"
16
+
17
+ const AccordionTrigger = React.forwardRef(({ className, children, ...props }, ref) => (
18
+ <AccordionPrimitive.Header className="flex">
19
+ <AccordionPrimitive.Trigger
20
+ ref={ref}
21
+ className={cn(
22
+ "flex flex-1 items-center justify-between py-4 font-medium transition-all hover:bg-slate-800/30 px-4 rounded-lg [&[data-state=open]>svg]:rotate-180",
23
+ className
24
+ )}
25
+ {...props}
26
+ >
27
+ {children}
28
+ <ChevronDown className="h-4 w-4 shrink-0 transition-transform duration-200 text-slate-400" />
29
+ </AccordionPrimitive.Trigger>
30
+ </AccordionPrimitive.Header>
31
+ ))
32
+ AccordionTrigger.displayName = AccordionPrimitive.Trigger.displayName
33
+
34
+ const AccordionContent = React.forwardRef(({ className, children, ...props }, ref) => (
35
+ <AccordionPrimitive.Content
36
+ ref={ref}
37
+ className="overflow-hidden text-sm transition-all data-[state=closed]:animate-accordion-up data-[state=open]:animate-accordion-down"
38
+ {...props}
39
+ >
40
+ <div className={cn("pb-4 pt-0 px-4", className)}>{children}</div>
41
+ </AccordionPrimitive.Content>
42
+ ))
43
+
44
+ AccordionContent.displayName = AccordionPrimitive.Content.displayName
45
+
46
+ export { Accordion, AccordionItem, AccordionTrigger, AccordionContent }
@@ -0,0 +1,38 @@
1
+ import * as React from "react"
2
+ import { cva } from "class-variance-authority"
3
+ import { cn } from "../../lib/utils"
4
+
5
+ const badgeVariants = cva(
6
+ "inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-medium font-mono transition-all",
7
+ {
8
+ variants: {
9
+ variant: {
10
+ default:
11
+ "border-cyan-500/30 bg-cyan-500/10 text-cyan-400",
12
+ secondary:
13
+ "border-slate-600 bg-slate-800/50 text-slate-300",
14
+ success:
15
+ "border-emerald-500/30 bg-emerald-500/10 text-emerald-400",
16
+ warning:
17
+ "border-amber-500/30 bg-amber-500/10 text-amber-400",
18
+ danger:
19
+ "border-rose-500/30 bg-rose-500/10 text-rose-400",
20
+ outline:
21
+ "border-slate-600 text-slate-400 bg-transparent",
22
+ info:
23
+ "border-blue-500/30 bg-blue-500/10 text-blue-400",
24
+ },
25
+ },
26
+ defaultVariants: {
27
+ variant: "default",
28
+ },
29
+ }
30
+ )
31
+
32
+ function Badge({ className, variant, ...props }) {
33
+ return (
34
+ <div className={cn(badgeVariants({ variant }), className)} {...props} />
35
+ )
36
+ }
37
+
38
+ export { Badge, badgeVariants }
@@ -0,0 +1,60 @@
1
+ import * as React from "react"
2
+ import { cn } from "../../lib/utils"
3
+
4
+ const Card = React.forwardRef(({ className, ...props }, ref) => (
5
+ <div
6
+ ref={ref}
7
+ className={cn(
8
+ "rounded-lg border border-slate-800 bg-slate-900/50 backdrop-blur text-slate-50 shadow-sm",
9
+ className
10
+ )}
11
+ {...props}
12
+ />
13
+ ))
14
+ Card.displayName = "Card"
15
+
16
+ const CardHeader = React.forwardRef(({ className, ...props }, ref) => (
17
+ <div
18
+ ref={ref}
19
+ className={cn("flex flex-col space-y-1.5 p-6", className)}
20
+ {...props}
21
+ />
22
+ ))
23
+ CardHeader.displayName = "CardHeader"
24
+
25
+ const CardTitle = React.forwardRef(({ className, ...props }, ref) => (
26
+ <h3
27
+ ref={ref}
28
+ className={cn(
29
+ "text-2xl font-semibold leading-none tracking-tight",
30
+ className
31
+ )}
32
+ {...props}
33
+ />
34
+ ))
35
+ CardTitle.displayName = "CardTitle"
36
+
37
+ const CardDescription = React.forwardRef(({ className, ...props }, ref) => (
38
+ <p
39
+ ref={ref}
40
+ className={cn("text-sm text-slate-400", className)}
41
+ {...props}
42
+ />
43
+ ))
44
+ CardDescription.displayName = "CardDescription"
45
+
46
+ const CardContent = React.forwardRef(({ className, ...props }, ref) => (
47
+ <div ref={ref} className={cn("p-6 pt-0", className)} {...props} />
48
+ ))
49
+ CardContent.displayName = "CardContent"
50
+
51
+ const CardFooter = React.forwardRef(({ className, ...props }, ref) => (
52
+ <div
53
+ ref={ref}
54
+ className={cn("flex items-center p-6 pt-0", className)}
55
+ {...props}
56
+ />
57
+ ))
58
+ CardFooter.displayName = "CardFooter"
59
+
60
+ export { Card, CardHeader, CardFooter, CardTitle, CardDescription, CardContent }
@@ -0,0 +1,22 @@
1
+ import * as React from "react"
2
+ import * as ProgressPrimitive from "@radix-ui/react-progress"
3
+ import { cn } from "../../lib/utils"
4
+
5
+ const Progress = React.forwardRef(({ className, value, ...props }, ref) => (
6
+ <ProgressPrimitive.Root
7
+ ref={ref}
8
+ className={cn(
9
+ "relative h-4 w-full overflow-hidden rounded-full bg-slate-800",
10
+ className
11
+ )}
12
+ {...props}
13
+ >
14
+ <ProgressPrimitive.Indicator
15
+ className="h-full w-full flex-1 bg-gradient-to-r from-emerald-500 to-emerald-400 transition-all"
16
+ style={{ transform: `translateX(-${100 - (value || 0)}%)` }}
17
+ />
18
+ </ProgressPrimitive.Root>
19
+ ))
20
+ Progress.displayName = ProgressPrimitive.Root.displayName
21
+
22
+ export { Progress }
@@ -0,0 +1,47 @@
1
+ import * as React from "react"
2
+ import * as TabsPrimitive from "@radix-ui/react-tabs"
3
+ import { cn } from "../../lib/utils"
4
+
5
+ const Tabs = TabsPrimitive.Root
6
+
7
+ const TabsList = React.forwardRef(({ className, ...props }, ref) => (
8
+ <TabsPrimitive.List
9
+ ref={ref}
10
+ className={cn(
11
+ "inline-flex h-auto items-center justify-center rounded-xl bg-navy-900/80 backdrop-blur-sm p-1.5 text-slate-400 border border-cyan-500/10",
12
+ className
13
+ )}
14
+ {...props}
15
+ />
16
+ ))
17
+ TabsList.displayName = TabsPrimitive.List.displayName
18
+
19
+ const TabsTrigger = React.forwardRef(({ className, ...props }, ref) => (
20
+ <TabsPrimitive.Trigger
21
+ ref={ref}
22
+ className={cn(
23
+ "inline-flex items-center justify-center whitespace-nowrap rounded-lg px-4 py-2 text-sm font-medium transition-all",
24
+ "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-cyan-500/50",
25
+ "disabled:pointer-events-none disabled:opacity-50",
26
+ "data-[state=active]:bg-cyan-500/20 data-[state=active]:text-cyan-400 data-[state=active]:shadow-sm",
27
+ "hover:text-slate-300 hover:bg-navy-800/50",
28
+ className
29
+ )}
30
+ {...props}
31
+ />
32
+ ))
33
+ TabsTrigger.displayName = TabsPrimitive.Trigger.displayName
34
+
35
+ const TabsContent = React.forwardRef(({ className, ...props }, ref) => (
36
+ <TabsPrimitive.Content
37
+ ref={ref}
38
+ className={cn(
39
+ "mt-4 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-cyan-500/50",
40
+ className
41
+ )}
42
+ {...props}
43
+ />
44
+ ))
45
+ TabsContent.displayName = TabsPrimitive.Content.displayName
46
+
47
+ export { Tabs, TabsList, TabsTrigger, TabsContent }
@@ -0,0 +1,440 @@
1
+ @tailwind base;
2
+ @tailwind components;
3
+ @tailwind utilities;
4
+
5
+ @layer base {
6
+ :root {
7
+ --matrix: #00ff88;
8
+ --signal: #ff9500;
9
+ --cyber: #00d4ff;
10
+ --alert: #ff3366;
11
+ }
12
+
13
+ ::selection {
14
+ background-color: rgba(0, 255, 136, 0.3);
15
+ color: white;
16
+ }
17
+
18
+ /* Custom scrollbar */
19
+ ::-webkit-scrollbar {
20
+ width: 6px;
21
+ height: 6px;
22
+ }
23
+
24
+ ::-webkit-scrollbar-track {
25
+ background: transparent;
26
+ }
27
+
28
+ ::-webkit-scrollbar-thumb {
29
+ background: rgba(0, 255, 136, 0.2);
30
+ border-radius: 3px;
31
+ }
32
+
33
+ ::-webkit-scrollbar-thumb:hover {
34
+ background: rgba(0, 255, 136, 0.4);
35
+ }
36
+
37
+ /* Select styling */
38
+ select {
39
+ background-color: #0a0a0a;
40
+ color: #00ff88;
41
+ border: 1px solid rgba(0, 255, 136, 0.2);
42
+ }
43
+
44
+ select option {
45
+ background-color: #0a0a0a;
46
+ color: #e0e0e0;
47
+ padding: 12px;
48
+ }
49
+
50
+ select option:hover,
51
+ select option:focus,
52
+ select option:checked {
53
+ background-color: rgba(0, 255, 136, 0.1);
54
+ color: #00ff88;
55
+ }
56
+
57
+ /* Focus styles */
58
+ input:focus,
59
+ textarea:focus,
60
+ select:focus {
61
+ outline: none;
62
+ border-color: var(--matrix);
63
+ box-shadow: 0 0 0 1px var(--matrix), 0 0 20px rgba(0, 255, 136, 0.2);
64
+ }
65
+ }
66
+
67
+ @layer components {
68
+ /* Terminal card */
69
+ .terminal-card {
70
+ background: linear-gradient(180deg, #0d0d0d 0%, #0a0a0a 100%);
71
+ border: 1px solid rgba(0, 255, 136, 0.15);
72
+ position: relative;
73
+ overflow: hidden;
74
+ }
75
+
76
+ .terminal-card::before {
77
+ content: '';
78
+ position: absolute;
79
+ top: 0;
80
+ left: 0;
81
+ right: 0;
82
+ height: 1px;
83
+ background: linear-gradient(90deg, transparent, var(--matrix), transparent);
84
+ opacity: 0.5;
85
+ }
86
+
87
+ /* Sidebar style */
88
+ .sidebar-terminal {
89
+ background: linear-gradient(180deg, #050505 0%, #0a0a0a 50%, #050505 100%);
90
+ border-right: 1px solid rgba(0, 255, 136, 0.1);
91
+ position: relative;
92
+ }
93
+
94
+ .sidebar-terminal::after {
95
+ content: '';
96
+ position: absolute;
97
+ top: 0;
98
+ right: 0;
99
+ bottom: 0;
100
+ width: 1px;
101
+ background: linear-gradient(180deg, transparent, var(--matrix), transparent);
102
+ opacity: 0.3;
103
+ }
104
+
105
+ /* Glowing border */
106
+ .glow-border {
107
+ position: relative;
108
+ }
109
+
110
+ .glow-border::before {
111
+ content: '';
112
+ position: absolute;
113
+ inset: -1px;
114
+ border-radius: inherit;
115
+ padding: 1px;
116
+ background: linear-gradient(135deg, var(--matrix), var(--cyber));
117
+ -webkit-mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0);
118
+ mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0);
119
+ -webkit-mask-composite: xor;
120
+ mask-composite: exclude;
121
+ opacity: 0.5;
122
+ }
123
+
124
+ /* LED indicator */
125
+ .led {
126
+ width: 8px;
127
+ height: 8px;
128
+ border-radius: 50%;
129
+ position: relative;
130
+ }
131
+
132
+ .led::after {
133
+ content: '';
134
+ position: absolute;
135
+ inset: -3px;
136
+ border-radius: 50%;
137
+ background: inherit;
138
+ filter: blur(4px);
139
+ opacity: 0.6;
140
+ }
141
+
142
+ .led-green {
143
+ background: var(--matrix);
144
+ }
145
+
146
+ .led-amber {
147
+ background: var(--signal);
148
+ }
149
+
150
+ .led-red {
151
+ background: var(--alert);
152
+ }
153
+
154
+ .led-gray {
155
+ background: #444;
156
+ }
157
+
158
+ .led-gray::after {
159
+ opacity: 0;
160
+ }
161
+
162
+ /* Progress bar brutalist */
163
+ .progress-brutal {
164
+ height: 6px;
165
+ background: #1a1a1a;
166
+ border: 1px solid #333;
167
+ position: relative;
168
+ }
169
+
170
+ .progress-brutal-fill {
171
+ height: 100%;
172
+ background: linear-gradient(90deg, var(--matrix), var(--cyber));
173
+ box-shadow: 0 0 10px var(--matrix);
174
+ transition: width 0.5s ease-out;
175
+ }
176
+
177
+ /* Nav item terminal style */
178
+ .nav-terminal {
179
+ position: relative;
180
+ font-family: 'IBM Plex Mono', monospace;
181
+ letter-spacing: 0.05em;
182
+ transition: all 0.2s ease;
183
+ }
184
+
185
+ .nav-terminal::before {
186
+ content: '>';
187
+ position: absolute;
188
+ left: 12px;
189
+ opacity: 0;
190
+ color: var(--matrix);
191
+ transition: all 0.2s ease;
192
+ transform: translateX(-5px);
193
+ }
194
+
195
+ .nav-terminal:hover::before,
196
+ .nav-terminal.active::before {
197
+ opacity: 1;
198
+ transform: translateX(0);
199
+ }
200
+
201
+ .nav-terminal.active {
202
+ background: rgba(0, 255, 136, 0.05);
203
+ color: var(--matrix);
204
+ border-left: 2px solid var(--matrix);
205
+ }
206
+
207
+ /* ASCII box decorations */
208
+ .ascii-box {
209
+ position: relative;
210
+ }
211
+
212
+ .ascii-box::before {
213
+ content: '┌──';
214
+ position: absolute;
215
+ top: -8px;
216
+ left: 0;
217
+ font-family: 'IBM Plex Mono', monospace;
218
+ font-size: 10px;
219
+ color: rgba(0, 255, 136, 0.3);
220
+ }
221
+
222
+ .ascii-box::after {
223
+ content: '──┘';
224
+ position: absolute;
225
+ bottom: -8px;
226
+ right: 0;
227
+ font-family: 'IBM Plex Mono', monospace;
228
+ font-size: 10px;
229
+ color: rgba(0, 255, 136, 0.3);
230
+ }
231
+
232
+ /* Scanline overlay */
233
+ .scanlines {
234
+ pointer-events: none;
235
+ position: fixed;
236
+ inset: 0;
237
+ background: repeating-linear-gradient(
238
+ 0deg,
239
+ transparent,
240
+ transparent 2px,
241
+ rgba(0, 0, 0, 0.05) 2px,
242
+ rgba(0, 0, 0, 0.05) 4px
243
+ );
244
+ z-index: 9999;
245
+ }
246
+
247
+ /* CRT glow effect */
248
+ .crt-glow {
249
+ text-shadow: 0 0 5px currentColor, 0 0 10px currentColor;
250
+ }
251
+
252
+ /* Button terminal */
253
+ .btn-terminal {
254
+ font-family: 'IBM Plex Mono', monospace;
255
+ font-size: 12px;
256
+ letter-spacing: 0.1em;
257
+ text-transform: uppercase;
258
+ background: transparent;
259
+ border: 1px solid var(--matrix);
260
+ color: var(--matrix);
261
+ padding: 10px 20px;
262
+ position: relative;
263
+ overflow: hidden;
264
+ transition: all 0.3s ease;
265
+ }
266
+
267
+ .btn-terminal::before {
268
+ content: '';
269
+ position: absolute;
270
+ inset: 0;
271
+ background: var(--matrix);
272
+ transform: translateX(-100%);
273
+ transition: transform 0.3s ease;
274
+ z-index: -1;
275
+ }
276
+
277
+ .btn-terminal:hover {
278
+ color: black;
279
+ box-shadow: 0 0 20px rgba(0, 255, 136, 0.4);
280
+ }
281
+
282
+ .btn-terminal:hover::before {
283
+ transform: translateX(0);
284
+ }
285
+
286
+ /* Status badge */
287
+ .status-badge {
288
+ font-family: 'IBM Plex Mono', monospace;
289
+ font-size: 10px;
290
+ letter-spacing: 0.1em;
291
+ text-transform: uppercase;
292
+ padding: 4px 8px;
293
+ border: 1px solid currentColor;
294
+ }
295
+
296
+ /* Grid background */
297
+ .grid-bg {
298
+ background-image:
299
+ linear-gradient(rgba(0, 255, 136, 0.02) 1px, transparent 1px),
300
+ linear-gradient(90deg, rgba(0, 255, 136, 0.02) 1px, transparent 1px);
301
+ background-size: 40px 40px;
302
+ }
303
+
304
+ /* Typing cursor */
305
+ .typing-cursor::after {
306
+ content: '█';
307
+ animation: blink 1s step-end infinite;
308
+ color: var(--matrix);
309
+ }
310
+
311
+ /* Metric display */
312
+ .metric-display {
313
+ font-family: 'Orbitron', monospace;
314
+ font-variant-numeric: tabular-nums;
315
+ }
316
+
317
+ /* Feature card */
318
+ .feature-card {
319
+ background: #0d0d0d;
320
+ border: 1px solid #00ff88;
321
+ transition: all 0.3s ease;
322
+ }
323
+
324
+ .feature-card:hover {
325
+ border-color: #00ff88;
326
+ box-shadow: 0 0 30px rgba(0, 255, 136, 0.2);
327
+ }
328
+
329
+ /* Task row */
330
+ .task-row {
331
+ background: #0d0d0d;
332
+ border: 1px solid #1a1a1a;
333
+ transition: all 0.2s ease;
334
+ }
335
+
336
+ .task-row:hover {
337
+ background: #111;
338
+ border-color: #333;
339
+ }
340
+
341
+ /* Input terminal style */
342
+ .input-terminal {
343
+ background: #0a0a0a;
344
+ border: 1px solid #333;
345
+ color: #e0e0e0;
346
+ font-family: 'IBM Plex Mono', monospace;
347
+ transition: all 0.2s ease;
348
+ }
349
+
350
+ .input-terminal::placeholder {
351
+ color: #555;
352
+ }
353
+
354
+ .input-terminal:focus {
355
+ border-color: var(--matrix);
356
+ box-shadow: 0 0 0 1px var(--matrix), 0 0 20px rgba(0, 255, 136, 0.1);
357
+ }
358
+
359
+ /* Modal overlay */
360
+ .modal-overlay {
361
+ background: rgba(0, 0, 0, 0.9);
362
+ backdrop-filter: blur(4px);
363
+ }
364
+
365
+ /* Animated gradient text */
366
+ .text-matrix {
367
+ color: var(--matrix);
368
+ }
369
+
370
+ .text-signal {
371
+ color: var(--signal);
372
+ }
373
+
374
+ .text-cyber {
375
+ color: var(--cyber);
376
+ }
377
+
378
+ .text-alert {
379
+ color: var(--alert);
380
+ }
381
+ }
382
+
383
+ @layer utilities {
384
+ /* Stagger animations - elements always visible, animation is just enhancement */
385
+ .stagger > * {
386
+ opacity: 1 !important;
387
+ animation: slide-up 0.4s ease-out forwards;
388
+ }
389
+ .stagger > *:nth-child(1) { animation-delay: 0ms; }
390
+ .stagger > *:nth-child(2) { animation-delay: 50ms; }
391
+ .stagger > *:nth-child(3) { animation-delay: 100ms; }
392
+ .stagger > *:nth-child(4) { animation-delay: 150ms; }
393
+ .stagger > *:nth-child(5) { animation-delay: 200ms; }
394
+ .stagger > *:nth-child(6) { animation-delay: 250ms; }
395
+ .stagger > *:nth-child(7) { animation-delay: 300ms; }
396
+ .stagger > *:nth-child(8) { animation-delay: 350ms; }
397
+
398
+ /* Glow utilities */
399
+ .glow-matrix {
400
+ box-shadow: 0 0 20px rgba(0, 255, 136, 0.3);
401
+ }
402
+
403
+ .glow-signal {
404
+ box-shadow: 0 0 20px rgba(255, 149, 0, 0.3);
405
+ }
406
+
407
+ .glow-cyber {
408
+ box-shadow: 0 0 20px rgba(0, 212, 255, 0.3);
409
+ }
410
+
411
+ .glow-alert {
412
+ box-shadow: 0 0 20px rgba(255, 51, 102, 0.3);
413
+ }
414
+ }
415
+
416
+ /* Keyframes */
417
+ @keyframes blink {
418
+ 0%, 100% { opacity: 1; }
419
+ 50% { opacity: 0; }
420
+ }
421
+
422
+ @keyframes slide-up {
423
+ 0% {
424
+ opacity: 0;
425
+ transform: translateY(20px);
426
+ }
427
+ 100% {
428
+ opacity: 1;
429
+ transform: translateY(0);
430
+ }
431
+ }
432
+
433
+ @keyframes fade-in {
434
+ from { opacity: 0; }
435
+ to { opacity: 1; }
436
+ }
437
+
438
+ .animate-fade-in {
439
+ animation: fade-in 0.3s ease-out forwards;
440
+ }
@@ -0,0 +1,6 @@
1
+ import { clsx } from "clsx"
2
+ import { twMerge } from "tailwind-merge"
3
+
4
+ export function cn(...inputs) {
5
+ return twMerge(clsx(inputs))
6
+ }
@@ -0,0 +1,10 @@
1
+ import React from 'react'
2
+ import ReactDOM from 'react-dom/client'
3
+ import App from './App.jsx'
4
+ import './index.css'
5
+
6
+ ReactDOM.createRoot(document.getElementById('root')).render(
7
+ <React.StrictMode>
8
+ <App />
9
+ </React.StrictMode>,
10
+ )