mirror of
https://github.com/FranP-code/create-better-t-stack.git
synced 2025-10-12 23:52:15 +00:00
update theme
This commit is contained in:
21
apps/web/components.json
Normal file
21
apps/web/components.json
Normal file
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"$schema": "https://ui.shadcn.com/schema.json",
|
||||
"style": "new-york",
|
||||
"rsc": true,
|
||||
"tsx": true,
|
||||
"tailwind": {
|
||||
"config": "",
|
||||
"css": "src/app/global.css",
|
||||
"baseColor": "neutral",
|
||||
"cssVariables": true,
|
||||
"prefix": ""
|
||||
},
|
||||
"aliases": {
|
||||
"components": "@/components",
|
||||
"utils": "@/lib/utils",
|
||||
"ui": "@/components/ui",
|
||||
"lib": "@/lib",
|
||||
"hooks": "@/hooks"
|
||||
},
|
||||
"iconLibrary": "lucide"
|
||||
}
|
||||
@@ -11,18 +11,21 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@heroicons/react": "^2.2.0",
|
||||
"@radix-ui/react-scroll-area": "^1.2.5",
|
||||
"@xyflow/react": "^12.5.5",
|
||||
"babel-plugin-react-compiler": "^19.0.0-beta-e993439-20250405",
|
||||
"canvas-confetti": "^1.9.3",
|
||||
"class-variance-authority": "^0.7.1",
|
||||
"clsx": "^2.1.1",
|
||||
"fumadocs-core": "15.1.2",
|
||||
"fumadocs-mdx": "11.5.7",
|
||||
"fumadocs-ui": "15.1.2",
|
||||
"lucide-react": "^0.485.0",
|
||||
"lucide-react": "^0.501.0",
|
||||
"motion": "^12.7.4",
|
||||
"next": "15.2.3",
|
||||
"react": "^19.1.0",
|
||||
"react-dom": "^19.1.0",
|
||||
"react-tweet": "^3.2.2"
|
||||
"react-tweet": "^3.2.2",
|
||||
"tailwind-merge": "^3.2.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@tailwindcss/postcss": "^4.1.3",
|
||||
|
||||
3
apps/web/public/icon/orpc.svg
Normal file
3
apps/web/public/icon/orpc.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg width="100" height="100" xmlns="http://www.w3.org/2000/svg">
|
||||
<circle cx="50" cy="50" r="45" fill="black" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 122 B |
@@ -1,16 +0,0 @@
|
||||
import React from "react";
|
||||
|
||||
const BackgroundGradients = () => {
|
||||
return (
|
||||
<div className="-z-10 fixed inset-0 overflow-hidden">
|
||||
<div className="absolute inset-0 bg-[radial-gradient(circle_at_30%_20%,rgba(59,130,246,0.04)_0%,transparent_40%),radial-gradient(circle_at_70%_60%,rgba(79,70,229,0.03)_0%,transparent_40%)] dark:bg-[radial-gradient(circle_at_30%_20%,rgba(59,130,246,0.1)_0%,transparent_40%),radial-gradient(circle_at_70%_60%,rgba(79,70,229,0.08)_0%,transparent_40%)]" />
|
||||
<div className="absolute inset-0 opacity-20 dark:opacity-25">
|
||||
<div className="absolute top-1/4 left-1/3 h-28 w-28 rounded-full bg-blue-300/30 blur-[120px] dark:bg-blue-500/20" />
|
||||
<div className="absolute top-2/3 right-1/4 h-32 w-32 rounded-full bg-indigo-300/30 blur-[140px] dark:bg-indigo-500/20" />
|
||||
</div>
|
||||
<div className="absolute inset-0 bg-[linear-gradient(to_right,#e5e7eb_1px,transparent_1px),linear-gradient(to_bottom,#e5e7eb_1px,transparent_1px)] bg-[size:4rem_4rem] opacity-[0.15] [mask-image:radial-gradient(ellipse_60%_50%_at_50%_0%,#000_70%,transparent_100%)] dark:bg-[linear-gradient(to_right,#1f2937_1px,transparent_1px),linear-gradient(to_bottom,#1f2937_1px,transparent_1px)]" />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default BackgroundGradients;
|
||||
@@ -1,12 +0,0 @@
|
||||
import React from "react";
|
||||
|
||||
const CenterLines = () => {
|
||||
return (
|
||||
<>
|
||||
<div className="-translate-y-1/2 absolute top-3/4 left-0 h-14 w-80 rotate-180 transform rounded-bl-3xl border-slate-700 border-b-2 border-l-2 shadow-lg backdrop-blur-sm dark:border-slate-300" />
|
||||
<div className="-translate-y-1/2 absolute top-3/4 right-0 h-14 w-80 rotate-180 transform rounded-br-3xl border-slate-700 border-r-2 border-b-2 shadow-lg backdrop-blur-sm dark:border-slate-300" />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default CenterLines;
|
||||
@@ -1,4 +1,5 @@
|
||||
"use client";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { Check, ClipboardCopy, Terminal } from "lucide-react";
|
||||
import { motion } from "motion/react";
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
@@ -51,28 +52,26 @@ const CodeContainer = () => {
|
||||
|
||||
return (
|
||||
<div className="mx-auto mt-4 w-full max-w-3xl">
|
||||
<div className="overflow-hidden rounded-lg border border-gray-200 bg-white shadow-md dark:border-gray-800 dark:bg-gray-950">
|
||||
<div className="flex items-center justify-between bg-gray-100 px-3 py-2 dark:bg-gray-900">
|
||||
<div className="overflow-hidden rounded-lg border border-border bg-card shadow-md">
|
||||
<div className="flex items-center justify-between bg-muted px-3 py-2">
|
||||
<div className="flex gap-1.5">
|
||||
<div className="h-2.5 w-2.5 rounded-full bg-red-500" />
|
||||
<div className="h-2.5 w-2.5 rounded-full bg-yellow-500" />
|
||||
<div className="h-2.5 w-2.5 rounded-full bg-green-500" />
|
||||
</div>
|
||||
|
||||
<div className="text-gray-600 text-xs dark:text-gray-400">
|
||||
Terminal
|
||||
</div>
|
||||
<div className="text-muted-foreground text-xs">Terminal</div>
|
||||
|
||||
<div className="relative" ref={menuRef}>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setIsOpen(!isOpen)}
|
||||
className="flex items-center gap-1 rounded border border-gray-200 bg-white px-2 py-1 text-gray-700 text-xs hover:bg-gray-50 dark:border-gray-700 dark:bg-gray-800 dark:text-gray-300 dark:hover:bg-gray-700"
|
||||
className="flex items-center gap-1 rounded border border-border bg-card px-2 py-1 text-foreground text-xs hover:bg-muted"
|
||||
>
|
||||
<Terminal className="h-3 w-3 text-gray-600 dark:text-gray-400" />
|
||||
<Terminal className="h-3 w-3 text-muted-foreground" />
|
||||
<span>{selectedPM}</span>
|
||||
<svg
|
||||
className="h-3 w-3 text-gray-500"
|
||||
className="h-3 w-3 text-muted-foreground"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
viewBox="0 0 24 24"
|
||||
@@ -91,17 +90,16 @@ const CodeContainer = () => {
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: -5 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
className="absolute right-0 z-50 mt-1 w-28 rounded-md border border-gray-200 bg-white shadow-lg dark:border-gray-700 dark:bg-gray-800"
|
||||
className="absolute right-0 z-50 mt-1 w-28 rounded-md border border-border bg-card shadow-lg"
|
||||
>
|
||||
{(["npm", "pnpm", "bun"] as const).map((pm) => (
|
||||
<button
|
||||
type="button"
|
||||
key={pm}
|
||||
className={`block w-full px-3 py-1.5 text-left text-xs ${
|
||||
selectedPM === pm
|
||||
? "bg-gray-200 text-gray-800 dark:bg-gray-700 dark:text-gray-200"
|
||||
: "text-gray-700 hover:bg-gray-50 dark:text-gray-300 dark:hover:bg-gray-700/50"
|
||||
}`}
|
||||
className={cn(
|
||||
"block w-full px-3 py-1.5 text-left text-foreground text-xs",
|
||||
selectedPM === pm ? "bg-muted" : "hover:bg-muted/50",
|
||||
)}
|
||||
onClick={() => copyToClipboard(pm)}
|
||||
>
|
||||
{pm === "bun" ? "🥟 bun" : pm}
|
||||
@@ -112,26 +110,27 @@ const CodeContainer = () => {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="bg-gray-50 p-4 text-left font-mono text-sm dark:bg-gray-900">
|
||||
<div className="bg-card p-4 text-left font-mono text-sm">
|
||||
<div className="flex items-center">
|
||||
<span className="mr-2 text-gray-600 dark:text-gray-400">$</span>
|
||||
<span className="mr-2 text-muted-foreground">$</span>
|
||||
<div className="flex-grow">
|
||||
<span className="text-gray-800 dark:text-gray-200">
|
||||
{commands[selectedPM]}
|
||||
</span>
|
||||
<span className="text-foreground">{commands[selectedPM]}</span>
|
||||
{step === 0 && (
|
||||
<span
|
||||
className={`ml-0.5 inline-block h-4 w-2 bg-gray-800 dark:bg-white ${showCursor ? "opacity-100" : "opacity-0"}`}
|
||||
className={cn(
|
||||
"ml-0.5 inline-block h-4 w-2 bg-foreground",
|
||||
showCursor ? "opacity-100" : "opacity-0",
|
||||
)}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => copyToClipboard(selectedPM)}
|
||||
className="text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-300"
|
||||
className="text-muted-foreground hover:text-foreground"
|
||||
>
|
||||
{copied ? (
|
||||
<Check className="h-4 w-4 text-gray-600 dark:text-gray-400" />
|
||||
<Check className="h-4 w-4 text-[--color-chart-4]" />
|
||||
) : (
|
||||
<ClipboardCopy className="h-4 w-4" />
|
||||
)}
|
||||
@@ -141,55 +140,47 @@ const CodeContainer = () => {
|
||||
{step > 0 && (
|
||||
<div className="mt-3 space-y-1.5 text-sm">
|
||||
{step > 0 && (
|
||||
<div className="text-gray-600 dark:text-gray-400">
|
||||
<div className="text-muted-foreground">
|
||||
Creating a new Better-T-Stack project
|
||||
</div>
|
||||
)}
|
||||
|
||||
{step > 1 && (
|
||||
<div className="ml-2 grid grid-cols-[80px_1fr] gap-x-2 text-gray-700 text-xs dark:text-gray-300">
|
||||
<div className="ml-2 grid grid-cols-[80px_1fr] gap-x-2 text-muted-foreground text-xs">
|
||||
<span>Project:</span>
|
||||
<span className="text-gray-800 dark:text-gray-200">
|
||||
my-app
|
||||
</span>
|
||||
<span className="text-foreground">my-app</span>
|
||||
<span>Frontend:</span>
|
||||
<span className="text-gray-800 dark:text-gray-200">
|
||||
React Web
|
||||
</span>
|
||||
<span className="text-foreground">React Web</span>
|
||||
<span>Backend:</span>
|
||||
<span className="text-gray-800 dark:text-gray-200">Hono</span>
|
||||
<span className="text-foreground">Hono</span>
|
||||
<span>Database:</span>
|
||||
<span className="text-gray-800 dark:text-gray-200">
|
||||
SQLite + Drizzle
|
||||
</span>
|
||||
<span className="text-foreground">SQLite + Drizzle</span>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{step > 2 && (
|
||||
<div className="text-gray-600 dark:text-gray-400">
|
||||
<div className="text-muted-foreground">
|
||||
✓ Creating project structure
|
||||
</div>
|
||||
)}
|
||||
|
||||
{step > 3 && (
|
||||
<div className="text-gray-600 dark:text-gray-400">
|
||||
<div className="text-muted-foreground">
|
||||
✓ Installing dependencies
|
||||
</div>
|
||||
)}
|
||||
|
||||
{step > 4 && (
|
||||
<div className="mt-2 border-gray-400 border-l-2 bg-gray-100 py-2 pl-3 text-xs dark:border-gray-600 dark:bg-gray-800">
|
||||
<span className="font-semibold text-gray-800 dark:text-gray-200">
|
||||
<div className="mt-2 border-border border-l-2 bg-muted py-2 pl-3 text-xs">
|
||||
<span className="font-semibold text-foreground">
|
||||
Project created successfully! Run:
|
||||
</span>
|
||||
<div className="mt-1 flex flex-wrap gap-1">
|
||||
<code className="rounded bg-gray-200 px-1 py-0.5 text-gray-800 dark:bg-gray-700 dark:text-white">
|
||||
<code className="rounded bg-secondary px-1 py-0.5 text-secondary-foreground">
|
||||
cd my-app
|
||||
</code>
|
||||
<span className="text-gray-700 dark:text-gray-300">
|
||||
and
|
||||
</span>
|
||||
<code className="rounded bg-gray-200 px-1 py-0.5 text-gray-800 dark:bg-gray-700 dark:text-white">
|
||||
<span className="text-muted-foreground">and</span>
|
||||
<code className="rounded bg-secondary px-1 py-0.5 text-secondary-foreground">
|
||||
{selectedPM === "npm"
|
||||
? "npm run dev"
|
||||
: selectedPM === "pnpm"
|
||||
@@ -204,17 +195,20 @@ const CodeContainer = () => {
|
||||
|
||||
{step > 4 && (
|
||||
<div className="mt-3 flex items-center">
|
||||
<span className="mr-2 text-gray-600 dark:text-gray-400">$</span>
|
||||
<span className="mr-2 text-muted-foreground">$</span>
|
||||
<span
|
||||
className={`inline-block h-4 w-2 bg-gray-800 dark:bg-white ${showCursor ? "opacity-100" : "opacity-0"}`}
|
||||
className={cn(
|
||||
"inline-block h-4 w-2 bg-foreground",
|
||||
showCursor ? "opacity-100" : "opacity-0",
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="border-gray-200 border-t bg-gray-50 px-4 py-1.5 text-left text-gray-600 text-xs dark:border-gray-800 dark:bg-gray-900 dark:text-gray-400">
|
||||
<div className="border-border border-t bg-muted px-4 py-1.5 text-left text-muted-foreground text-xs">
|
||||
For customization options:{" "}
|
||||
<code className="rounded bg-gray-200 px-1 py-0.5 text-gray-700 dark:bg-gray-700 dark:text-gray-300">
|
||||
<code className="rounded bg-secondary px-1 py-0.5 text-secondary-foreground">
|
||||
{selectedPM === "npm"
|
||||
? "npx"
|
||||
: selectedPM === "pnpm"
|
||||
|
||||
@@ -1,37 +0,0 @@
|
||||
"use client";
|
||||
|
||||
import { Check, Copy } from "lucide-react";
|
||||
import { useState } from "react";
|
||||
|
||||
interface CommandDisplayProps {
|
||||
command: string;
|
||||
}
|
||||
|
||||
export function CommandDisplay({ command }: CommandDisplayProps) {
|
||||
const [copied, setCopied] = useState(false);
|
||||
|
||||
const copyToClipboard = async () => {
|
||||
await navigator.clipboard.writeText(command);
|
||||
setCopied(true);
|
||||
setTimeout(() => setCopied(false), 2000);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="group relative">
|
||||
<div className="w-fit overflow-x-auto rounded-lg border border-gray-200 bg-gray-100/80 p-4 font-mono text-gray-800 text-sm backdrop-blur-xl dark:border-gray-800 dark:bg-gray-950/20 dark:text-gray-300">
|
||||
<button
|
||||
type="button"
|
||||
onClick={copyToClipboard}
|
||||
className="-translate-y-1/2 absolute top-1/2 right-4 rounded-md p-2 transition-colors hover:bg-gray-200 dark:hover:bg-gray-800"
|
||||
>
|
||||
{copied ? (
|
||||
<Check className="h-4 w-4 text-green-500" />
|
||||
) : (
|
||||
<Copy className="h-4 w-4 text-gray-500 dark:text-gray-400" />
|
||||
)}
|
||||
</button>
|
||||
<pre className="pr-12 max-sm:text-xs">{command}</pre>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
import { cn } from "@/lib/utils";
|
||||
import { Code, Sliders, Terminal, TerminalSquare } from "lucide-react";
|
||||
import { motion } from "motion/react";
|
||||
import StackArchitect from "./StackArchitech";
|
||||
@@ -14,11 +15,10 @@ export default function CustomizableSection() {
|
||||
className="relative"
|
||||
>
|
||||
<h2 className="font-bold font-mono text-2xl tracking-tight sm:text-3xl md:text-4xl lg:text-5xl">
|
||||
<span className="border-blue-500 border-b-2 pb-1 text-gray-900 dark:text-blue-100">
|
||||
<span className="border-primary border-b-2 pb-1 text-foreground dark:text-primary">
|
||||
Your Stack, Your Choice
|
||||
</span>
|
||||
</h2>
|
||||
<div className="-inset-x-1/4 -inset-y-1/2 -z-10 absolute bg-gradient-to-r from-blue-300/0 via-blue-300/10 to-blue-300/0 blur-3xl dark:from-blue-800/0 dark:via-blue-800/10 dark:to-blue-800/0" />
|
||||
</motion.div>
|
||||
|
||||
<motion.div
|
||||
@@ -28,31 +28,31 @@ export default function CustomizableSection() {
|
||||
transition={{ duration: 0.5, delay: 0.2 }}
|
||||
className="mx-auto max-w-3xl space-y-6"
|
||||
>
|
||||
<p className="font-mono text-gray-700 text-lg leading-relaxed sm:text-xl dark:text-gray-300">
|
||||
<p className="font-mono text-lg text-muted-foreground leading-relaxed sm:text-xl">
|
||||
Configure your ideal TypeScript environment with all the options you
|
||||
need
|
||||
</p>
|
||||
|
||||
<div className="flex flex-wrap justify-center gap-2 text-gray-700 text-xs sm:gap-3 sm:text-sm dark:text-gray-300">
|
||||
<div className="flex items-center gap-1.5 rounded-md border border-gray-200 bg-gray-100 px-3 py-1.5 shadow-sm transition-colors dark:border-gray-800 dark:bg-gray-900/50">
|
||||
<div className="flex flex-wrap justify-center gap-2 text-muted-foreground text-xs sm:gap-3 sm:text-sm">
|
||||
<div className="flex items-center gap-1.5 rounded-md border border-border bg-muted px-3 py-1.5 shadow-sm transition-colors">
|
||||
<Terminal className="h-3.5 w-3.5">
|
||||
<title>Runtime Options</title>
|
||||
</Terminal>
|
||||
<span>--runtime</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-1.5 rounded-md border border-gray-200 bg-gray-100 px-3 py-1.5 shadow-sm transition-colors dark:border-gray-800 dark:bg-gray-900/50">
|
||||
<div className="flex items-center gap-1.5 rounded-md border border-border bg-muted px-3 py-1.5 shadow-sm transition-colors">
|
||||
<Code className="h-3.5 w-3.5">
|
||||
<title>Framework Options</title>
|
||||
</Code>
|
||||
<span>--framework</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-1.5 rounded-md border border-gray-200 bg-gray-100 px-3 py-1.5 shadow-sm transition-colors dark:border-gray-800 dark:bg-gray-900/50">
|
||||
<div className="flex items-center gap-1.5 rounded-md border border-border bg-muted px-3 py-1.5 shadow-sm transition-colors">
|
||||
<TerminalSquare className="h-3.5 w-3.5">
|
||||
<title>Database Options</title>
|
||||
</TerminalSquare>
|
||||
<span>--database</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-1.5 rounded-md border border-gray-200 bg-gray-100 px-3 py-1.5 shadow-sm transition-colors dark:border-gray-800 dark:bg-gray-900/50">
|
||||
<div className="flex items-center gap-1.5 rounded-md border border-border bg-muted px-3 py-1.5 shadow-sm transition-colors">
|
||||
<Sliders className="h-3.5 w-3.5">
|
||||
<title>Addon Options</title>
|
||||
</Sliders>
|
||||
@@ -68,12 +68,12 @@ export default function CustomizableSection() {
|
||||
transition={{ duration: 0.4, delay: 0.3 }}
|
||||
className="flex flex-wrap justify-center gap-2 pt-2 sm:gap-3"
|
||||
>
|
||||
<Badge color="amber">Bun or Node</Badge>
|
||||
<Badge color="blue">Hono or Elysia</Badge>
|
||||
<Badge color="indigo">SQLite or PostgreSQL</Badge>
|
||||
<Badge color="cyan">Drizzle or Prisma</Badge>
|
||||
<Badge color="green">Authentication</Badge>
|
||||
<Badge color="violet">Optional Addons</Badge>
|
||||
<Badge color="chart-5">Bun or Node</Badge>
|
||||
<Badge color="chart-2">Hono or Elysia</Badge>
|
||||
<Badge color="chart-3">SQLite or PostgreSQL</Badge>
|
||||
<Badge color="chart-2">Drizzle or Prisma</Badge>
|
||||
<Badge color="chart-4">Authentication</Badge>
|
||||
<Badge color="chart-1">Optional Addons</Badge>
|
||||
</motion.div>
|
||||
</div>
|
||||
|
||||
@@ -84,7 +84,6 @@ export default function CustomizableSection() {
|
||||
transition={{ duration: 0.6, delay: 0.2 }}
|
||||
className="relative"
|
||||
>
|
||||
<div className="-z-10 absolute inset-0 bg-gradient-to-r from-blue-500/5 via-transparent to-indigo-500/5 blur-3xl" />
|
||||
<StackArchitect />
|
||||
</motion.div>
|
||||
</section>
|
||||
@@ -95,26 +94,10 @@ function Badge({
|
||||
children,
|
||||
color,
|
||||
}: { children: React.ReactNode; color: string }) {
|
||||
const colorMap = {
|
||||
amber:
|
||||
"bg-amber-100 text-amber-800 dark:bg-amber-900/30 dark:text-amber-300 border-amber-200 dark:border-amber-800/30",
|
||||
blue: "bg-blue-100 text-blue-800 dark:bg-blue-900/30 dark:text-blue-300 border-blue-200 dark:border-blue-800/30",
|
||||
indigo:
|
||||
"bg-indigo-100 text-indigo-800 dark:bg-indigo-900/30 dark:text-indigo-300 border-indigo-200 dark:border-indigo-800/30",
|
||||
cyan: "bg-cyan-100 text-cyan-800 dark:bg-cyan-900/30 dark:text-cyan-300 border-cyan-200 dark:border-cyan-800/30",
|
||||
green:
|
||||
"bg-green-100 text-green-800 dark:bg-green-900/30 dark:text-green-300 border-green-200 dark:border-green-800/30",
|
||||
violet:
|
||||
"bg-violet-100 text-violet-800 dark:bg-violet-900/30 dark:text-violet-300 border-violet-200 dark:border-violet-800/30",
|
||||
};
|
||||
const baseClasses =
|
||||
"rounded-full border px-2.5 py-1 font-medium text-xs shadow-sm";
|
||||
|
||||
return (
|
||||
<span
|
||||
className={`rounded-full border px-2.5 py-1 font-medium text-xs ${
|
||||
colorMap[color as keyof typeof colorMap]
|
||||
} shadow-sm`}
|
||||
>
|
||||
{children}
|
||||
</span>
|
||||
);
|
||||
const colorClasses = `bg-[--color-${color}]/10 text-[--color-${color}] border-[--color-${color}]/30`;
|
||||
|
||||
return <span className={cn(baseClasses, colorClasses)}>{children}</span>;
|
||||
}
|
||||
|
||||
@@ -1,138 +0,0 @@
|
||||
import { ArrowRight, Code2, Shield, Zap } from "lucide-react";
|
||||
import React from "react";
|
||||
|
||||
const Featured = () => {
|
||||
return (
|
||||
<>
|
||||
<div className="relative z-50 mx-auto w-full max-w-6xl py-24">
|
||||
<div className="grid grid-cols-1 gap-8 md:grid-cols-2 lg:grid-cols-3">
|
||||
{[
|
||||
{
|
||||
icon: Shield,
|
||||
title: "Type-Safe by Default",
|
||||
description:
|
||||
"End-to-end type safety from database to frontend. Catch errors before they happen.",
|
||||
},
|
||||
{
|
||||
icon: Zap,
|
||||
title: "Lightning Fast",
|
||||
description:
|
||||
"Built on Bun's lightning-fast runtime with optimal configurations for performance.",
|
||||
},
|
||||
{
|
||||
icon: Code2,
|
||||
title: "Developer Experience",
|
||||
description:
|
||||
"Modern tooling and intuitive APIs make development a breeze.",
|
||||
},
|
||||
].map((feature) => (
|
||||
<div
|
||||
key={feature.title}
|
||||
className="group relative rounded-xl border border-gray-200 bg-white p-6 transition-all hover:border-gray-300 dark:border-gray-800 dark:bg-gray-900/50 dark:hover:border-gray-700"
|
||||
>
|
||||
<div className="absolute inset-0 rounded-xl bg-gradient-to-r from-blue-500/10 to-purple-500/10 opacity-0 transition-opacity group-hover:opacity-100" />
|
||||
<feature.icon className="mb-4 h-10 w-10 text-blue-500 dark:text-blue-400" />
|
||||
<h3 className="mb-2 font-semibold text-gray-900 text-xl dark:text-white">
|
||||
{feature.title}
|
||||
</h3>
|
||||
<p className="text-gray-600 dark:text-gray-400">
|
||||
{feature.description}
|
||||
</p>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="relative z-50 w-full border-gray-200 border-y bg-gray-50 dark:border-gray-800 dark:bg-gray-900/50">
|
||||
<div className="mx-auto max-w-6xl py-24">
|
||||
<div className="mb-12 text-center">
|
||||
<h2 className="mb-4 font-bold text-3xl text-gray-900 md:text-4xl dark:text-white">
|
||||
Write Better Code, Faster
|
||||
</h2>
|
||||
<p className="mx-auto max-w-2xl text-gray-600 dark:text-gray-400">
|
||||
Leverage the power of TypeScript with our carefully selected tools
|
||||
and frameworks.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="grid items-center gap-12 md:grid-cols-2">
|
||||
<div className="space-y-6">
|
||||
{[
|
||||
{
|
||||
title: "Type-Safe API Calls",
|
||||
description:
|
||||
"No more guessing API shapes. tRPC ensures type safety across your stack.",
|
||||
},
|
||||
{
|
||||
title: "Database Type Safety",
|
||||
description:
|
||||
"Drizzle ORM provides type-safe database queries with great DX.",
|
||||
},
|
||||
{
|
||||
title: "Modern Authentication",
|
||||
description:
|
||||
"Secure authentication with Better-Auth, built for modern web apps.",
|
||||
},
|
||||
].map((item) => (
|
||||
<div
|
||||
key={item.title}
|
||||
className="flex items-start space-x-4 rounded-lg p-4 transition-colors hover:bg-gray-100 dark:hover:bg-gray-800/50"
|
||||
>
|
||||
<ArrowRight className="mt-1 h-6 w-6 text-blue-500 dark:text-blue-400" />
|
||||
<div>
|
||||
<h3 className="font-semibold text-gray-900 text-lg dark:text-white">
|
||||
{item.title}
|
||||
</h3>
|
||||
<p className="text-gray-600 dark:text-gray-400">
|
||||
{item.description}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<div className="relative">
|
||||
<div className="absolute inset-0 bg-gradient-to-r from-blue-500/10 to-purple-500/10 blur-xl" />
|
||||
<div className="relative rounded-lg border border-gray-200 bg-white p-6 dark:border-gray-800 dark:bg-gray-900">
|
||||
<pre className="overflow-x-auto text-gray-700 text-sm dark:text-gray-300">
|
||||
<code>{`// Type-safe API endpoint
|
||||
export const userRouter = router({
|
||||
get: publicProcedure
|
||||
.input(z.string())
|
||||
.query(async ({ input }) => {
|
||||
const user = await db
|
||||
.select()
|
||||
.from(users)
|
||||
.where(eq(users.id, input));
|
||||
|
||||
return user;
|
||||
})
|
||||
});`}</code>
|
||||
</pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="relative z-50 mx-auto w-full max-w-6xl py-24 text-center">
|
||||
<h2 className="mb-6 font-bold text-3xl text-gray-900 md:text-4xl dark:text-white">
|
||||
Ready to Build Something Amazing?
|
||||
</h2>
|
||||
<p className="mx-auto mb-8 max-w-xl text-gray-600 dark:text-gray-400">
|
||||
Start your next project with Better-T Stack and experience the future
|
||||
of web development.
|
||||
</p>
|
||||
<button
|
||||
type="button"
|
||||
className="group inline-flex items-center rounded-full bg-blue-600 px-8 py-3 font-semibold text-white transition-colors hover:bg-blue-700"
|
||||
>
|
||||
Get Started
|
||||
<ArrowRight className="ml-2 h-5 w-5 transform transition-transform group-hover:translate-x-1" />
|
||||
</button>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default Featured;
|
||||
@@ -3,14 +3,14 @@ import Link from "next/link";
|
||||
|
||||
const Footer = () => {
|
||||
return (
|
||||
<footer className="relative w-full border-gray-200 border-t font-mono dark:border-gray-800">
|
||||
<footer className="relative w-full border-border border-t font-mono">
|
||||
<div className="mx-auto max-w-6xl px-4 py-12 sm:px-6">
|
||||
<div className="mb-12 grid gap-8 md:grid-cols-3">
|
||||
<div>
|
||||
<h3 className="mb-4 flex items-center gap-2 font-bold text-gray-900 text-lg dark:text-white">
|
||||
<h3 className="mb-4 flex items-center gap-2 font-bold text-foreground text-lg">
|
||||
<span>Better-T Stack</span>
|
||||
</h3>
|
||||
<p className="text-gray-600 leading-relaxed dark:text-gray-400">
|
||||
<p className="text-muted-foreground leading-relaxed">
|
||||
Type-safe, modern TypeScript scaffolding for full-stack web
|
||||
development
|
||||
</p>
|
||||
@@ -19,7 +19,7 @@ const Footer = () => {
|
||||
<Link
|
||||
href="https://github.com/better-t-stack/create-better-t-stack"
|
||||
target="_blank"
|
||||
className="text-gray-500 transition-colors hover:text-gray-900 dark:hover:text-white"
|
||||
className="text-muted-foreground transition-colors hover:text-foreground"
|
||||
aria-label="GitHub"
|
||||
>
|
||||
<Github size={18} />
|
||||
@@ -27,7 +27,7 @@ const Footer = () => {
|
||||
<Link
|
||||
href="https://www.npmjs.com/package/create-better-t-stack"
|
||||
target="_blank"
|
||||
className="text-gray-500 transition-colors hover:text-gray-900 dark:hover:text-white"
|
||||
className="text-muted-foreground transition-colors hover:text-foreground"
|
||||
aria-label="NPM"
|
||||
>
|
||||
<svg
|
||||
@@ -44,15 +44,15 @@ const Footer = () => {
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h3 className="mb-4 font-bold text-gray-900 text-lg dark:text-white">
|
||||
<h3 className="mb-4 font-bold text-foreground text-lg">
|
||||
Resources
|
||||
</h3>
|
||||
<ul className="space-y-2.5 text-gray-600 dark:text-gray-400">
|
||||
<ul className="space-y-2.5 text-muted-foreground">
|
||||
<li>
|
||||
<Link
|
||||
target="_blank"
|
||||
href="https://github.com/better-t-stack/create-better-t-stack"
|
||||
className="transition-colors hover:text-blue-600 dark:hover:text-blue-400"
|
||||
className="transition-colors hover:text-primary"
|
||||
>
|
||||
GitHub Repository
|
||||
</Link>
|
||||
@@ -61,7 +61,7 @@ const Footer = () => {
|
||||
<Link
|
||||
target="_blank"
|
||||
href="https://www.npmjs.com/package/create-better-t-stack"
|
||||
className="transition-colors hover:text-blue-600 dark:hover:text-blue-400"
|
||||
className="transition-colors hover:text-primary"
|
||||
>
|
||||
NPM Package
|
||||
</Link>
|
||||
@@ -70,30 +70,30 @@ const Footer = () => {
|
||||
<Link
|
||||
target="_blank"
|
||||
href="https://my-better-t-app-client.pages.dev/"
|
||||
className="transition-colors hover:text-blue-600 dark:hover:text-blue-400"
|
||||
className="transition-colors hover:text-primary"
|
||||
>
|
||||
Demo Application
|
||||
</Link>
|
||||
</li>
|
||||
{/* <li>
|
||||
{/*
|
||||
<li>
|
||||
<Link
|
||||
target="_blank"
|
||||
href="https://github.com/better-t-stack/create-better-t-stack#readme"
|
||||
className="hover:text-blue-600 dark:hover:text-blue-400 transition-colors"
|
||||
className="hover:text-primary transition-colors"
|
||||
>
|
||||
Documentation
|
||||
</Link>
|
||||
</li> */}
|
||||
</li>
|
||||
*/}
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h3 className="mb-4 font-bold text-gray-900 text-lg dark:text-white">
|
||||
Contact
|
||||
</h3>
|
||||
<div className="space-y-2.5 text-gray-600 dark:text-gray-400">
|
||||
<h3 className="mb-4 font-bold text-foreground text-lg">Contact</h3>
|
||||
<div className="space-y-2.5 text-muted-foreground">
|
||||
<p className="flex items-center">
|
||||
<span className="mr-2 rounded bg-gray-100 px-2 py-1 font-mono text-sm dark:bg-gray-800">
|
||||
<span className="mr-2 rounded bg-muted px-2 py-1 font-mono text-sm">
|
||||
$
|
||||
</span>
|
||||
<span>amanvarshney.work@gmail.com</span>
|
||||
@@ -106,13 +106,13 @@ const Footer = () => {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="mt-12 flex flex-col items-center justify-between gap-4 border-gray-200 border-t pt-6 sm:flex-row dark:border-gray-800">
|
||||
<p className="text-gray-500 text-sm">
|
||||
<div className="mt-12 flex flex-col items-center justify-between gap-4 border-border border-t pt-6 sm:flex-row">
|
||||
<p className="text-muted-foreground text-sm">
|
||||
© {new Date().getFullYear()} Better-T Stack. All rights reserved.
|
||||
</p>
|
||||
<p className="flex items-center gap-1.5 text-gray-500 text-sm">
|
||||
<p className="flex items-center gap-1.5 text-muted-foreground text-sm">
|
||||
Built with
|
||||
<span className="bg-gradient-to-r from-blue-500 to-indigo-600 bg-clip-text font-medium text-transparent">
|
||||
<span className="bg-gradient-to-r from-primary to-primary/80 bg-clip-text font-medium text-transparent">
|
||||
TypeScript
|
||||
</span>
|
||||
</p>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
"use client";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { Github, Maximize2, Menu, X } from "lucide-react";
|
||||
import Link from "next/link";
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
@@ -47,33 +48,34 @@ const Navbar = () => {
|
||||
return (
|
||||
<>
|
||||
<nav
|
||||
className={`fixed top-0 z-[100] flex w-full items-center justify-between px-4 py-4 transition-all duration-300 sm:px-8 ${
|
||||
scrolled
|
||||
? "bg-transparent"
|
||||
: "bg-gray-50/80 backdrop-blur-xl dark:bg-gray-950/80"
|
||||
}`}
|
||||
className={cn(
|
||||
"fixed top-0 z-[100] flex w-full items-center justify-between px-4 py-4 transition-all duration-300 sm:px-8",
|
||||
scrolled ? "bg-transparent" : "bg-background/80 backdrop-blur-xl",
|
||||
)}
|
||||
>
|
||||
<div
|
||||
className={`flex flex-row items-center space-x-3 transition-opacity duration-300 ${
|
||||
scrolled ? "opacity-0" : "opacity-100"
|
||||
}`}
|
||||
className={cn(
|
||||
"flex flex-row items-center space-x-3 transition-opacity duration-300",
|
||||
scrolled ? "opacity-0" : "opacity-100",
|
||||
)}
|
||||
>
|
||||
<div className="flex h-4 w-4 items-center justify-center rounded-sm">
|
||||
<span className="text-blue-600 text-md dark:text-blue-500">$_</span>
|
||||
<span className="text-md text-primary">$_</span>
|
||||
</div>
|
||||
<span className="font-semibold text-gray-600 text-md dark:text-gray-100">
|
||||
<span className="font-semibold text-foreground text-md">
|
||||
Better-T Stack
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div className="-translate-x-1/2 absolute left-1/2 hidden transform md:block">
|
||||
<div
|
||||
className={`relative flex items-center rounded-lg border border-gray-200 bg-gray-100/90 px-1.5 py-1 text-sm backdrop-blur-sm transition-all duration-500 ease-out dark:border-gray-800 dark:bg-gray-900/90 ${
|
||||
scrolled ? "w-[352px]" : "w-[245px]"
|
||||
}`}
|
||||
className={cn(
|
||||
"relative flex items-center rounded-lg border border-border bg-muted/90 px-1.5 py-1 text-sm backdrop-blur-sm transition-all duration-500 ease-out",
|
||||
scrolled ? "w-[352px]" : "w-[245px]",
|
||||
)}
|
||||
>
|
||||
<div
|
||||
className="absolute rounded-md bg-white shadow-sm transition-all duration-200 ease-in-out dark:bg-gray-800"
|
||||
className="absolute rounded-md bg-card shadow-sm transition-all duration-200 ease-in-out"
|
||||
style={bgStyles}
|
||||
/>
|
||||
<Link
|
||||
@@ -82,9 +84,9 @@ const Navbar = () => {
|
||||
linkRefs.current.home = ref;
|
||||
}}
|
||||
onMouseOver={() => setActiveLink("home")}
|
||||
className="relative flex items-center gap-1 rounded-md px-4 py-2 font-mono text-gray-700 transition-colors hover:text-blue-600 dark:text-gray-300 dark:hover:text-blue-300"
|
||||
className="relative flex items-center gap-1 rounded-md px-4 py-2 font-mono text-muted-foreground transition-colors hover:text-primary"
|
||||
>
|
||||
<span className="text-blue-600 dark:text-blue-400">~/</span>
|
||||
<span className="text-primary">~/</span>
|
||||
home
|
||||
</Link>
|
||||
|
||||
@@ -96,7 +98,7 @@ const Navbar = () => {
|
||||
}}
|
||||
onMouseOver={() => setActiveLink("demo")}
|
||||
onMouseLeave={() => setActiveLink("home")}
|
||||
className="relative flex items-center gap-2 rounded-md px-4 py-2 font-mono text-gray-700 transition-colors hover:text-blue-600 dark:text-gray-300 dark:hover:text-blue-300"
|
||||
className="relative flex items-center gap-2 rounded-md px-4 py-2 font-mono text-muted-foreground transition-colors hover:text-primary"
|
||||
>
|
||||
<span>demo</span>
|
||||
</Link>
|
||||
@@ -109,7 +111,7 @@ const Navbar = () => {
|
||||
}}
|
||||
onMouseOver={() => setActiveLink("npm")}
|
||||
onMouseLeave={() => setActiveLink("home")}
|
||||
className="relative flex items-center gap-2 rounded-md px-4 py-2 font-mono text-gray-700 transition-colors hover:text-blue-600 dark:text-gray-300 dark:hover:text-blue-300"
|
||||
className="relative flex items-center gap-2 rounded-md px-4 py-2 font-mono text-muted-foreground transition-colors hover:text-primary"
|
||||
>
|
||||
<PackageIcon pm="npm" className="h-4 w-4 rounded-full" />{" "}
|
||||
<span>npm</span>
|
||||
@@ -123,11 +125,12 @@ const Navbar = () => {
|
||||
}}
|
||||
onMouseOver={() => setActiveLink("github")}
|
||||
onMouseLeave={() => setActiveLink("home")}
|
||||
className={`relative flex items-center gap-2 rounded-md px-4 py-2 font-mono text-gray-700 transition-colors hover:text-blue-600 dark:text-gray-300 dark:hover:text-blue-300 ${
|
||||
className={cn(
|
||||
"relative flex items-center gap-2 rounded-md px-4 py-2 font-mono text-muted-foreground transition-colors hover:text-primary",
|
||||
scrolled
|
||||
? "translate-y-0 opacity-100"
|
||||
: "pointer-events-none opacity-0"
|
||||
}`}
|
||||
: "pointer-events-none opacity-0",
|
||||
)}
|
||||
>
|
||||
<Github className="size-4">
|
||||
<title>GitHub</title>
|
||||
@@ -138,13 +141,14 @@ const Navbar = () => {
|
||||
</div>
|
||||
|
||||
<div
|
||||
className={`hidden justify-end gap-2 transition-opacity duration-300 md:flex ${
|
||||
scrolled ? "pointer-events-none opacity-0" : "opacity-100"
|
||||
}`}
|
||||
className={cn(
|
||||
"hidden justify-end gap-2 transition-opacity duration-300 md:flex",
|
||||
scrolled ? "pointer-events-none opacity-0" : "opacity-100",
|
||||
)}
|
||||
>
|
||||
<Link
|
||||
href="/new"
|
||||
className="inline-flex items-center rounded-lg border border-blue-300 bg-blue-100/90 px-4 py-1 font-mono text-blue-700 text-sm backdrop-blur-sm transition-colors hover:bg-blue-200 dark:border-blue-800 dark:bg-blue-900/90 dark:text-blue-300 dark:hover:bg-blue-800/50"
|
||||
className="inline-flex items-center rounded-lg border border-primary/50 bg-primary/10 px-4 py-1 font-mono text-primary text-sm backdrop-blur-sm transition-colors hover:bg-primary/20"
|
||||
>
|
||||
<Maximize2 className="mr-1 size-4" />
|
||||
Stack Builder
|
||||
@@ -152,7 +156,7 @@ const Navbar = () => {
|
||||
<Link
|
||||
href="https://www.github.com/better-t-stack/create-better-t-stack"
|
||||
target="_blank"
|
||||
className="inline-flex items-center rounded-lg border border-gray-200 bg-gray-100/90 px-4 py-1 font-mono text-gray-700 text-sm backdrop-blur-sm transition-colors hover:text-blue-600 dark:border-gray-800 dark:bg-gray-900/90 dark:text-gray-300 dark:hover:text-blue-300"
|
||||
className="inline-flex items-center rounded-lg border border-border bg-muted/90 px-4 py-1 font-mono text-muted-foreground text-sm backdrop-blur-sm transition-colors hover:bg-muted hover:text-primary"
|
||||
>
|
||||
<Github className="mr-1 size-4">
|
||||
<title>GitHub</title>
|
||||
@@ -164,7 +168,7 @@ const Navbar = () => {
|
||||
<button
|
||||
type="button"
|
||||
onClick={toggleMobileMenu}
|
||||
className="flex items-center justify-center rounded-md p-2 text-gray-700 hover:bg-gray-100/50 focus:outline-none md:hidden dark:text-gray-300 dark:hover:bg-gray-800/50"
|
||||
className="flex items-center justify-center rounded-md p-2 text-foreground hover:bg-muted/50 focus:outline-none md:hidden"
|
||||
aria-expanded={mobileMenuOpen}
|
||||
>
|
||||
{mobileMenuOpen ? (
|
||||
@@ -177,39 +181,38 @@ const Navbar = () => {
|
||||
</nav>
|
||||
|
||||
<div
|
||||
className={`fixed inset-0 z-[99] pt-16 backdrop-blur-md transition-all duration-300 ease-in-out md:hidden ${
|
||||
className={cn(
|
||||
"fixed inset-0 z-[99] pt-16 backdrop-blur-md transition-all duration-300 ease-in-out md:hidden",
|
||||
mobileMenuOpen
|
||||
? "pointer-events-auto opacity-100"
|
||||
: "pointer-events-none opacity-0"
|
||||
}`}
|
||||
: "pointer-events-none opacity-0",
|
||||
)}
|
||||
>
|
||||
<div className="mx-4 mt-4 overflow-hidden rounded-lg border border-gray-300 bg-gray-100/95 shadow-lg dark:border-gray-700 dark:bg-gray-900/95">
|
||||
<div className="flex items-center bg-gray-200 px-4 py-2 dark:bg-gray-800">
|
||||
<div className="mx-4 mt-4 overflow-hidden rounded-lg border border-border bg-card/95 shadow-lg">
|
||||
<div className="flex items-center bg-muted px-4 py-2">
|
||||
<div className="mr-4 flex space-x-2">
|
||||
<div className="h-3 w-3 rounded-full bg-red-500" />
|
||||
<div className="h-3 w-3 rounded-full bg-yellow-500" />
|
||||
<div className="h-3 w-3 rounded-full bg-green-500" />
|
||||
</div>
|
||||
<div className="font-mono text-gray-600 text-sm dark:text-gray-300">
|
||||
<div className="font-mono text-muted-foreground text-sm">
|
||||
better-t-stack:~
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="p-4 font-mono text-sm">
|
||||
<div className="pb-3">
|
||||
<span className="text-green-600 dark:text-green-500">
|
||||
<span className="text-[--color-chart-4]">
|
||||
user@better-t-stack
|
||||
</span>
|
||||
<span className="text-gray-600 dark:text-gray-400">:~$</span>
|
||||
<span className="ml-2 text-gray-800 dark:text-gray-200">
|
||||
ls -la
|
||||
</span>
|
||||
<span className="text-muted-foreground">:~$</span>
|
||||
<span className="ml-2 text-foreground">ls -la</span>
|
||||
</div>
|
||||
|
||||
<div className="space-y-2 border-gray-300 border-l-2 pl-4 dark:border-gray-700">
|
||||
<div className="space-y-2 border-border border-l-2 pl-4">
|
||||
<Link
|
||||
href="/"
|
||||
className="block text-blue-600 hover:underline dark:text-blue-400"
|
||||
className="block text-primary hover:underline"
|
||||
onClick={() => setMobileMenuOpen(false)}
|
||||
>
|
||||
~/home
|
||||
@@ -218,7 +221,7 @@ const Navbar = () => {
|
||||
<Link
|
||||
href="https://my-better-t-app-client.pages.dev/"
|
||||
target="_blank"
|
||||
className="block text-blue-600 hover:underline dark:text-blue-400"
|
||||
className="block text-primary hover:underline"
|
||||
onClick={() => setMobileMenuOpen(false)}
|
||||
>
|
||||
~/demo
|
||||
@@ -229,7 +232,7 @@ const Navbar = () => {
|
||||
<Link
|
||||
href="https://www.npmjs.com/package/create-better-t-stack"
|
||||
target="_blank"
|
||||
className="block text-blue-600 hover:underline dark:text-blue-400"
|
||||
className="block text-primary hover:underline"
|
||||
onClick={() => setMobileMenuOpen(false)}
|
||||
>
|
||||
~/npm
|
||||
@@ -237,11 +240,11 @@ const Navbar = () => {
|
||||
</div>
|
||||
|
||||
<div className="flex items-center">
|
||||
<Github className="mr-1 size-4 text-gray-700 dark:text-gray-300" />
|
||||
<Github className="mr-1 size-4 text-foreground" />
|
||||
<Link
|
||||
href="https://www.github.com/better-t-stack/create-better-t-stack"
|
||||
target="_blank"
|
||||
className="block text-blue-600 hover:underline dark:text-blue-400"
|
||||
className="block text-primary hover:underline"
|
||||
onClick={() => setMobileMenuOpen(false)}
|
||||
>
|
||||
~/github
|
||||
@@ -250,20 +253,18 @@ const Navbar = () => {
|
||||
</div>
|
||||
|
||||
<div className="mt-6 pb-3">
|
||||
<span className="text-green-600 dark:text-green-500">
|
||||
<span className="text-[--color-chart-4]">
|
||||
user@better-t-stack
|
||||
</span>
|
||||
<span className="text-gray-600 dark:text-gray-400">:~$</span>
|
||||
<span className="ml-2 text-gray-800 dark:text-gray-200">
|
||||
star-repo
|
||||
</span>
|
||||
<span className="text-muted-foreground">:~$</span>
|
||||
<span className="ml-2 text-foreground">star-repo</span>
|
||||
</div>
|
||||
|
||||
<div className="border-gray-300 border-l-2 pb-2 pl-4 dark:border-gray-700">
|
||||
<div className="border-border border-l-2 pb-2 pl-4">
|
||||
<Link
|
||||
href="https://www.github.com/better-t-stack/create-better-t-stack"
|
||||
target="_blank"
|
||||
className="inline-flex items-center rounded-md bg-gray-200 px-4 py-2 text-gray-800 transition-colors hover:bg-gray-300 dark:bg-gray-800 dark:text-gray-200 dark:hover:bg-gray-700"
|
||||
className="inline-flex items-center rounded-md bg-muted px-4 py-2 text-foreground transition-colors hover:bg-muted/80"
|
||||
onClick={() => setMobileMenuOpen(false)}
|
||||
>
|
||||
<Github className="mr-1 size-5" />
|
||||
@@ -272,11 +273,11 @@ const Navbar = () => {
|
||||
</div>
|
||||
|
||||
<div className="mt-4">
|
||||
<span className="text-green-600 dark:text-green-500">
|
||||
<span className="text-[--color-chart-4]">
|
||||
user@better-t-stack
|
||||
</span>
|
||||
<span className="text-gray-600 dark:text-gray-400">:~$</span>
|
||||
<span className="ml-2 animate-pulse">█</span>
|
||||
<span className="text-muted-foreground">:~$</span>
|
||||
<span className="ml-2 animate-pulse text-foreground">█</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
"use client";
|
||||
|
||||
import { cn } from "@/lib/utils";
|
||||
import { useEffect, useState } from "react";
|
||||
|
||||
const NpmPackage = () => {
|
||||
@@ -9,21 +10,33 @@ const NpmPackage = () => {
|
||||
useEffect(() => {
|
||||
const getLatestVersion = async () => {
|
||||
setVersionLoading(true);
|
||||
const res = await fetch(
|
||||
"https://registry.npmjs.org/create-better-t-stack/latest",
|
||||
);
|
||||
const data = await res.json();
|
||||
setVersionLoading(false);
|
||||
setVersion(data.version);
|
||||
try {
|
||||
const res = await fetch(
|
||||
"https://registry.npmjs.org/create-better-t-stack/latest",
|
||||
);
|
||||
if (!res.ok) throw new Error("Failed to fetch version");
|
||||
const data = await res.json();
|
||||
setVersion(data.version);
|
||||
} catch (error) {
|
||||
console.error("Error fetching NPM version:", error);
|
||||
setVersion("?.?.?");
|
||||
} finally {
|
||||
setVersionLoading(false);
|
||||
}
|
||||
};
|
||||
getLatestVersion();
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div className="mt-2 flex items-center justify-center">
|
||||
<span className="mr-2 inline-block h-5 w-3 animate-pulse bg-blue-400 dark:bg-blue-500" />
|
||||
<span className="font-mono text-gray-700 text-xl dark:text-gray-300">
|
||||
{versionLoading ? "[v1.0.0]" : `[v${version}]`}
|
||||
<span
|
||||
className={cn(
|
||||
"mr-2 inline-block h-5 w-3 bg-primary",
|
||||
versionLoading && "animate-pulse",
|
||||
)}
|
||||
/>
|
||||
<span className="font-mono text-muted-foreground text-xl">
|
||||
{versionLoading ? "[v?.?.?]" : `[v${version}]`}
|
||||
</span>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
const SafetyMessage = () => {
|
||||
return <div className="relative" />;
|
||||
};
|
||||
|
||||
export default SafetyMessage;
|
||||
@@ -1,16 +1,19 @@
|
||||
"use client";
|
||||
|
||||
import { ScrollArea } from "@/components/ui/scroll-area";
|
||||
import {
|
||||
DEFAULT_STACK,
|
||||
PRESET_TEMPLATES,
|
||||
type StackState,
|
||||
TECH_OPTIONS,
|
||||
} from "@/lib/constant";
|
||||
import { cn } from "@/lib/utils";
|
||||
import {
|
||||
Check,
|
||||
Circle,
|
||||
CircleCheck,
|
||||
ClipboardCopy,
|
||||
Github,
|
||||
HelpCircle,
|
||||
InfoIcon,
|
||||
RefreshCw,
|
||||
@@ -20,6 +23,7 @@ import {
|
||||
} from "lucide-react";
|
||||
import { motion } from "motion/react";
|
||||
import Image from "next/image";
|
||||
import Link from "next/link";
|
||||
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
||||
|
||||
const validateProjectName = (name: string): string | undefined => {
|
||||
@@ -158,7 +162,6 @@ const StackArchitect = () => {
|
||||
const isPWACompat = hasPWACompatibleFrontend(nextStack.frontend);
|
||||
const isNative = hasNativeFrontend(nextStack.frontend);
|
||||
|
||||
// Database/ORM/Auth/DB Setup auto-fix
|
||||
if (nextStack.database === "none") {
|
||||
if (nextStack.orm !== "none") {
|
||||
nextStack.orm = "none";
|
||||
@@ -212,13 +215,11 @@ const StackArchitect = () => {
|
||||
changed = true;
|
||||
}
|
||||
|
||||
// API auto-fix for Native
|
||||
if (isNative && nextStack.api !== "trpc") {
|
||||
nextStack.api = "trpc";
|
||||
changed = true;
|
||||
}
|
||||
|
||||
// Addons auto-fix
|
||||
const incompatibleAddons: string[] = [];
|
||||
if (!isPWACompat) incompatibleAddons.push("pwa", "tauri");
|
||||
const originalAddonsLength = nextStack.addons.length;
|
||||
@@ -235,7 +236,6 @@ const StackArchitect = () => {
|
||||
changed = true;
|
||||
}
|
||||
|
||||
// Examples auto-fix
|
||||
const incompatibleExamples: string[] = [];
|
||||
if (!isWeb) incompatibleExamples.push("todo", "ai");
|
||||
if (nextStack.database === "none") incompatibleExamples.push("todo");
|
||||
@@ -608,18 +608,13 @@ const StackArchitect = () => {
|
||||
const catKey = category as keyof StackState;
|
||||
|
||||
if (
|
||||
(catKey === "frontend" ||
|
||||
catKey === "addons" ||
|
||||
catKey === "examples") &&
|
||||
catKey === "frontend" &&
|
||||
Array.isArray(stack[catKey]) &&
|
||||
(stack[catKey] as string[]).length === 1 &&
|
||||
(stack[catKey] as string[])[0] === techId
|
||||
(stack[catKey] as string[])[0] === techId &&
|
||||
techId !== "none"
|
||||
) {
|
||||
if (catKey === "frontend" && techId === "none") {
|
||||
} else if (catKey !== "frontend") {
|
||||
} else {
|
||||
return "At least one frontend option must be selected.";
|
||||
}
|
||||
return "At least one frontend option must be selected.";
|
||||
}
|
||||
|
||||
if (
|
||||
@@ -630,7 +625,9 @@ const StackArchitect = () => {
|
||||
) &&
|
||||
stack[catKey] === techId
|
||||
) {
|
||||
return "This option is currently selected.";
|
||||
if (techId !== "none" && techId !== "false") {
|
||||
return "This option is currently selected.";
|
||||
}
|
||||
}
|
||||
|
||||
if (catKey === "api" && techId !== "trpc" && currentHasNativeFrontend) {
|
||||
@@ -769,21 +766,27 @@ const StackArchitect = () => {
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="flex h-screen flex-col overflow-hidden border-gray-300 bg-gray-100 text-gray-800 shadow-xl dark:border-gray-700 dark:bg-gray-900 dark:text-white">
|
||||
<div className="flex flex-shrink-0 items-center justify-between border-gray-300 border-b bg-gray-200 px-2 py-2 sm:px-4 dark:border-gray-700 dark:bg-gray-800">
|
||||
<div className="flex space-x-2">
|
||||
<div className="h-3 w-3 rounded-full bg-red-500" />
|
||||
<div className="h-3 w-3 rounded-full bg-yellow-500" />
|
||||
<div className="h-3 w-3 rounded-full bg-green-500" />
|
||||
</div>
|
||||
<div className="hidden font-mono text-gray-600 text-xs sm:block dark:text-gray-400">
|
||||
Stack Architect Terminal
|
||||
<div
|
||||
className={cn(
|
||||
"flex h-screen flex-col overflow-hidden border-border bg-background text-foreground shadow-xl",
|
||||
)}
|
||||
>
|
||||
<div
|
||||
className={cn(
|
||||
"flex flex-shrink-0 items-center justify-between border-border border-b bg-card px-2 py-2 sm:px-4",
|
||||
)}
|
||||
>
|
||||
<div className="font-mono text-muted-foreground text-xs">Home</div>
|
||||
<div className="hidden font-mono text-muted-foreground text-xs sm:block">
|
||||
Create Better T Stack
|
||||
</div>
|
||||
<div className="flex space-x-2">
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setShowHelp(!showHelp)}
|
||||
className="text-gray-600 transition-colors hover:text-gray-800 dark:text-gray-400 dark:hover:text-white"
|
||||
className={cn(
|
||||
"text-muted-foreground transition-colors hover:text-foreground",
|
||||
)}
|
||||
title="Help"
|
||||
>
|
||||
<HelpCircle className="h-4 w-4" />
|
||||
@@ -791,20 +794,37 @@ const StackArchitect = () => {
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setShowPresets(!showPresets)}
|
||||
className="text-gray-600 transition-colors hover:text-gray-800 dark:text-gray-400 dark:hover:text-white"
|
||||
className={cn(
|
||||
"text-muted-foreground transition-colors hover:text-foreground",
|
||||
)}
|
||||
title="Presets"
|
||||
>
|
||||
<Star className="h-4 w-4" />
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setShowPresets(!showPresets)}
|
||||
className={cn(
|
||||
"text-muted-foreground transition-colors hover:text-foreground",
|
||||
)}
|
||||
title="Presets"
|
||||
>
|
||||
<Link
|
||||
href={"https://github.com/AmanVarshney01/create-better-t-stack"}
|
||||
target="_blank"
|
||||
>
|
||||
<Github className="h-4 w-4" />
|
||||
</Link>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{showHelp && (
|
||||
<div className="flex-shrink-0 border-gray-300 border-b bg-blue-50 p-3 sm:p-4 dark:border-gray-700 dark:bg-blue-900/20">
|
||||
<h3 className="mb-2 font-medium text-blue-800 text-sm dark:text-blue-300">
|
||||
<div className="flex-shrink-0 border-border border-b bg-background p-3 text-foreground sm:p-4">
|
||||
<h3 className="mb-2 font-medium text-sm">
|
||||
How to Use Stack Architect
|
||||
</h3>
|
||||
<ul className="list-disc space-y-1 pl-5 text-blue-700 text-xs dark:text-blue-400">
|
||||
<ul className="list-disc space-y-1 pl-5 text-xs">
|
||||
<li>Use the sidebar to navigate between configuration sections.</li>
|
||||
<li>Select your preferred technologies in the main area.</li>
|
||||
<li>
|
||||
@@ -832,8 +852,8 @@ const StackArchitect = () => {
|
||||
)}
|
||||
|
||||
{showPresets && (
|
||||
<div className="flex-shrink-0 border-gray-300 border-b bg-amber-50 p-3 sm:p-4 dark:border-gray-700 dark:bg-amber-900/20">
|
||||
<h3 className="mb-2 font-medium text-amber-800 text-sm dark:text-amber-300">
|
||||
<div className="flex-shrink-0 border-border border-b bg-background p-3 sm:p-4">
|
||||
<h3 className="mb-2 font-medium text-foreground text-sm">
|
||||
Quick Start Presets
|
||||
</h3>
|
||||
<div className="grid grid-cols-1 gap-2 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4">
|
||||
@@ -842,12 +862,12 @@ const StackArchitect = () => {
|
||||
type="button"
|
||||
key={preset.id}
|
||||
onClick={() => applyPreset(preset.id)}
|
||||
className="rounded border border-amber-200 p-2 text-left transition-colors hover:bg-amber-100 dark:border-amber-700 dark:hover:bg-amber-800/30"
|
||||
className="rounded border border-border bg-card p-2 text-left transition-colors hover:bg-muted"
|
||||
>
|
||||
<div className="font-medium text-amber-700 text-sm dark:text-amber-300">
|
||||
<div className="font-medium text-foreground text-sm">
|
||||
{preset.name}
|
||||
</div>
|
||||
<div className="text-amber-600 text-xs dark:text-amber-400">
|
||||
<div className="text-muted-foreground text-xs">
|
||||
{preset.description}
|
||||
</div>
|
||||
</button>
|
||||
@@ -859,7 +879,7 @@ const StackArchitect = () => {
|
||||
<div className="flex-shrink-0 p-3 pb-0 font-mono sm:p-4 sm:pb-0">
|
||||
<div className="mb-3 flex flex-col justify-between gap-y-3 sm:flex-row sm:items-start">
|
||||
<label className="flex flex-col">
|
||||
<span className="mb-1 text-gray-600 text-xs dark:text-gray-400">
|
||||
<span className="mb-1 text-muted-foreground text-xs">
|
||||
Project Name:
|
||||
</span>
|
||||
<input
|
||||
@@ -870,22 +890,25 @@ const StackArchitect = () => {
|
||||
setStack((prev) => ({ ...prev, projectName: newValue }));
|
||||
setProjectNameError(validateProjectName(newValue));
|
||||
}}
|
||||
className={`w-full rounded border px-2 py-1 font-mono text-sm focus:outline-none sm:w-auto ${
|
||||
className={cn(
|
||||
"w-full rounded border bg-card px-2 py-1 font-mono text-sm focus:outline-none sm:w-auto",
|
||||
projectNameError
|
||||
? "border-red-500 bg-red-50 dark:border-red-500 dark:bg-red-900/20"
|
||||
: "border-gray-300 bg-gray-200 focus:border-blue-500 dark:border-gray-700 dark:bg-gray-800 dark:focus:border-blue-400"
|
||||
}`}
|
||||
? "border-destructive bg-destructive/10 text-destructive-foreground"
|
||||
: "border-border focus:border-primary",
|
||||
)}
|
||||
placeholder="my-better-t-app"
|
||||
/>
|
||||
{projectNameError && (
|
||||
<p className="mt-1 text-red-500 text-xs">{projectNameError}</p>
|
||||
<p className="mt-1 text-destructive text-xs">
|
||||
{projectNameError}
|
||||
</p>
|
||||
)}
|
||||
</label>
|
||||
<div className="flex flex-wrap gap-2">
|
||||
<button
|
||||
type="button"
|
||||
onClick={resetStack}
|
||||
className="flex items-center gap-1 rounded border border-gray-300 bg-gray-200 px-2 py-1 text-gray-700 text-xs transition-colors hover:bg-gray-300 dark:border-gray-700 dark:bg-gray-800 dark:text-gray-300 dark:hover:bg-gray-700"
|
||||
className="flex items-center gap-1 rounded border border-border bg-card px-2 py-1 text-muted-foreground text-xs transition-colors hover:bg-muted"
|
||||
title="Reset to defaults"
|
||||
>
|
||||
<RefreshCw className="h-3 w-3" />
|
||||
@@ -895,7 +918,7 @@ const StackArchitect = () => {
|
||||
<button
|
||||
type="button"
|
||||
onClick={loadSavedStack}
|
||||
className="flex items-center gap-1 rounded border border-blue-300 bg-blue-100 px-2 py-1 text-blue-700 text-xs transition-colors hover:bg-blue-200 dark:border-blue-700 dark:bg-blue-900/50 dark:text-blue-300 dark:hover:bg-blue-800/50"
|
||||
className="flex items-center gap-1 rounded border border-primary bg-primary/10 px-2 py-1 text-primary text-xs transition-colors hover:bg-primary/20"
|
||||
title="Load saved preferences"
|
||||
>
|
||||
<Settings className="h-3 w-3" />
|
||||
@@ -906,7 +929,7 @@ const StackArchitect = () => {
|
||||
id="save-stack-button"
|
||||
type="button"
|
||||
onClick={saveCurrentStack}
|
||||
className="flex items-center gap-1 rounded border border-green-300 bg-green-100 px-2 py-1 text-green-700 text-xs transition-colors hover:bg-green-200 dark:border-green-700 dark:bg-green-900/50 dark:text-green-300 dark:hover:bg-green-800/50"
|
||||
className="flex items-center gap-1 rounded border border-[--color-chart-4] bg-[--color-chart-4]/10 px-2 py-1 text-[--color-chart-4] text-xs transition-colors hover:bg-[--color-chart-4]/20"
|
||||
title="Save current preferences"
|
||||
>
|
||||
<Star className="h-3 w-3" />
|
||||
@@ -915,23 +938,21 @@ const StackArchitect = () => {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="relative mb-4 overflow-hidden rounded border border-gray-300 bg-gray-200 p-2 dark:border-gray-700 dark:bg-gray-800">
|
||||
<div className="relative mb-4 overflow-hidden rounded border border-border bg-card p-2">
|
||||
<div className="flex overflow-x-auto pr-10">
|
||||
<span className="mr-2 select-none text-green-600 dark:text-green-400">
|
||||
$
|
||||
</span>
|
||||
<code className="no-scrollbar inline-flex items-center overflow-x-auto whitespace-pre break-words text-gray-700 text-xs sm:text-sm dark:text-gray-300">
|
||||
<span className="mr-2 select-none text-[--color-chart-4]">$</span>
|
||||
<code className="no-scrollbar inline-flex items-center overflow-x-auto whitespace-pre break-words text-muted-foreground text-xs sm:text-sm">
|
||||
{command}
|
||||
</code>
|
||||
</div>
|
||||
<button
|
||||
type="button"
|
||||
onClick={copyToClipboard}
|
||||
className="-translate-y-1/2 absolute top-1/2 right-1 rounded p-1 text-gray-500 transition-colors hover:bg-gray-300 hover:text-gray-700 dark:text-gray-400 dark:hover:bg-gray-700 dark:hover:text-gray-200"
|
||||
className="-translate-y-1/2 absolute top-1/2 right-1 rounded p-1 text-muted-foreground transition-colors hover:bg-muted hover:text-foreground"
|
||||
title={copied ? "Copied!" : "Copy command"}
|
||||
>
|
||||
{copied ? (
|
||||
<Check className="h-4 w-4 text-green-500" />
|
||||
<Check className="h-4 w-4 text-[--color-chart-4]" />
|
||||
) : (
|
||||
<ClipboardCopy className="h-4 w-4" />
|
||||
)}
|
||||
@@ -960,9 +981,10 @@ const StackArchitect = () => {
|
||||
.map((tech) => (
|
||||
<span
|
||||
key={`${category}-${tech.id}`}
|
||||
className={`inline-flex items-center gap-1.5 rounded-full border px-2 py-0.5 text-xs ${getBadgeColors(
|
||||
category,
|
||||
)}`}
|
||||
className={cn(
|
||||
"inline-flex items-center gap-1.5 rounded-full border px-2 py-0.5 text-xs",
|
||||
getBadgeColors(category),
|
||||
)}
|
||||
>
|
||||
<TechIcon
|
||||
icon={tech.icon}
|
||||
@@ -994,9 +1016,10 @@ const StackArchitect = () => {
|
||||
return (
|
||||
<span
|
||||
key={`${category}-${tech.id}`}
|
||||
className={`inline-flex items-center gap-1.5 rounded-full border px-2 py-0.5 text-xs ${getBadgeColors(
|
||||
category,
|
||||
)}`}
|
||||
className={cn(
|
||||
"inline-flex items-center gap-1.5 rounded-full border px-2 py-0.5 text-xs",
|
||||
getBadgeColors(category),
|
||||
)}
|
||||
>
|
||||
<TechIcon
|
||||
icon={tech.icon}
|
||||
@@ -1012,23 +1035,24 @@ const StackArchitect = () => {
|
||||
</div>
|
||||
|
||||
<div className="flex flex-grow overflow-hidden">
|
||||
<nav className="hidden w-48 flex-shrink-0 overflow-y-auto border-gray-300 border-r bg-gray-200/50 p-2 md:flex dark:border-gray-700 dark:bg-gray-800/50">
|
||||
<nav className="hidden w-48 flex-shrink-0 overflow-y-auto border-border border-r p-2 md:flex">
|
||||
<ul className="space-y-1">
|
||||
{CATEGORY_ORDER.map((category) => (
|
||||
<li key={category}>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => handleSidebarClick(category)}
|
||||
className={`flex w-full items-center justify-between rounded px-2 py-1.5 text-left font-mono text-xs transition-colors ${
|
||||
className={cn(
|
||||
"flex w-full items-center justify-between rounded px-2 py-1.5 text-left font-mono text-xs transition-colors",
|
||||
activeCategory === category
|
||||
? "bg-blue-100 text-blue-700 dark:bg-blue-900/50 dark:text-blue-300"
|
||||
: "text-gray-600 hover:bg-gray-300/50 dark:text-gray-400 dark:hover:bg-gray-700/50"
|
||||
}`}
|
||||
? "bg-primary/10 text-primary"
|
||||
: "text-muted-foreground hover:bg-muted/50",
|
||||
)}
|
||||
>
|
||||
<span>{getCategoryDisplayName(category)}</span>
|
||||
{compatNotes[category]?.hasIssue && (
|
||||
<span title="Compatibility issue affects this section">
|
||||
<InfoIcon className="h-3 w-3 flex-shrink-0 text-orange-500 dark:text-orange-400" />
|
||||
<InfoIcon className="h-3 w-3 flex-shrink-0 text-[--color-chart-5]" />{" "}
|
||||
</span>
|
||||
)}
|
||||
</button>
|
||||
@@ -1037,170 +1061,175 @@ const StackArchitect = () => {
|
||||
</ul>
|
||||
</nav>
|
||||
|
||||
<main
|
||||
ref={contentRef}
|
||||
className="flex-grow overflow-y-auto scroll-smooth p-4"
|
||||
>
|
||||
{CATEGORY_ORDER.map((categoryKey) => {
|
||||
const categoryOptions =
|
||||
TECH_OPTIONS[categoryKey as keyof typeof TECH_OPTIONS] || [];
|
||||
const categoryDisplayName = getCategoryDisplayName(categoryKey);
|
||||
const notesInfo = compatNotes[categoryKey];
|
||||
<ScrollArea className="flex-1">
|
||||
<main
|
||||
ref={contentRef}
|
||||
className="flex-grow overflow-y-auto scroll-smooth p-4"
|
||||
>
|
||||
{CATEGORY_ORDER.map((categoryKey) => {
|
||||
const categoryOptions =
|
||||
TECH_OPTIONS[categoryKey as keyof typeof TECH_OPTIONS] || [];
|
||||
const categoryDisplayName = getCategoryDisplayName(categoryKey);
|
||||
const notesInfo = compatNotes[categoryKey];
|
||||
|
||||
return (
|
||||
<section
|
||||
ref={(el) => {
|
||||
sectionRefs.current[categoryKey] = el;
|
||||
}}
|
||||
key={categoryKey}
|
||||
id={`section-${categoryKey}`}
|
||||
className="mb-8 scroll-mt-4"
|
||||
>
|
||||
<div className="mb-3 flex items-center border-gray-300 border-b pb-2 text-gray-700 dark:border-gray-700 dark:text-gray-300">
|
||||
<Terminal className="mr-2 h-5 w-5 flex-shrink-0" />
|
||||
<h2 className="font-semibold text-base">
|
||||
{categoryDisplayName}
|
||||
</h2>
|
||||
</div>
|
||||
|
||||
{notesInfo?.notes && notesInfo.notes.length > 0 && (
|
||||
<div
|
||||
className={`mb-4 rounded-md border p-3 ${
|
||||
notesInfo.hasIssue
|
||||
? "border-orange-200 bg-orange-50 dark:border-orange-800 dark:bg-orange-900/20"
|
||||
: "border-blue-200 bg-blue-50 dark:border-blue-800 dark:bg-blue-900/20"
|
||||
}`}
|
||||
>
|
||||
<div
|
||||
className={`mb-1 flex items-center gap-2 font-medium text-xs sm:text-sm ${
|
||||
notesInfo.hasIssue
|
||||
? "text-orange-800 dark:text-orange-300"
|
||||
: "text-blue-800 dark:text-blue-300"
|
||||
}`}
|
||||
>
|
||||
<InfoIcon className="h-4 w-4 flex-shrink-0" />
|
||||
<span>
|
||||
{notesInfo.hasIssue
|
||||
? "Compatibility Issues / Auto-Adjustments"
|
||||
: "Notes"}
|
||||
</span>
|
||||
</div>
|
||||
<ul
|
||||
className={`list-inside list-disc space-y-1 text-xs ${
|
||||
notesInfo.hasIssue
|
||||
? "text-orange-700 dark:text-orange-400"
|
||||
: "text-blue-700 dark:text-blue-400"
|
||||
}`}
|
||||
>
|
||||
{notesInfo.notes.map((note, index) => (
|
||||
// biome-ignore lint/suspicious/noArrayIndexKey: <explanation>
|
||||
<li key={index}>{note}</li>
|
||||
))}
|
||||
</ul>
|
||||
return (
|
||||
<section
|
||||
ref={(el) => {
|
||||
sectionRefs.current[categoryKey] = el;
|
||||
}}
|
||||
key={categoryKey}
|
||||
id={`section-${categoryKey}`}
|
||||
className="mb-8 scroll-mt-4"
|
||||
>
|
||||
<div className="mb-3 flex items-center border-border border-b pb-2 text-muted-foreground">
|
||||
<Terminal className="mr-2 h-5 w-5 flex-shrink-0" />
|
||||
<h2 className="font-semibold text-base text-foreground">
|
||||
{categoryDisplayName}
|
||||
</h2>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="grid grid-cols-1 gap-3 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4">
|
||||
{categoryOptions.map((tech) => {
|
||||
let isSelected = false;
|
||||
const category = categoryKey as keyof StackState;
|
||||
|
||||
if (
|
||||
category === "addons" ||
|
||||
category === "examples" ||
|
||||
category === "frontend"
|
||||
) {
|
||||
isSelected = (
|
||||
(stack[category] as string[]) || []
|
||||
).includes(tech.id);
|
||||
} else {
|
||||
isSelected = stack[category] === tech.id;
|
||||
}
|
||||
|
||||
const disabledReason = getDisabledReason(
|
||||
categoryKey as keyof typeof TECH_OPTIONS,
|
||||
tech.id,
|
||||
);
|
||||
const isDisabled = !!disabledReason && !isSelected;
|
||||
|
||||
return (
|
||||
<motion.div
|
||||
key={tech.id}
|
||||
className={`relative rounded border p-3 transition-all ${
|
||||
isDisabled
|
||||
? "cursor-not-allowed opacity-60"
|
||||
: "cursor-pointer"
|
||||
} ${
|
||||
isSelected
|
||||
? "border-blue-400 bg-blue-100 ring-1 ring-blue-300 dark:border-blue-600 dark:bg-blue-900/40 dark:ring-blue-700"
|
||||
: `border-gray-300 dark:border-gray-700 ${
|
||||
!isDisabled
|
||||
? "hover:border-gray-400 hover:bg-gray-200/50 dark:hover:border-gray-600 dark:hover:bg-gray-800/50"
|
||||
: ""
|
||||
}`
|
||||
}`}
|
||||
title={
|
||||
isDisabled
|
||||
? (disabledReason ?? "Option disabled")
|
||||
: tech.description
|
||||
}
|
||||
whileHover={!isDisabled ? { scale: 1.02 } : undefined}
|
||||
whileTap={!isDisabled ? { scale: 0.98 } : undefined}
|
||||
onClick={() =>
|
||||
!isDisabled &&
|
||||
handleTechSelect(
|
||||
categoryKey as keyof typeof TECH_OPTIONS,
|
||||
tech.id,
|
||||
)
|
||||
}
|
||||
>
|
||||
<div className="flex items-start">
|
||||
<div className="mt-1 mr-3 flex-shrink-0">
|
||||
{isSelected ? (
|
||||
<CircleCheck className="h-5 w-5 text-blue-600 dark:text-blue-400" />
|
||||
) : (
|
||||
<Circle className="h-5 w-5 text-gray-400 dark:text-gray-600" />
|
||||
)}
|
||||
</div>
|
||||
<div className="flex-grow">
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex items-center">
|
||||
<TechIcon
|
||||
icon={tech.icon}
|
||||
name={tech.name}
|
||||
className="mr-2 h-5 w-5"
|
||||
/>
|
||||
<span
|
||||
className={`font-medium ${
|
||||
isSelected
|
||||
? "text-blue-800 dark:text-blue-300"
|
||||
: "text-gray-800 dark:text-gray-200"
|
||||
} text-sm`}
|
||||
>
|
||||
{tech.name}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<p className="mt-1 text-gray-600 text-xs dark:text-gray-400">
|
||||
{tech.description}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
{tech.default && !isSelected && !isDisabled && (
|
||||
<span className="absolute top-1 right-1 ml-2 flex-shrink-0 rounded bg-gray-300 px-1 py-0.5 text-[10px] text-gray-600 dark:bg-gray-700 dark:text-gray-400">
|
||||
Default
|
||||
</span>
|
||||
{notesInfo?.notes && notesInfo.notes.length > 0 && (
|
||||
<div
|
||||
className={cn(
|
||||
"mb-4 rounded-md border p-3",
|
||||
notesInfo.hasIssue
|
||||
? "border-[--color-chart-5] bg-[--color-chart-5]/10"
|
||||
: "border-primary bg-primary/10",
|
||||
)}
|
||||
>
|
||||
<div
|
||||
className={cn(
|
||||
"mb-1 flex items-center gap-2 font-medium text-xs sm:text-sm",
|
||||
notesInfo.hasIssue
|
||||
? "text-[--color-chart-5]"
|
||||
: "text-primary",
|
||||
)}
|
||||
</motion.div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
})}
|
||||
>
|
||||
<InfoIcon className="h-4 w-4 flex-shrink-0" />
|
||||
<span>
|
||||
{notesInfo.hasIssue
|
||||
? "Compatibility Issues / Auto-Adjustments"
|
||||
: "Notes"}
|
||||
</span>
|
||||
</div>
|
||||
<ul
|
||||
className={cn(
|
||||
"list-inside list-disc space-y-1 text-xs",
|
||||
notesInfo.hasIssue
|
||||
? "text-[--color-chart-5]/90"
|
||||
: "text-primary/90",
|
||||
)}
|
||||
>
|
||||
{notesInfo.notes.map((note, index) => (
|
||||
// biome-ignore lint/suspicious/noArrayIndexKey: <explanation>
|
||||
<li key={index}>{note}</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="h-10" />
|
||||
</main>
|
||||
<div className="grid grid-cols-1 gap-3 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4">
|
||||
{categoryOptions.map((tech) => {
|
||||
let isSelected = false;
|
||||
const category = categoryKey as keyof StackState;
|
||||
|
||||
if (
|
||||
category === "addons" ||
|
||||
category === "examples" ||
|
||||
category === "frontend"
|
||||
) {
|
||||
isSelected = (
|
||||
(stack[category] as string[]) || []
|
||||
).includes(tech.id);
|
||||
} else {
|
||||
isSelected = stack[category] === tech.id;
|
||||
}
|
||||
|
||||
const disabledReason = getDisabledReason(
|
||||
categoryKey as keyof typeof TECH_OPTIONS,
|
||||
tech.id,
|
||||
);
|
||||
const isDisabled = !!disabledReason && !isSelected;
|
||||
|
||||
return (
|
||||
<motion.div
|
||||
key={tech.id}
|
||||
className={cn(
|
||||
"relative rounded border p-3 transition-all",
|
||||
isDisabled
|
||||
? "cursor-not-allowed opacity-60"
|
||||
: "cursor-pointer",
|
||||
isSelected
|
||||
? "border-primary bg-primary/10 ring-1 ring-primary"
|
||||
: `border-border ${
|
||||
!isDisabled
|
||||
? "hover:border-muted hover:bg-muted"
|
||||
: ""
|
||||
}`,
|
||||
)}
|
||||
title={
|
||||
isDisabled
|
||||
? (disabledReason ?? "Option disabled")
|
||||
: tech.description
|
||||
}
|
||||
whileHover={!isDisabled ? { scale: 1.02 } : undefined}
|
||||
whileTap={!isDisabled ? { scale: 0.98 } : undefined}
|
||||
onClick={() =>
|
||||
!isDisabled &&
|
||||
handleTechSelect(
|
||||
categoryKey as keyof typeof TECH_OPTIONS,
|
||||
tech.id,
|
||||
)
|
||||
}
|
||||
>
|
||||
<div className="flex items-start">
|
||||
<div className="mt-1 mr-3 flex-shrink-0">
|
||||
{isSelected ? (
|
||||
<CircleCheck className="h-5 w-5 text-primary" />
|
||||
) : (
|
||||
<Circle className="h-5 w-5 text-muted-foreground" />
|
||||
)}
|
||||
</div>
|
||||
<div className="flex-grow">
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex items-center">
|
||||
<TechIcon
|
||||
icon={tech.icon}
|
||||
name={tech.name}
|
||||
className="mr-2 h-5 w-5"
|
||||
/>
|
||||
<span
|
||||
className={cn(
|
||||
"font-medium text-sm",
|
||||
isSelected
|
||||
? "text-primary"
|
||||
: "text-foreground",
|
||||
)}
|
||||
>
|
||||
{tech.name}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<p className="mt-1 text-muted-foreground text-xs">
|
||||
{tech.description}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
{tech.default && !isSelected && !isDisabled && (
|
||||
<span className="absolute top-1 right-1 ml-2 flex-shrink-0 rounded bg-muted px-1 py-0.5 text-[10px] text-muted-foreground">
|
||||
Default
|
||||
</span>
|
||||
)}
|
||||
</motion.div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
})}
|
||||
<div className="h-10" />
|
||||
</main>
|
||||
</ScrollArea>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -1,275 +0,0 @@
|
||||
import { motion } from "motion/react";
|
||||
import React, { useState } from "react";
|
||||
|
||||
interface TechItem {
|
||||
name: string;
|
||||
description: string;
|
||||
category: "frontend" | "backend" | "database" | "tooling" | "core";
|
||||
}
|
||||
|
||||
const techStack: TechItem[] = [
|
||||
{
|
||||
name: "TypeScript",
|
||||
description: "Type safety across the entire stack",
|
||||
category: "core",
|
||||
},
|
||||
{
|
||||
name: "React",
|
||||
description: "JavaScript library for user interfaces",
|
||||
category: "frontend",
|
||||
},
|
||||
{
|
||||
name: "TanStack Router",
|
||||
description: "Type-safe routing with file-based routes",
|
||||
category: "frontend",
|
||||
},
|
||||
{
|
||||
name: "TanStack Query",
|
||||
description: "Powerful data synchronization",
|
||||
category: "frontend",
|
||||
},
|
||||
{
|
||||
name: "Tailwind CSS",
|
||||
description: "Utility-first CSS framework",
|
||||
category: "frontend",
|
||||
},
|
||||
{
|
||||
name: "shadcn/ui",
|
||||
description: "Re-usable UI components",
|
||||
category: "frontend",
|
||||
},
|
||||
{
|
||||
name: "Hono",
|
||||
description: "Ultrafast web framework",
|
||||
category: "backend",
|
||||
},
|
||||
{
|
||||
name: "tRPC",
|
||||
description: "End-to-end type-safe APIs",
|
||||
category: "backend",
|
||||
},
|
||||
{
|
||||
name: "Better-Auth",
|
||||
description: "Modern authentication solution",
|
||||
category: "backend",
|
||||
},
|
||||
{
|
||||
name: "Drizzle ORM",
|
||||
description: "TypeScript-first ORM",
|
||||
category: "database",
|
||||
},
|
||||
{
|
||||
name: "Prisma",
|
||||
description: "Next-generation ORM",
|
||||
category: "database",
|
||||
},
|
||||
{
|
||||
name: "SQLite + Turso",
|
||||
description: "Serverless SQLite with edge replication",
|
||||
category: "database",
|
||||
},
|
||||
{
|
||||
name: "PostgreSQL",
|
||||
description: "Advanced open-source relational database",
|
||||
category: "database",
|
||||
},
|
||||
{
|
||||
name: "Biome",
|
||||
description: "Fast formatter and linter",
|
||||
category: "tooling",
|
||||
},
|
||||
{
|
||||
name: "Husky",
|
||||
description: "Git hooks made easy",
|
||||
category: "tooling",
|
||||
},
|
||||
{
|
||||
name: "PWA",
|
||||
description: "Progressive Web App support",
|
||||
category: "tooling",
|
||||
},
|
||||
{
|
||||
name: "Tauri",
|
||||
description: "Build desktop and mobile apps with web tech",
|
||||
category: "tooling",
|
||||
},
|
||||
{
|
||||
name: "Docker",
|
||||
description: "Containerized deployments",
|
||||
category: "tooling",
|
||||
},
|
||||
{
|
||||
name: "Turborepo",
|
||||
description: "Optimized build system for monorepos",
|
||||
category: "core",
|
||||
},
|
||||
];
|
||||
|
||||
const categoryIcons = {
|
||||
frontend: "🖥️",
|
||||
backend: "⚙️",
|
||||
database: "🗄️",
|
||||
tooling: "🔧",
|
||||
core: "⚡",
|
||||
};
|
||||
|
||||
export default function TechShowcase() {
|
||||
const [activeCategory, setActiveCategory] = useState<string | null>(null);
|
||||
|
||||
const categories = Array.from(
|
||||
new Set(techStack.map((item) => item.category)),
|
||||
);
|
||||
|
||||
const filteredTech = activeCategory
|
||||
? techStack.filter((tech) => tech.category === activeCategory)
|
||||
: techStack;
|
||||
|
||||
const groupedTech = !activeCategory
|
||||
? categories.map((category) => ({
|
||||
category,
|
||||
items: techStack.filter((tech) => tech.category === category),
|
||||
}))
|
||||
: null;
|
||||
|
||||
return (
|
||||
<section className="mx-auto w-full max-w-6xl px-4 py-16">
|
||||
<div className="mb-8 flex flex-wrap justify-center gap-3">
|
||||
<button
|
||||
type="button"
|
||||
className={`rounded-md px-4 py-2 font-mono text-sm transition-colors ${
|
||||
activeCategory === null
|
||||
? "bg-blue-500 text-white dark:text-white"
|
||||
: "bg-gray-200 text-gray-800 hover:bg-gray-300 dark:bg-gray-800 dark:text-gray-300 dark:hover:bg-gray-700"
|
||||
}`}
|
||||
onClick={() => setActiveCategory(null)}
|
||||
>
|
||||
all technologies
|
||||
</button>
|
||||
|
||||
{categories.map((category) => (
|
||||
<button
|
||||
key={category}
|
||||
type="button"
|
||||
className={`rounded-md px-4 py-2 font-mono text-sm transition-colors ${
|
||||
activeCategory === category
|
||||
? "bg-blue-500 text-white dark:text-white"
|
||||
: "bg-gray-200 text-gray-800 hover:bg-gray-300 dark:bg-gray-800 dark:text-gray-300 dark:hover:bg-gray-700"
|
||||
}`}
|
||||
onClick={() => setActiveCategory(category)}
|
||||
>
|
||||
{categoryIcons[category as keyof typeof categoryIcons]} {category}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{activeCategory && (
|
||||
<div className="grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3">
|
||||
{filteredTech.map((tech) => (
|
||||
<motion.div
|
||||
key={tech.name}
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
exit={{ opacity: 0 }}
|
||||
className="rounded-lg border border-gray-200 bg-white/50 p-4 backdrop-blur-sm transition-colors hover:border-blue-500/30 dark:border-gray-800 dark:bg-gray-900/50"
|
||||
>
|
||||
<h3 className="mb-2 font-semibold text-gray-900 text-lg dark:text-white">
|
||||
{tech.name}
|
||||
</h3>
|
||||
<p className="text-gray-600 text-sm dark:text-gray-400">
|
||||
{tech.description}
|
||||
</p>
|
||||
<div className="mt-3 font-mono text-gray-500 text-xs dark:text-gray-500">
|
||||
{tech.category === "tooling" || tech.category === "database" ? (
|
||||
<span>
|
||||
{tech.name === "Drizzle ORM" ||
|
||||
tech.name === "Prisma" ||
|
||||
tech.name === "SQLite + Turso" ||
|
||||
tech.name === "PostgreSQL" ? (
|
||||
<code>--{tech.name.toLowerCase().split(" ")[0]}</code>
|
||||
) : (
|
||||
<code>--{tech.name.toLowerCase()}</code>
|
||||
)}
|
||||
</span>
|
||||
) : (
|
||||
<span>Included by default</span>
|
||||
)}
|
||||
</div>
|
||||
</motion.div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{!activeCategory && groupedTech && (
|
||||
<div className="space-y-12">
|
||||
{groupedTech.map((group) => (
|
||||
<div key={group.category} className="relative">
|
||||
<div className="mb-4 flex items-center">
|
||||
<div className="mr-3 flex h-6 w-6 items-center justify-center rounded-full bg-blue-100 text-blue-500 dark:bg-blue-500/20 dark:text-blue-300">
|
||||
{categoryIcons[group.category as keyof typeof categoryIcons]}
|
||||
</div>
|
||||
<h2 className="font-mono font-semibold text-blue-500 text-xl dark:text-blue-300">
|
||||
{group.category}/
|
||||
</h2>
|
||||
</div>
|
||||
|
||||
<div className="ml-3 border-gray-200 border-l-2 pb-4 pl-6 dark:border-gray-800">
|
||||
<div className="grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3">
|
||||
{group.items.map((tech) => (
|
||||
<motion.div
|
||||
key={tech.name}
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: 1 }}
|
||||
className="rounded-md border border-gray-200 bg-white/30 p-4 backdrop-blur-sm transition-colors hover:bg-gray-100/50 dark:border-gray-800 dark:bg-gray-900/30 dark:hover:bg-gray-800/30"
|
||||
>
|
||||
<div className="flex items-start justify-between">
|
||||
<h3 className="font-semibold text-gray-900 text-lg dark:text-white">
|
||||
{tech.name}
|
||||
</h3>
|
||||
<div className="rounded bg-gray-100 px-2 py-1 font-mono text-gray-600 text-xs dark:bg-gray-800 dark:text-gray-400">
|
||||
{group.category === "tooling" ||
|
||||
tech.category === "database"
|
||||
? "optional"
|
||||
: "core"}
|
||||
</div>
|
||||
</div>
|
||||
<p className="mt-2 text-gray-600 text-sm dark:text-gray-400">
|
||||
{tech.description}
|
||||
</p>
|
||||
<div className="mt-3 flex items-center justify-between border-gray-200 border-t pt-2 dark:border-gray-800">
|
||||
<span className="font-mono text-gray-500 text-xs dark:text-gray-500">
|
||||
{group.category === "tooling" ||
|
||||
tech.category === "database"
|
||||
? tech.name === "Drizzle ORM" ||
|
||||
tech.name === "Prisma" ||
|
||||
tech.name === "SQLite + Turso" ||
|
||||
tech.name === "PostgreSQL"
|
||||
? `--${tech.name.toLowerCase().split(" ")[0]}`
|
||||
: `--${tech.name.toLowerCase()}`
|
||||
: "included by default"}
|
||||
</span>
|
||||
<span
|
||||
className={`h-2 w-2 ${group.category === "tooling" || tech.category === "database" ? "bg-yellow-500" : "bg-green-500"} rounded-full`}
|
||||
/>
|
||||
</div>
|
||||
</motion.div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="mt-10 text-center">
|
||||
<div className="inline-block rounded-md border border-gray-200 bg-white/50 px-5 py-3 font-mono text-gray-600 text-sm backdrop-blur-sm dark:border-gray-800 dark:bg-gray-900/50 dark:text-gray-400">
|
||||
<span className="text-green-500 dark:text-green-400">$</span> npx
|
||||
create-better-t-stack
|
||||
<span className="text-blue-500 dark:text-blue-400">
|
||||
{" "}
|
||||
—your-options
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
"use client";
|
||||
|
||||
import { cn } from "@/lib/utils";
|
||||
import { ChevronLeft, ChevronRight } from "lucide-react";
|
||||
import { motion } from "motion/react";
|
||||
import { useEffect, useState } from "react";
|
||||
@@ -94,11 +95,10 @@ export default function Testimonials() {
|
||||
className="relative"
|
||||
>
|
||||
<h2 className="font-bold font-mono text-xl tracking-tight sm:text-2xl md:text-3xl lg:text-4xl xl:text-5xl">
|
||||
<span className="border-blue-500 border-b-2 pb-1 text-gray-900 dark:text-blue-100">
|
||||
<span className="border-primary border-b-2 pb-1 text-foreground dark:text-primary">
|
||||
Developer Feedback
|
||||
</span>
|
||||
</h2>
|
||||
<div className="-inset-x-1/4 -inset-y-1/2 -z-10 absolute bg-gradient-to-r from-blue-300/0 via-blue-300/10 to-blue-300/0 blur-3xl dark:from-blue-800/0 dark:via-blue-800/10 dark:to-blue-800/0" />
|
||||
</motion.div>
|
||||
|
||||
<motion.p
|
||||
@@ -106,7 +106,7 @@ export default function Testimonials() {
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true, margin: "-100px" }}
|
||||
transition={{ duration: 0.5, delay: 0.2 }}
|
||||
className="mx-auto max-w-3xl font-mono text-base text-gray-700 leading-relaxed sm:text-lg md:text-xl dark:text-gray-300"
|
||||
className="mx-auto max-w-3xl font-mono text-base text-muted-foreground leading-relaxed sm:text-lg md:text-xl"
|
||||
>
|
||||
what devs are saying about Better-T-Stack
|
||||
</motion.p>
|
||||
@@ -119,14 +119,14 @@ export default function Testimonials() {
|
||||
transition={{ duration: 0.5, delay: 0.3 }}
|
||||
className="relative mt-4 sm:mt-8"
|
||||
>
|
||||
<div className="overflow-hidden rounded-xl border border-gray-300 bg-gray-100 shadow-xl dark:border-gray-700 dark:bg-gray-900">
|
||||
<div className="flex items-center justify-between bg-gray-200 px-2 py-2 sm:px-4 dark:bg-gray-800">
|
||||
<div className="overflow-hidden rounded-xl border border-border bg-card shadow-xl">
|
||||
<div className="flex items-center justify-between bg-muted px-2 py-2 sm:px-4">
|
||||
<div className="flex space-x-1 sm:space-x-2">
|
||||
<div className="h-2 w-2 rounded-full bg-red-500 sm:h-3 sm:w-3" />
|
||||
<div className="h-2 w-2 rounded-full bg-yellow-500 sm:h-3 sm:w-3" />
|
||||
<div className="h-2 w-2 rounded-full bg-green-500 sm:h-3 sm:w-3" />
|
||||
</div>
|
||||
<div className="font-mono text-[10px] text-gray-600 sm:text-xs dark:text-gray-400">
|
||||
<div className="font-mono text-[10px] text-muted-foreground sm:text-xs">
|
||||
Developer Feedback
|
||||
</div>
|
||||
<div className="flex items-center gap-1 sm:gap-2">
|
||||
@@ -134,7 +134,7 @@ export default function Testimonials() {
|
||||
whileHover={{ scale: 1.05 }}
|
||||
whileTap={{ scale: 0.95 }}
|
||||
onClick={handlePrev}
|
||||
className="flex h-5 w-5 items-center justify-center rounded bg-gray-300 text-gray-700 transition-colors hover:bg-gray-400 sm:h-6 sm:w-6 dark:bg-gray-700 dark:text-gray-300 dark:hover:bg-gray-600"
|
||||
className="flex h-5 w-5 items-center justify-center rounded bg-secondary text-secondary-foreground transition-colors hover:bg-muted sm:h-6 sm:w-6"
|
||||
title="Previous testimonials"
|
||||
aria-label="Previous testimonials"
|
||||
>
|
||||
@@ -145,7 +145,7 @@ export default function Testimonials() {
|
||||
whileHover={{ scale: 1.05 }}
|
||||
whileTap={{ scale: 0.95 }}
|
||||
onClick={handleNext}
|
||||
className="flex h-5 w-5 items-center justify-center rounded bg-gray-300 text-gray-700 transition-colors hover:bg-gray-400 sm:h-6 sm:w-6 dark:bg-gray-700 dark:text-gray-300 dark:hover:bg-gray-600"
|
||||
className="flex h-5 w-5 items-center justify-center rounded bg-secondary text-secondary-foreground transition-colors hover:bg-muted sm:h-6 sm:w-6"
|
||||
title="Next testimonials"
|
||||
aria-label="Next testimonials"
|
||||
>
|
||||
@@ -162,9 +162,9 @@ export default function Testimonials() {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center justify-between border-gray-300 border-t bg-gray-200 px-2 py-2 sm:p-3 dark:border-gray-700 dark:bg-gray-800">
|
||||
<div className="flex items-center justify-between border-border border-t bg-muted px-2 py-2 sm:p-3">
|
||||
<div className="flex items-center">
|
||||
<span className="text-[10px] text-gray-700 sm:text-xs dark:text-gray-300">
|
||||
<span className="text-[10px] text-muted-foreground sm:text-xs">
|
||||
{currentPage}/{totalPages}
|
||||
</span>
|
||||
</div>
|
||||
@@ -172,24 +172,33 @@ export default function Testimonials() {
|
||||
<div className="flex items-center gap-2 sm:gap-3">
|
||||
<div className="flex items-center gap-1">
|
||||
{Array.from({ length: Math.min(totalPages, 5) }).map((_, i) => {
|
||||
const isActive = i === Math.min(currentPage - 1, 4);
|
||||
const pageNum =
|
||||
totalPages <= 5
|
||||
? i
|
||||
: currentPage <= 3
|
||||
? i
|
||||
: currentPage >= totalPages - 1
|
||||
? totalPages - 5 + i
|
||||
: currentPage - 3 + i;
|
||||
const isActive = pageNum === currentPage - 1;
|
||||
return (
|
||||
<button
|
||||
type="button"
|
||||
// biome-ignore lint/suspicious/noArrayIndexKey: pagination indicator
|
||||
// biome-ignore lint/suspicious/noArrayIndexKey: <explanation>
|
||||
key={i}
|
||||
onClick={() => setStartIndex(i * tweetsPerPage)}
|
||||
className={`h-1 w-1 rounded-full transition-colors sm:h-1.5 sm:w-1.5 ${
|
||||
onClick={() => setStartIndex(pageNum * tweetsPerPage)}
|
||||
className={cn(
|
||||
"h-1 w-1 rounded-full transition-colors sm:h-1.5 sm:w-1.5",
|
||||
isActive
|
||||
? "bg-blue-500"
|
||||
: "bg-gray-400 hover:bg-gray-500 dark:bg-gray-600"
|
||||
}`}
|
||||
aria-label={`Go to page ${i + 1}`}
|
||||
? "bg-primary"
|
||||
: "bg-muted-foreground/50 hover:bg-muted-foreground/70",
|
||||
)}
|
||||
aria-label={`Go to page ${pageNum + 1}`}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
{totalPages > 5 && (
|
||||
<span className="text-[8px] text-gray-500 sm:text-[10px] dark:text-gray-400">
|
||||
<span className="text-[8px] text-muted-foreground sm:text-[10px]">
|
||||
...
|
||||
</span>
|
||||
)}
|
||||
@@ -197,8 +206,6 @@ export default function Testimonials() {
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="-z-10 absolute inset-0 bg-gradient-to-r from-blue-500/5 via-transparent to-indigo-500/5 blur-3xl" />
|
||||
</motion.div>
|
||||
</section>
|
||||
);
|
||||
|
||||
@@ -2,7 +2,7 @@ import type { ReactNode } from "react";
|
||||
|
||||
export default function Layout({ children }: { children: ReactNode }) {
|
||||
return (
|
||||
<main className="relative z-10 grid min-h-svh grid-cols-1 grid-rows-[auto_1fr_auto] overflow-hidden bg-gradient-to-b from-gray-50 to-gray-100 transition-color duration-300 dark:from-gray-950 dark:to-black">
|
||||
<main className="relative z-10 grid min-h-svh grid-cols-1 grid-rows-[auto_1fr_auto] overflow-hidden">
|
||||
{children}
|
||||
</main>
|
||||
);
|
||||
|
||||
@@ -5,7 +5,7 @@ import StackArchitect from "../_components/StackArchitech";
|
||||
|
||||
export default function FullScreenStackArchitect() {
|
||||
return (
|
||||
<div className="flex h-svh flex-col overflow-y-auto bg-gradient-to-b from-white to-gray-50 dark:from-gray-950 dark:to-gray-900">
|
||||
<div className="flex h-svh flex-col bg-background">
|
||||
<motion.div
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: 1 }}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
"use client";
|
||||
import ShinyText from "@/app/(home)/_components/ShinyText";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { motion } from "motion/react";
|
||||
import React from "react";
|
||||
import BackgroundGradients from "./_components/BackgroundGradients";
|
||||
import CodeContainer from "./_components/CodeContainer";
|
||||
import CustomizableSection from "./_components/CustomizableSection";
|
||||
import Footer from "./_components/Footer";
|
||||
@@ -48,9 +48,7 @@ export default function HomePage() {
|
||||
return (
|
||||
<>
|
||||
<Navbar />
|
||||
<main className="flex min-h-screen flex-col items-center justify-start overflow-x-hidden px-0 pt-24 pb-10 sm:px-4 sm:pb-16 md:px-8 md:pt-28 lg:pt-32">
|
||||
<BackgroundGradients />
|
||||
|
||||
<main className="flex min-h-screen flex-col items-center justify-start overflow-x-hidden bg-background px-0 pt-24 pb-10 sm:px-4 sm:pb-16 md:px-8 md:pt-28 lg:pt-32">
|
||||
<motion.div
|
||||
className="relative z-10 mx-auto mb-16 max-w-5xl text-center sm:mb-20"
|
||||
initial="hidden"
|
||||
@@ -63,7 +61,7 @@ export default function HomePage() {
|
||||
className="font-bold font-mono text-4xl xs:text-5xl tracking-tight sm:text-6xl md:text-7xl"
|
||||
variants={itemVariants}
|
||||
>
|
||||
<span className="border-blue-500 border-b-2 pb-1 text-gray-900 dark:text-blue-100">
|
||||
<span className="border-primary border-b-2 pb-1 text-foreground dark:text-primary">
|
||||
Better-T Stack
|
||||
</span>
|
||||
</motion.h1>
|
||||
@@ -73,7 +71,7 @@ export default function HomePage() {
|
||||
</motion.div>
|
||||
|
||||
<motion.p
|
||||
className="max-w-2xl px-1 font-mono text-gray-600 text-lg sm:text-xl dark:text-gray-300"
|
||||
className="max-w-2xl px-1 font-mono text-lg text-muted-foreground sm:text-xl"
|
||||
variants={itemVariants}
|
||||
>
|
||||
A modern CLI tool for scaffolding end-to-end type-safe
|
||||
@@ -92,7 +90,7 @@ export default function HomePage() {
|
||||
<ShinyText
|
||||
text="Type-safe. Modern. Minimal. Fast."
|
||||
speed={3}
|
||||
className="font-mono text-gray-600 text-xs xs:text-sm sm:text-base dark:text-gray-400"
|
||||
className="font-mono text-muted-foreground text-xs xs:text-sm sm:text-base"
|
||||
/>
|
||||
</motion.div>
|
||||
</div>
|
||||
@@ -119,15 +117,20 @@ export default function HomePage() {
|
||||
<div className="relative mx-auto max-w-5xl">
|
||||
<div className="flex items-center justify-center">
|
||||
<div className="hidden w-1/3 items-center sm:flex">
|
||||
<div className="h-px flex-grow bg-gradient-to-r from-transparent via-blue-500/30 to-blue-500/50" />
|
||||
<div className="h-2 w-2 rounded-full bg-blue-500/60 shadow-sm" />
|
||||
<div className="h-px flex-grow bg-gradient-to-r from-transparent via-primary/30 to-primary/50" />
|
||||
|
||||
<div className="h-2 w-2 rounded-full bg-primary/60 shadow-sm" />
|
||||
</div>
|
||||
|
||||
<div className="px-4 sm:px-6">
|
||||
<div className="flex h-8 w-8 items-center justify-center rounded-full border border-blue-500/80 bg-white shadow-md sm:h-9 sm:w-9 dark:border-blue-400/70 dark:bg-gray-900">
|
||||
<div
|
||||
className={cn(
|
||||
"flex h-8 w-8 items-center justify-center rounded-full border border-primary/80 bg-card shadow-md sm:h-9 sm:w-9",
|
||||
)}
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
className="h-4 w-4 text-blue-500 sm:h-5 sm:w-5 dark:text-blue-400"
|
||||
className="h-4 w-4 text-primary sm:h-5 sm:w-5"
|
||||
viewBox="0 0 20 20"
|
||||
fill="currentColor"
|
||||
>
|
||||
@@ -142,11 +145,13 @@ export default function HomePage() {
|
||||
</div>
|
||||
|
||||
<div className="hidden w-1/3 items-center sm:flex">
|
||||
<div className="h-2 w-2 rounded-full bg-blue-500/60 shadow-sm" />
|
||||
<div className="h-px flex-grow bg-gradient-to-l from-transparent via-blue-500/30 to-blue-500/50" />
|
||||
<div className="h-2 w-2 rounded-full bg-primary/60 shadow-sm" />
|
||||
|
||||
<div className="h-px flex-grow bg-gradient-to-l from-transparent via-primary/30 to-primary/50" />
|
||||
</div>
|
||||
</div>
|
||||
<div className="mt-6 h-px w-full bg-gradient-to-r from-transparent via-blue-500/30 to-transparent sm:hidden" />
|
||||
|
||||
<div className="mt-6 h-px w-full bg-gradient-to-r from-transparent via-primary/30 to-transparent sm:hidden" />
|
||||
</div>
|
||||
</motion.div>
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
@import "tailwindcss";
|
||||
@import "fumadocs-ui/css/ocean.css";
|
||||
/* @import "fumadocs-ui/css/ocean.css"; */
|
||||
@import "fumadocs-ui/css/preset.css";
|
||||
@import "tw-animate-css";
|
||||
|
||||
@source '../../../../node_modules/fumadocs-ui/dist/**/*.js';
|
||||
|
||||
@@ -12,6 +13,7 @@
|
||||
|
||||
.react-tweet-theme {
|
||||
--tweet-container-margin: 0 !important;
|
||||
@apply !bg-background;
|
||||
}
|
||||
|
||||
.shiny-text {
|
||||
@@ -78,3 +80,199 @@
|
||||
-ms-overflow-style: none;
|
||||
scrollbar-width: none;
|
||||
}
|
||||
|
||||
@theme inline {
|
||||
--radius-sm: calc(var(--radius) - 4px);
|
||||
--radius-md: calc(var(--radius) - 2px);
|
||||
--radius-lg: var(--radius);
|
||||
--radius-xl: calc(var(--radius) + 4px);
|
||||
--color-background: var(--background);
|
||||
--color-foreground: var(--foreground);
|
||||
--color-card: var(--card);
|
||||
--color-card-foreground: var(--card-foreground);
|
||||
--color-popover: var(--popover);
|
||||
--color-popover-foreground: var(--popover-foreground);
|
||||
--color-primary: var(--primary);
|
||||
--color-primary-foreground: var(--primary-foreground);
|
||||
--color-secondary: var(--secondary);
|
||||
--color-secondary-foreground: var(--secondary-foreground);
|
||||
--color-muted: var(--muted);
|
||||
--color-muted-foreground: var(--muted-foreground);
|
||||
--color-accent: var(--accent);
|
||||
--color-accent-foreground: var(--accent-foreground);
|
||||
--color-destructive: var(--destructive);
|
||||
--color-border: var(--border);
|
||||
--color-input: var(--input);
|
||||
--color-ring: var(--ring);
|
||||
--color-chart-1: var(--chart-1);
|
||||
--color-chart-2: var(--chart-2);
|
||||
--color-chart-3: var(--chart-3);
|
||||
--color-chart-4: var(--chart-4);
|
||||
--color-chart-5: var(--chart-5);
|
||||
--color-sidebar: var(--sidebar);
|
||||
--color-sidebar-foreground: var(--sidebar-foreground);
|
||||
--color-sidebar-primary: var(--sidebar-primary);
|
||||
--color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
|
||||
--color-sidebar-accent: var(--sidebar-accent);
|
||||
--color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
|
||||
--color-sidebar-border: var(--sidebar-border);
|
||||
--color-sidebar-ring: var(--sidebar-ring);
|
||||
--font-sans: Montserrat, sans-serif;
|
||||
--font-mono: Fira Code, monospace;
|
||||
--font-serif: Georgia, serif;
|
||||
--radius: 0.35rem;
|
||||
--tracking-tighter: calc(var(--tracking-normal) - 0.05em);
|
||||
--tracking-tight: calc(var(--tracking-normal) - 0.025em);
|
||||
--tracking-wide: calc(var(--tracking-normal) + 0.025em);
|
||||
--tracking-wider: calc(var(--tracking-normal) + 0.05em);
|
||||
--tracking-widest: calc(var(--tracking-normal) + 0.1em);
|
||||
--tracking-normal: var(--tracking-normal);
|
||||
--shadow-2xl: var(--shadow-2xl);
|
||||
--shadow-xl: var(--shadow-xl);
|
||||
--shadow-lg: var(--shadow-lg);
|
||||
--shadow-md: var(--shadow-md);
|
||||
--shadow: var(--shadow);
|
||||
--shadow-sm: var(--shadow-sm);
|
||||
--shadow-xs: var(--shadow-xs);
|
||||
--shadow-2xs: var(--shadow-2xs);
|
||||
--spacing: var(--spacing);
|
||||
--letter-spacing: var(--letter-spacing);
|
||||
--shadow-offset-y: var(--shadow-offset-y);
|
||||
--shadow-offset-x: var(--shadow-offset-x);
|
||||
--shadow-spread: var(--shadow-spread);
|
||||
--shadow-blur: var(--shadow-blur);
|
||||
--shadow-opacity: var(--shadow-opacity);
|
||||
--color-shadow-color: var(--shadow-color);
|
||||
--color-destructive-foreground: var(--destructive-foreground);
|
||||
}
|
||||
|
||||
:root {
|
||||
--radius: 0.35rem;
|
||||
--background: oklch(0.96 0.01 264.53);
|
||||
--foreground: oklch(0.44 0.04 279.33);
|
||||
--card: oklch(1.0 0 0);
|
||||
--card-foreground: oklch(0.44 0.04 279.33);
|
||||
--popover: oklch(0.86 0.01 268.48);
|
||||
--popover-foreground: oklch(0.44 0.04 279.33);
|
||||
--primary: oklch(0.55 0.25 297.02);
|
||||
--primary-foreground: oklch(1.0 0 0);
|
||||
--secondary: oklch(0.86 0.01 268.48);
|
||||
--secondary-foreground: oklch(0.44 0.04 279.33);
|
||||
--muted: oklch(0.91 0.01 264.51);
|
||||
--muted-foreground: oklch(0.55 0.03 279.08);
|
||||
--accent: oklch(0.68 0.14 235.38);
|
||||
--accent-foreground: oklch(1.0 0 0);
|
||||
--destructive: oklch(0.55 0.22 19.81);
|
||||
--border: oklch(0.81 0.02 271.2);
|
||||
--input: oklch(0.86 0.01 268.48);
|
||||
--ring: oklch(0.55 0.25 297.02);
|
||||
--chart-1: oklch(0.55 0.25 297.02);
|
||||
--chart-2: oklch(0.68 0.14 235.38);
|
||||
--chart-3: oklch(0.63 0.18 140.44);
|
||||
--chart-4: oklch(0.69 0.2 42.43);
|
||||
--chart-5: oklch(0.71 0.1 33.1);
|
||||
--sidebar: oklch(0.93 0.01 264.52);
|
||||
--sidebar-foreground: oklch(0.44 0.04 279.33);
|
||||
--sidebar-primary: oklch(0.55 0.25 297.02);
|
||||
--sidebar-primary-foreground: oklch(1.0 0 0);
|
||||
--sidebar-accent: oklch(0.68 0.14 235.38);
|
||||
--sidebar-accent-foreground: oklch(1.0 0 0);
|
||||
--sidebar-border: oklch(0.81 0.02 271.2);
|
||||
--sidebar-ring: oklch(0.55 0.25 297.02);
|
||||
--destructive-foreground: oklch(1.0 0 0);
|
||||
--font-sans: Montserrat, sans-serif;
|
||||
--font-serif: Georgia, serif;
|
||||
--font-mono: Fira Code, monospace;
|
||||
--shadow-color: hsl(240 30% 25%);
|
||||
--shadow-opacity: 0.12;
|
||||
--shadow-blur: 6px;
|
||||
--shadow-spread: 0px;
|
||||
--shadow-offset-x: 0px;
|
||||
--shadow-offset-y: 4px;
|
||||
--letter-spacing: 0em;
|
||||
--spacing: 0.25rem;
|
||||
--shadow-2xs: 0px 4px 6px 0px hsl(240 30% 25% / 0.06);
|
||||
--shadow-xs: 0px 4px 6px 0px hsl(240 30% 25% / 0.06);
|
||||
--shadow-sm: 0px 4px 6px 0px hsl(240 30% 25% / 0.12), 0px 1px 2px -1px
|
||||
hsl(240 30% 25% / 0.12);
|
||||
--shadow: 0px 4px 6px 0px hsl(240 30% 25% / 0.12), 0px 1px 2px -1px
|
||||
hsl(240 30% 25% / 0.12);
|
||||
--shadow-md: 0px 4px 6px 0px hsl(240 30% 25% / 0.12), 0px 2px 4px -1px
|
||||
hsl(240 30% 25% / 0.12);
|
||||
--shadow-lg: 0px 4px 6px 0px hsl(240 30% 25% / 0.12), 0px 4px 6px -1px
|
||||
hsl(240 30% 25% / 0.12);
|
||||
--shadow-xl: 0px 4px 6px 0px hsl(240 30% 25% / 0.12), 0px 8px 10px -1px
|
||||
hsl(240 30% 25% / 0.12);
|
||||
--shadow-2xl: 0px 4px 6px 0px hsl(240 30% 25% / 0.3);
|
||||
--tracking-normal: 0em;
|
||||
}
|
||||
|
||||
.dark {
|
||||
--background: oklch(0.22 0.03 284.06);
|
||||
--foreground: oklch(0.88 0.04 272.28);
|
||||
--card: oklch(0.24 0.03 283.91);
|
||||
--card-foreground: oklch(0.88 0.04 272.28);
|
||||
--popover: oklch(0.4 0.03 280.15);
|
||||
--popover-foreground: oklch(0.88 0.04 272.28);
|
||||
--primary: oklch(0.79 0.12 304.77);
|
||||
--primary-foreground: oklch(0.24 0.03 283.91);
|
||||
--secondary: oklch(0.48 0.03 278.64);
|
||||
--secondary-foreground: oklch(0.88 0.04 272.28);
|
||||
--muted: oklch(0.3 0.03 276.21);
|
||||
--muted-foreground: oklch(0.75 0.04 273.93);
|
||||
--accent: oklch(0.85 0.08 210.25);
|
||||
--accent-foreground: oklch(0.24 0.03 283.91);
|
||||
--destructive: oklch(0.76 0.13 2.76);
|
||||
--border: oklch(0.32 0.03 281.98);
|
||||
--input: oklch(0.32 0.03 281.98);
|
||||
--ring: oklch(0.79 0.12 304.77);
|
||||
--chart-1: oklch(0.79 0.12 304.77);
|
||||
--chart-2: oklch(0.85 0.08 210.25);
|
||||
--chart-3: oklch(0.86 0.11 142.72);
|
||||
--chart-4: oklch(0.82 0.1 52.63);
|
||||
--chart-5: oklch(0.92 0.02 30.49);
|
||||
--sidebar: oklch(0.18 0.02 284.2);
|
||||
--sidebar-foreground: oklch(0.88 0.04 272.28);
|
||||
--sidebar-primary: oklch(0.79 0.12 304.77);
|
||||
--sidebar-primary-foreground: oklch(0.24 0.03 283.91);
|
||||
--sidebar-accent: oklch(0.85 0.08 210.25);
|
||||
--sidebar-accent-foreground: oklch(0.24 0.03 283.91);
|
||||
--sidebar-border: oklch(0.4 0.03 280.15);
|
||||
--sidebar-ring: oklch(0.79 0.12 304.77);
|
||||
--destructive-foreground: oklch(0.24 0.03 283.91);
|
||||
--radius: 0.35rem;
|
||||
--font-sans: Montserrat, sans-serif;
|
||||
--font-serif: Georgia, serif;
|
||||
--font-mono: Fira Code, monospace;
|
||||
--shadow-color: hsl(240 30% 25%);
|
||||
--shadow-opacity: 0.12;
|
||||
--shadow-blur: 6px;
|
||||
--shadow-spread: 0px;
|
||||
--shadow-offset-x: 0px;
|
||||
--shadow-offset-y: 4px;
|
||||
--letter-spacing: 0em;
|
||||
--spacing: 0.25rem;
|
||||
--shadow-2xs: 0px 4px 6px 0px hsl(240 30% 25% / 0.06);
|
||||
--shadow-xs: 0px 4px 6px 0px hsl(240 30% 25% / 0.06);
|
||||
--shadow-sm: 0px 4px 6px 0px hsl(240 30% 25% / 0.12), 0px 1px 2px -1px
|
||||
hsl(240 30% 25% / 0.12);
|
||||
--shadow: 0px 4px 6px 0px hsl(240 30% 25% / 0.12), 0px 1px 2px -1px
|
||||
hsl(240 30% 25% / 0.12);
|
||||
--shadow-md: 0px 4px 6px 0px hsl(240 30% 25% / 0.12), 0px 2px 4px -1px
|
||||
hsl(240 30% 25% / 0.12);
|
||||
--shadow-lg: 0px 4px 6px 0px hsl(240 30% 25% / 0.12), 0px 4px 6px -1px
|
||||
hsl(240 30% 25% / 0.12);
|
||||
--shadow-xl: 0px 4px 6px 0px hsl(240 30% 25% / 0.12), 0px 8px 10px -1px
|
||||
hsl(240 30% 25% / 0.12);
|
||||
--shadow-2xl: 0px 4px 6px 0px hsl(240 30% 25% / 0.3);
|
||||
}
|
||||
|
||||
@layer base {
|
||||
* {
|
||||
@apply border-border outline-ring/50;
|
||||
}
|
||||
body {
|
||||
@apply bg-background text-foreground;
|
||||
letter-spacing: var(--tracking-normal);
|
||||
}
|
||||
}
|
||||
|
||||
58
apps/web/src/components/ui/scroll-area.tsx
Normal file
58
apps/web/src/components/ui/scroll-area.tsx
Normal file
@@ -0,0 +1,58 @@
|
||||
"use client";
|
||||
|
||||
import * as ScrollAreaPrimitive from "@radix-ui/react-scroll-area";
|
||||
import type * as React from "react";
|
||||
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
function ScrollArea({
|
||||
className,
|
||||
children,
|
||||
...props
|
||||
}: React.ComponentProps<typeof ScrollAreaPrimitive.Root>) {
|
||||
return (
|
||||
<ScrollAreaPrimitive.Root
|
||||
data-slot="scroll-area"
|
||||
className={cn("relative", className)}
|
||||
{...props}
|
||||
>
|
||||
<ScrollAreaPrimitive.Viewport
|
||||
data-slot="scroll-area-viewport"
|
||||
className="size-full rounded-[inherit] outline-none transition-[color,box-shadow] focus-visible:outline-1 focus-visible:ring-[3px] focus-visible:ring-ring/50"
|
||||
>
|
||||
{children}
|
||||
</ScrollAreaPrimitive.Viewport>
|
||||
<ScrollBar />
|
||||
<ScrollAreaPrimitive.Corner />
|
||||
</ScrollAreaPrimitive.Root>
|
||||
);
|
||||
}
|
||||
|
||||
function ScrollBar({
|
||||
className,
|
||||
orientation = "vertical",
|
||||
...props
|
||||
}: React.ComponentProps<typeof ScrollAreaPrimitive.ScrollAreaScrollbar>) {
|
||||
return (
|
||||
<ScrollAreaPrimitive.ScrollAreaScrollbar
|
||||
data-slot="scroll-area-scrollbar"
|
||||
orientation={orientation}
|
||||
className={cn(
|
||||
"flex touch-none select-none p-px transition-colors",
|
||||
orientation === "vertical" &&
|
||||
"h-full w-2.5 border-l border-l-transparent",
|
||||
orientation === "horizontal" &&
|
||||
"h-2.5 flex-col border-t border-t-transparent",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
<ScrollAreaPrimitive.ScrollAreaThumb
|
||||
data-slot="scroll-area-thumb"
|
||||
className="relative flex-1 rounded-full bg-border"
|
||||
/>
|
||||
</ScrollAreaPrimitive.ScrollAreaScrollbar>
|
||||
);
|
||||
}
|
||||
|
||||
export { ScrollArea, ScrollBar };
|
||||
@@ -12,7 +12,7 @@ export const TECH_OPTIONS = {
|
||||
id: "orpc",
|
||||
name: "oRPC",
|
||||
description: "Opinionated RPC framework",
|
||||
icon: "🧩",
|
||||
icon: "/icon/orpc.svg",
|
||||
color: "from-indigo-400 to-indigo-600",
|
||||
},
|
||||
],
|
||||
|
||||
6
apps/web/src/lib/utils.ts
Normal file
6
apps/web/src/lib/utils.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
import { type ClassValue, clsx } from "clsx";
|
||||
import { twMerge } from "tailwind-merge";
|
||||
|
||||
export function cn(...inputs: ClassValue[]) {
|
||||
return twMerge(clsx(inputs));
|
||||
}
|
||||
193
bun.lock
193
bun.lock
@@ -43,18 +43,21 @@
|
||||
"version": "0.0.0",
|
||||
"dependencies": {
|
||||
"@heroicons/react": "^2.2.0",
|
||||
"@radix-ui/react-scroll-area": "^1.2.5",
|
||||
"@xyflow/react": "^12.5.5",
|
||||
"babel-plugin-react-compiler": "^19.0.0-beta-e993439-20250405",
|
||||
"canvas-confetti": "^1.9.3",
|
||||
"class-variance-authority": "^0.7.1",
|
||||
"clsx": "^2.1.1",
|
||||
"fumadocs-core": "15.1.2",
|
||||
"fumadocs-mdx": "11.5.7",
|
||||
"fumadocs-ui": "15.1.2",
|
||||
"lucide-react": "^0.485.0",
|
||||
"lucide-react": "^0.501.0",
|
||||
"motion": "^12.7.4",
|
||||
"next": "15.2.3",
|
||||
"react": "^19.1.0",
|
||||
"react-dom": "^19.1.0",
|
||||
"react-tweet": "^3.2.2",
|
||||
"tailwind-merge": "^3.2.0",
|
||||
},
|
||||
"devDependencies": {
|
||||
"@tailwindcss/postcss": "^4.1.3",
|
||||
@@ -315,9 +318,9 @@
|
||||
|
||||
"@pkgjs/parseargs": ["@pkgjs/parseargs@0.11.0", "", {}, "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg=="],
|
||||
|
||||
"@radix-ui/number": ["@radix-ui/number@1.1.0", "", {}, "sha512-V3gRzhVNU1ldS5XhAPTom1fOIo4ccrjjJgmE+LI2h/WaFpHmx0MQApT+KZHnx8abG6Avtfcz4WoEciMnpFT3HQ=="],
|
||||
"@radix-ui/number": ["@radix-ui/number@1.1.1", "", {}, "sha512-MkKCwxlXTgz6CFoJx3pCwn07GKp36+aZyu/u2Ln2VrA5DcdyCZkASEDBTd8x5whTQQL5CiYf4prXKLcgQdv29g=="],
|
||||
|
||||
"@radix-ui/primitive": ["@radix-ui/primitive@1.1.1", "", {}, "sha512-SJ31y+Q/zAyShtXJc8x83i9TYdbAfHZ++tUZnvjJJqFjzsdUnKsxPL6IEtBlxKkU7yzer//GQtZSV4GbldL3YA=="],
|
||||
"@radix-ui/primitive": ["@radix-ui/primitive@1.1.2", "", {}, "sha512-XnbHrrprsNqZKQhStrSwgRUQzoCI1glLzdw79xiZPoofhGICeZRSQ3dIxAKH1gb3OHfNf4d6f+vAv3kil2eggA=="],
|
||||
|
||||
"@radix-ui/react-accordion": ["@radix-ui/react-accordion@1.2.3", "", { "dependencies": { "@radix-ui/primitive": "1.1.1", "@radix-ui/react-collapsible": "1.1.3", "@radix-ui/react-collection": "1.1.2", "@radix-ui/react-compose-refs": "1.1.1", "@radix-ui/react-context": "1.1.1", "@radix-ui/react-direction": "1.1.0", "@radix-ui/react-id": "1.1.0", "@radix-ui/react-primitive": "2.0.2", "@radix-ui/react-use-controllable-state": "1.1.0" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-RIQ15mrcvqIkDARJeERSuXSry2N8uYnxkdDetpfmalT/+0ntOXLkFOsh9iwlAsCv+qcmhZjbdJogIm6WBa6c4A=="],
|
||||
|
||||
@@ -327,13 +330,13 @@
|
||||
|
||||
"@radix-ui/react-collection": ["@radix-ui/react-collection@1.1.2", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.1", "@radix-ui/react-context": "1.1.1", "@radix-ui/react-primitive": "2.0.2", "@radix-ui/react-slot": "1.1.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-9z54IEKRxIa9VityapoEYMuByaG42iSy1ZXlY2KcuLSEtq8x4987/N6m15ppoMffgZX72gER2uHe1D9Y6Unlcw=="],
|
||||
|
||||
"@radix-ui/react-compose-refs": ["@radix-ui/react-compose-refs@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-Y9VzoRDSJtgFMUCoiZBDVo084VQ5hfpXxVE+NgkdNsjiDBByiImMZKKhxMwCbdHvhlENG6a833CbFkOQvTricw=="],
|
||||
"@radix-ui/react-compose-refs": ["@radix-ui/react-compose-refs@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg=="],
|
||||
|
||||
"@radix-ui/react-context": ["@radix-ui/react-context@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-UASk9zi+crv9WteK/NU4PLvOoL3OuE6BWVKNF6hPRBtYBDXQ2u5iu3O59zUlJiTVvkyuycnqrztsHVJwcK9K+Q=="],
|
||||
"@radix-ui/react-context": ["@radix-ui/react-context@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA=="],
|
||||
|
||||
"@radix-ui/react-dialog": ["@radix-ui/react-dialog@1.1.6", "", { "dependencies": { "@radix-ui/primitive": "1.1.1", "@radix-ui/react-compose-refs": "1.1.1", "@radix-ui/react-context": "1.1.1", "@radix-ui/react-dismissable-layer": "1.1.5", "@radix-ui/react-focus-guards": "1.1.1", "@radix-ui/react-focus-scope": "1.1.2", "@radix-ui/react-id": "1.1.0", "@radix-ui/react-portal": "1.1.4", "@radix-ui/react-presence": "1.1.2", "@radix-ui/react-primitive": "2.0.2", "@radix-ui/react-slot": "1.1.2", "@radix-ui/react-use-controllable-state": "1.1.0", "aria-hidden": "^1.2.4", "react-remove-scroll": "^2.6.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-/IVhJV5AceX620DUJ4uYVMymzsipdKBzo3edo+omeskCKGm9FRHM0ebIdbPnlQVJqyuHbuBltQUOG2mOTq2IYw=="],
|
||||
|
||||
"@radix-ui/react-direction": ["@radix-ui/react-direction@1.1.0", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-BUuBvgThEiAXh2DWu93XsT+a3aWrGqolGlqqw5VU1kG7p/ZH2cuDlM1sRLNnY3QcBS69UIz2mcKhMxDsdewhjg=="],
|
||||
"@radix-ui/react-direction": ["@radix-ui/react-direction@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-1UEWRX6jnOA2y4H5WczZ44gOOjTEmlqv1uNW4GAJEO5+bauCBhv8snY65Iw5/VOS/ghKN9gr2KjnLKxrsvoMVw=="],
|
||||
|
||||
"@radix-ui/react-dismissable-layer": ["@radix-ui/react-dismissable-layer@1.1.5", "", { "dependencies": { "@radix-ui/primitive": "1.1.1", "@radix-ui/react-compose-refs": "1.1.1", "@radix-ui/react-primitive": "2.0.2", "@radix-ui/react-use-callback-ref": "1.1.0", "@radix-ui/react-use-escape-keydown": "1.1.0" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-E4TywXY6UsXNRhFrECa5HAvE5/4BFcGyfTyK36gP+pAW1ed7UTK4vKwdr53gAJYwqbfCWC6ATvJa3J3R/9+Qrg=="],
|
||||
|
||||
@@ -351,25 +354,25 @@
|
||||
|
||||
"@radix-ui/react-portal": ["@radix-ui/react-portal@1.1.4", "", { "dependencies": { "@radix-ui/react-primitive": "2.0.2", "@radix-ui/react-use-layout-effect": "1.1.0" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-sn2O9k1rPFYVyKd5LAJfo96JlSGVFpa1fS6UuBJfrZadudiw5tAmru+n1x7aMRQ84qDM71Zh1+SzK5QwU0tJfA=="],
|
||||
|
||||
"@radix-ui/react-presence": ["@radix-ui/react-presence@1.1.2", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.1", "@radix-ui/react-use-layout-effect": "1.1.0" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-18TFr80t5EVgL9x1SwF/YGtfG+l0BS0PRAlCWBDoBEiDQjeKgnNZRVJp/oVBl24sr3Gbfwc/Qpj4OcWTQMsAEg=="],
|
||||
"@radix-ui/react-presence": ["@radix-ui/react-presence@1.1.3", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-IrVLIhskYhH3nLvtcBLQFZr61tBG7wx7O3kEmdzcYwRGAEBmBicGGL7ATzNgruYJ3xBTbuzEEq9OXJM3PAX3tA=="],
|
||||
|
||||
"@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.0.2", "", { "dependencies": { "@radix-ui/react-slot": "1.1.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-Ec/0d38EIuvDF+GZjcMU/Ze6MxntVJYO/fRlCPhCaVUyPY9WTalHJw54tp9sXeJo3tlShWpy41vQRgLRGOuz+w=="],
|
||||
"@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.0", "", { "dependencies": { "@radix-ui/react-slot": "1.2.0" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-/J/FhLdK0zVcILOwt5g+dH4KnkonCtkVJsa2G6JmvbbtZfBEI1gMsO3QMjseL4F/SwfAMt1Vc/0XKYKq+xJ1sw=="],
|
||||
|
||||
"@radix-ui/react-roving-focus": ["@radix-ui/react-roving-focus@1.1.2", "", { "dependencies": { "@radix-ui/primitive": "1.1.1", "@radix-ui/react-collection": "1.1.2", "@radix-ui/react-compose-refs": "1.1.1", "@radix-ui/react-context": "1.1.1", "@radix-ui/react-direction": "1.1.0", "@radix-ui/react-id": "1.1.0", "@radix-ui/react-primitive": "2.0.2", "@radix-ui/react-use-callback-ref": "1.1.0", "@radix-ui/react-use-controllable-state": "1.1.0" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-zgMQWkNO169GtGqRvYrzb0Zf8NhMHS2DuEB/TiEmVnpr5OqPU3i8lfbxaAmC2J/KYuIQxyoQQ6DxepyXp61/xw=="],
|
||||
|
||||
"@radix-ui/react-scroll-area": ["@radix-ui/react-scroll-area@1.2.3", "", { "dependencies": { "@radix-ui/number": "1.1.0", "@radix-ui/primitive": "1.1.1", "@radix-ui/react-compose-refs": "1.1.1", "@radix-ui/react-context": "1.1.1", "@radix-ui/react-direction": "1.1.0", "@radix-ui/react-presence": "1.1.2", "@radix-ui/react-primitive": "2.0.2", "@radix-ui/react-use-callback-ref": "1.1.0", "@radix-ui/react-use-layout-effect": "1.1.0" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-l7+NNBfBYYJa9tNqVcP2AGvxdE3lmE6kFTBXdvHgUaZuy+4wGCL1Cl2AfaR7RKyimj7lZURGLwFO59k4eBnDJQ=="],
|
||||
"@radix-ui/react-scroll-area": ["@radix-ui/react-scroll-area@1.2.5", "", { "dependencies": { "@radix-ui/number": "1.1.1", "@radix-ui/primitive": "1.1.2", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-direction": "1.1.1", "@radix-ui/react-presence": "1.1.3", "@radix-ui/react-primitive": "2.1.0", "@radix-ui/react-use-callback-ref": "1.1.1", "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-VyLjxI8/gXYn+Wij1FLpXjZp6Z/uNklUFQQ75tOpJNESeNaZ2kCRfjiEDmHgWmLeUPeJGwrqbgRmcdFjtYEkMA=="],
|
||||
|
||||
"@radix-ui/react-slot": ["@radix-ui/react-slot@1.1.2", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-YAKxaiGsSQJ38VzKH86/BPRC4rh+b1Jpa+JneA5LRE7skmLPNAyeG8kPJj/oo4STLvlrs8vkf/iYyc3A5stYCQ=="],
|
||||
|
||||
"@radix-ui/react-tabs": ["@radix-ui/react-tabs@1.1.3", "", { "dependencies": { "@radix-ui/primitive": "1.1.1", "@radix-ui/react-context": "1.1.1", "@radix-ui/react-direction": "1.1.0", "@radix-ui/react-id": "1.1.0", "@radix-ui/react-presence": "1.1.2", "@radix-ui/react-primitive": "2.0.2", "@radix-ui/react-roving-focus": "1.1.2", "@radix-ui/react-use-controllable-state": "1.1.0" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-9mFyI30cuRDImbmFF6O2KUJdgEOsGh9Vmx9x/Dh9tOhL7BngmQPQfwW4aejKm5OHpfWIdmeV6ySyuxoOGjtNng=="],
|
||||
|
||||
"@radix-ui/react-use-callback-ref": ["@radix-ui/react-use-callback-ref@1.1.0", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-CasTfvsy+frcFkbXtSJ2Zu9JHpN8TYKxkgJGWbjiZhFivxaeW7rMeZt7QELGVLaYVfFMsKHjb7Ak0nMEe+2Vfw=="],
|
||||
"@radix-ui/react-use-callback-ref": ["@radix-ui/react-use-callback-ref@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-FkBMwD+qbGQeMu1cOHnuGB6x4yzPjho8ap5WtbEJ26umhgqVXbhekKUQO+hZEL1vU92a3wHwdp0HAcqAUF5iDg=="],
|
||||
|
||||
"@radix-ui/react-use-controllable-state": ["@radix-ui/react-use-controllable-state@1.1.0", "", { "dependencies": { "@radix-ui/react-use-callback-ref": "1.1.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-MtfMVJiSr2NjzS0Aa90NPTnvTSg6C/JLCV7ma0W6+OMV78vd8OyRpID+Ng9LxzsPbLeuBnWBA1Nq30AtBIDChw=="],
|
||||
|
||||
"@radix-ui/react-use-escape-keydown": ["@radix-ui/react-use-escape-keydown@1.1.0", "", { "dependencies": { "@radix-ui/react-use-callback-ref": "1.1.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-L7vwWlR1kTTQ3oh7g1O0CBF3YCyyTj8NmhLR+phShpyA50HCfBFKVJTpshm9PzLiKmehsrQzTYTpX9HvmC9rhw=="],
|
||||
|
||||
"@radix-ui/react-use-layout-effect": ["@radix-ui/react-use-layout-effect@1.1.0", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-+FPE0rOdziWSrH9athwI1R0HDVbWlEhd+FR+aSDk4uWGmSJ9Z54sdZVDQPZAinJhJXwfT+qnj969mCsT2gfm5w=="],
|
||||
"@radix-ui/react-use-layout-effect": ["@radix-ui/react-use-layout-effect@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ=="],
|
||||
|
||||
"@radix-ui/react-use-previous": ["@radix-ui/react-use-previous@1.1.0", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-Z/e78qg2YFnnXcW88A4JmTtm4ADckLno6F7OXotmkQfeuCVaKuYzqAATPhVzl3delXE7CxIV8shofPn3jPc5Og=="],
|
||||
|
||||
@@ -631,8 +634,6 @@
|
||||
|
||||
"caniuse-lite": ["caniuse-lite@1.0.30001699", "", {}, "sha512-b+uH5BakXZ9Do9iK+CkDmctUSEqZl+SP056vc5usa0PL+ev5OHw003rZXcnjNDv3L8P5j6rwT6C0BPKSikW08w=="],
|
||||
|
||||
"canvas-confetti": ["canvas-confetti@1.9.3", "", {}, "sha512-rFfTURMvmVEX1gyXFgn5QMn81bYk70qa0HLzcIOSVEyl57n6o9ItHeBtUSWdvKAPY0xlvBHno4/v3QPrT83q9g=="],
|
||||
|
||||
"ccount": ["ccount@2.0.1", "", {}, "sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg=="],
|
||||
|
||||
"chalk": ["chalk@5.4.1", "", {}, "sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w=="],
|
||||
@@ -1135,7 +1136,7 @@
|
||||
|
||||
"lru-cache": ["lru-cache@10.4.3", "", {}, "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ=="],
|
||||
|
||||
"lucide-react": ["lucide-react@0.485.0", "", { "peerDependencies": { "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-NvyQJ0LKyyCxL23nPKESlr/jmz8r7fJO1bkuptSNYSy0s8VVj4ojhX0YAgmE1e0ewfxUZjIlZpvH+otfTnla8Q=="],
|
||||
"lucide-react": ["lucide-react@0.501.0", "", { "peerDependencies": { "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-E2KoyhW59fCb/yUbR3rbDer83fqn7a8NG91ZhIot2yWaPHjPyGzzsNKh40N//GezYShAuycf3TcQksRQznIsRw=="],
|
||||
|
||||
"markdown-extensions": ["markdown-extensions@2.0.0", "", {}, "sha512-o5vL7aDWatOTX8LzaS1WMoaoxIiLRQJuIKKe2wAw6IeULDHaqbiqiggmx+pKvZDb1Sj+pE46Sn1T7lCqfFtg1Q=="],
|
||||
|
||||
@@ -1555,7 +1556,7 @@
|
||||
|
||||
"swr": ["swr@2.3.3", "", { "dependencies": { "dequal": "^2.0.3", "use-sync-external-store": "^1.4.0" }, "peerDependencies": { "react": "^16.11.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-dshNvs3ExOqtZ6kJBaAsabhPdHyeY4P2cKwRCniDVifBMoG/SVI7tfLWqPXriVspf2Rg4tPzXJTnwaihIeFw2A=="],
|
||||
|
||||
"tailwind-merge": ["tailwind-merge@3.0.2", "", {}, "sha512-l7z+OYZ7mu3DTqrL88RiKrKIqO3NcpEO8V/Od04bNpvk0kiIFndGEoqfuzvj4yuhRkHKjRkII2z+KS2HfPcSxw=="],
|
||||
"tailwind-merge": ["tailwind-merge@3.2.0", "", {}, "sha512-FQT/OVqCD+7edmmJpsgCsY820RTD5AkBryuG5IUqR5YQZSdj5xlH5nLgH7YPths7WsLPSpSBNneJdM8aS8aeFA=="],
|
||||
|
||||
"tailwindcss": ["tailwindcss@4.1.3", "", {}, "sha512-2Q+rw9vy1WFXu5cIxlvsabCwhU2qUwodGq03ODhLJ0jW4ek5BUtoCsnLB0qG+m8AHgEsSJcJGDSDe06FXlP74g=="],
|
||||
|
||||
@@ -1745,6 +1746,136 @@
|
||||
|
||||
"@next/eslint-plugin-next/fast-glob": ["fast-glob@3.3.1", "", { "dependencies": { "@nodelib/fs.stat": "^2.0.2", "@nodelib/fs.walk": "^1.2.3", "glob-parent": "^5.1.2", "merge2": "^1.3.0", "micromatch": "^4.0.4" } }, "sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg=="],
|
||||
|
||||
"@radix-ui/react-accordion/@radix-ui/primitive": ["@radix-ui/primitive@1.1.1", "", {}, "sha512-SJ31y+Q/zAyShtXJc8x83i9TYdbAfHZ++tUZnvjJJqFjzsdUnKsxPL6IEtBlxKkU7yzer//GQtZSV4GbldL3YA=="],
|
||||
|
||||
"@radix-ui/react-accordion/@radix-ui/react-compose-refs": ["@radix-ui/react-compose-refs@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-Y9VzoRDSJtgFMUCoiZBDVo084VQ5hfpXxVE+NgkdNsjiDBByiImMZKKhxMwCbdHvhlENG6a833CbFkOQvTricw=="],
|
||||
|
||||
"@radix-ui/react-accordion/@radix-ui/react-context": ["@radix-ui/react-context@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-UASk9zi+crv9WteK/NU4PLvOoL3OuE6BWVKNF6hPRBtYBDXQ2u5iu3O59zUlJiTVvkyuycnqrztsHVJwcK9K+Q=="],
|
||||
|
||||
"@radix-ui/react-accordion/@radix-ui/react-direction": ["@radix-ui/react-direction@1.1.0", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-BUuBvgThEiAXh2DWu93XsT+a3aWrGqolGlqqw5VU1kG7p/ZH2cuDlM1sRLNnY3QcBS69UIz2mcKhMxDsdewhjg=="],
|
||||
|
||||
"@radix-ui/react-accordion/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.0.2", "", { "dependencies": { "@radix-ui/react-slot": "1.1.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-Ec/0d38EIuvDF+GZjcMU/Ze6MxntVJYO/fRlCPhCaVUyPY9WTalHJw54tp9sXeJo3tlShWpy41vQRgLRGOuz+w=="],
|
||||
|
||||
"@radix-ui/react-arrow/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.0.2", "", { "dependencies": { "@radix-ui/react-slot": "1.1.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-Ec/0d38EIuvDF+GZjcMU/Ze6MxntVJYO/fRlCPhCaVUyPY9WTalHJw54tp9sXeJo3tlShWpy41vQRgLRGOuz+w=="],
|
||||
|
||||
"@radix-ui/react-collapsible/@radix-ui/primitive": ["@radix-ui/primitive@1.1.1", "", {}, "sha512-SJ31y+Q/zAyShtXJc8x83i9TYdbAfHZ++tUZnvjJJqFjzsdUnKsxPL6IEtBlxKkU7yzer//GQtZSV4GbldL3YA=="],
|
||||
|
||||
"@radix-ui/react-collapsible/@radix-ui/react-compose-refs": ["@radix-ui/react-compose-refs@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-Y9VzoRDSJtgFMUCoiZBDVo084VQ5hfpXxVE+NgkdNsjiDBByiImMZKKhxMwCbdHvhlENG6a833CbFkOQvTricw=="],
|
||||
|
||||
"@radix-ui/react-collapsible/@radix-ui/react-context": ["@radix-ui/react-context@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-UASk9zi+crv9WteK/NU4PLvOoL3OuE6BWVKNF6hPRBtYBDXQ2u5iu3O59zUlJiTVvkyuycnqrztsHVJwcK9K+Q=="],
|
||||
|
||||
"@radix-ui/react-collapsible/@radix-ui/react-presence": ["@radix-ui/react-presence@1.1.2", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.1", "@radix-ui/react-use-layout-effect": "1.1.0" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-18TFr80t5EVgL9x1SwF/YGtfG+l0BS0PRAlCWBDoBEiDQjeKgnNZRVJp/oVBl24sr3Gbfwc/Qpj4OcWTQMsAEg=="],
|
||||
|
||||
"@radix-ui/react-collapsible/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.0.2", "", { "dependencies": { "@radix-ui/react-slot": "1.1.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-Ec/0d38EIuvDF+GZjcMU/Ze6MxntVJYO/fRlCPhCaVUyPY9WTalHJw54tp9sXeJo3tlShWpy41vQRgLRGOuz+w=="],
|
||||
|
||||
"@radix-ui/react-collapsible/@radix-ui/react-use-layout-effect": ["@radix-ui/react-use-layout-effect@1.1.0", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-+FPE0rOdziWSrH9athwI1R0HDVbWlEhd+FR+aSDk4uWGmSJ9Z54sdZVDQPZAinJhJXwfT+qnj969mCsT2gfm5w=="],
|
||||
|
||||
"@radix-ui/react-collection/@radix-ui/react-compose-refs": ["@radix-ui/react-compose-refs@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-Y9VzoRDSJtgFMUCoiZBDVo084VQ5hfpXxVE+NgkdNsjiDBByiImMZKKhxMwCbdHvhlENG6a833CbFkOQvTricw=="],
|
||||
|
||||
"@radix-ui/react-collection/@radix-ui/react-context": ["@radix-ui/react-context@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-UASk9zi+crv9WteK/NU4PLvOoL3OuE6BWVKNF6hPRBtYBDXQ2u5iu3O59zUlJiTVvkyuycnqrztsHVJwcK9K+Q=="],
|
||||
|
||||
"@radix-ui/react-collection/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.0.2", "", { "dependencies": { "@radix-ui/react-slot": "1.1.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-Ec/0d38EIuvDF+GZjcMU/Ze6MxntVJYO/fRlCPhCaVUyPY9WTalHJw54tp9sXeJo3tlShWpy41vQRgLRGOuz+w=="],
|
||||
|
||||
"@radix-ui/react-dialog/@radix-ui/primitive": ["@radix-ui/primitive@1.1.1", "", {}, "sha512-SJ31y+Q/zAyShtXJc8x83i9TYdbAfHZ++tUZnvjJJqFjzsdUnKsxPL6IEtBlxKkU7yzer//GQtZSV4GbldL3YA=="],
|
||||
|
||||
"@radix-ui/react-dialog/@radix-ui/react-compose-refs": ["@radix-ui/react-compose-refs@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-Y9VzoRDSJtgFMUCoiZBDVo084VQ5hfpXxVE+NgkdNsjiDBByiImMZKKhxMwCbdHvhlENG6a833CbFkOQvTricw=="],
|
||||
|
||||
"@radix-ui/react-dialog/@radix-ui/react-context": ["@radix-ui/react-context@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-UASk9zi+crv9WteK/NU4PLvOoL3OuE6BWVKNF6hPRBtYBDXQ2u5iu3O59zUlJiTVvkyuycnqrztsHVJwcK9K+Q=="],
|
||||
|
||||
"@radix-ui/react-dialog/@radix-ui/react-presence": ["@radix-ui/react-presence@1.1.2", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.1", "@radix-ui/react-use-layout-effect": "1.1.0" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-18TFr80t5EVgL9x1SwF/YGtfG+l0BS0PRAlCWBDoBEiDQjeKgnNZRVJp/oVBl24sr3Gbfwc/Qpj4OcWTQMsAEg=="],
|
||||
|
||||
"@radix-ui/react-dialog/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.0.2", "", { "dependencies": { "@radix-ui/react-slot": "1.1.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-Ec/0d38EIuvDF+GZjcMU/Ze6MxntVJYO/fRlCPhCaVUyPY9WTalHJw54tp9sXeJo3tlShWpy41vQRgLRGOuz+w=="],
|
||||
|
||||
"@radix-ui/react-dismissable-layer/@radix-ui/primitive": ["@radix-ui/primitive@1.1.1", "", {}, "sha512-SJ31y+Q/zAyShtXJc8x83i9TYdbAfHZ++tUZnvjJJqFjzsdUnKsxPL6IEtBlxKkU7yzer//GQtZSV4GbldL3YA=="],
|
||||
|
||||
"@radix-ui/react-dismissable-layer/@radix-ui/react-compose-refs": ["@radix-ui/react-compose-refs@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-Y9VzoRDSJtgFMUCoiZBDVo084VQ5hfpXxVE+NgkdNsjiDBByiImMZKKhxMwCbdHvhlENG6a833CbFkOQvTricw=="],
|
||||
|
||||
"@radix-ui/react-dismissable-layer/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.0.2", "", { "dependencies": { "@radix-ui/react-slot": "1.1.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-Ec/0d38EIuvDF+GZjcMU/Ze6MxntVJYO/fRlCPhCaVUyPY9WTalHJw54tp9sXeJo3tlShWpy41vQRgLRGOuz+w=="],
|
||||
|
||||
"@radix-ui/react-dismissable-layer/@radix-ui/react-use-callback-ref": ["@radix-ui/react-use-callback-ref@1.1.0", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-CasTfvsy+frcFkbXtSJ2Zu9JHpN8TYKxkgJGWbjiZhFivxaeW7rMeZt7QELGVLaYVfFMsKHjb7Ak0nMEe+2Vfw=="],
|
||||
|
||||
"@radix-ui/react-focus-scope/@radix-ui/react-compose-refs": ["@radix-ui/react-compose-refs@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-Y9VzoRDSJtgFMUCoiZBDVo084VQ5hfpXxVE+NgkdNsjiDBByiImMZKKhxMwCbdHvhlENG6a833CbFkOQvTricw=="],
|
||||
|
||||
"@radix-ui/react-focus-scope/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.0.2", "", { "dependencies": { "@radix-ui/react-slot": "1.1.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-Ec/0d38EIuvDF+GZjcMU/Ze6MxntVJYO/fRlCPhCaVUyPY9WTalHJw54tp9sXeJo3tlShWpy41vQRgLRGOuz+w=="],
|
||||
|
||||
"@radix-ui/react-focus-scope/@radix-ui/react-use-callback-ref": ["@radix-ui/react-use-callback-ref@1.1.0", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-CasTfvsy+frcFkbXtSJ2Zu9JHpN8TYKxkgJGWbjiZhFivxaeW7rMeZt7QELGVLaYVfFMsKHjb7Ak0nMEe+2Vfw=="],
|
||||
|
||||
"@radix-ui/react-id/@radix-ui/react-use-layout-effect": ["@radix-ui/react-use-layout-effect@1.1.0", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-+FPE0rOdziWSrH9athwI1R0HDVbWlEhd+FR+aSDk4uWGmSJ9Z54sdZVDQPZAinJhJXwfT+qnj969mCsT2gfm5w=="],
|
||||
|
||||
"@radix-ui/react-navigation-menu/@radix-ui/primitive": ["@radix-ui/primitive@1.1.1", "", {}, "sha512-SJ31y+Q/zAyShtXJc8x83i9TYdbAfHZ++tUZnvjJJqFjzsdUnKsxPL6IEtBlxKkU7yzer//GQtZSV4GbldL3YA=="],
|
||||
|
||||
"@radix-ui/react-navigation-menu/@radix-ui/react-compose-refs": ["@radix-ui/react-compose-refs@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-Y9VzoRDSJtgFMUCoiZBDVo084VQ5hfpXxVE+NgkdNsjiDBByiImMZKKhxMwCbdHvhlENG6a833CbFkOQvTricw=="],
|
||||
|
||||
"@radix-ui/react-navigation-menu/@radix-ui/react-context": ["@radix-ui/react-context@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-UASk9zi+crv9WteK/NU4PLvOoL3OuE6BWVKNF6hPRBtYBDXQ2u5iu3O59zUlJiTVvkyuycnqrztsHVJwcK9K+Q=="],
|
||||
|
||||
"@radix-ui/react-navigation-menu/@radix-ui/react-direction": ["@radix-ui/react-direction@1.1.0", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-BUuBvgThEiAXh2DWu93XsT+a3aWrGqolGlqqw5VU1kG7p/ZH2cuDlM1sRLNnY3QcBS69UIz2mcKhMxDsdewhjg=="],
|
||||
|
||||
"@radix-ui/react-navigation-menu/@radix-ui/react-presence": ["@radix-ui/react-presence@1.1.2", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.1", "@radix-ui/react-use-layout-effect": "1.1.0" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-18TFr80t5EVgL9x1SwF/YGtfG+l0BS0PRAlCWBDoBEiDQjeKgnNZRVJp/oVBl24sr3Gbfwc/Qpj4OcWTQMsAEg=="],
|
||||
|
||||
"@radix-ui/react-navigation-menu/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.0.2", "", { "dependencies": { "@radix-ui/react-slot": "1.1.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-Ec/0d38EIuvDF+GZjcMU/Ze6MxntVJYO/fRlCPhCaVUyPY9WTalHJw54tp9sXeJo3tlShWpy41vQRgLRGOuz+w=="],
|
||||
|
||||
"@radix-ui/react-navigation-menu/@radix-ui/react-use-callback-ref": ["@radix-ui/react-use-callback-ref@1.1.0", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-CasTfvsy+frcFkbXtSJ2Zu9JHpN8TYKxkgJGWbjiZhFivxaeW7rMeZt7QELGVLaYVfFMsKHjb7Ak0nMEe+2Vfw=="],
|
||||
|
||||
"@radix-ui/react-navigation-menu/@radix-ui/react-use-layout-effect": ["@radix-ui/react-use-layout-effect@1.1.0", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-+FPE0rOdziWSrH9athwI1R0HDVbWlEhd+FR+aSDk4uWGmSJ9Z54sdZVDQPZAinJhJXwfT+qnj969mCsT2gfm5w=="],
|
||||
|
||||
"@radix-ui/react-popover/@radix-ui/primitive": ["@radix-ui/primitive@1.1.1", "", {}, "sha512-SJ31y+Q/zAyShtXJc8x83i9TYdbAfHZ++tUZnvjJJqFjzsdUnKsxPL6IEtBlxKkU7yzer//GQtZSV4GbldL3YA=="],
|
||||
|
||||
"@radix-ui/react-popover/@radix-ui/react-compose-refs": ["@radix-ui/react-compose-refs@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-Y9VzoRDSJtgFMUCoiZBDVo084VQ5hfpXxVE+NgkdNsjiDBByiImMZKKhxMwCbdHvhlENG6a833CbFkOQvTricw=="],
|
||||
|
||||
"@radix-ui/react-popover/@radix-ui/react-context": ["@radix-ui/react-context@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-UASk9zi+crv9WteK/NU4PLvOoL3OuE6BWVKNF6hPRBtYBDXQ2u5iu3O59zUlJiTVvkyuycnqrztsHVJwcK9K+Q=="],
|
||||
|
||||
"@radix-ui/react-popover/@radix-ui/react-presence": ["@radix-ui/react-presence@1.1.2", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.1", "@radix-ui/react-use-layout-effect": "1.1.0" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-18TFr80t5EVgL9x1SwF/YGtfG+l0BS0PRAlCWBDoBEiDQjeKgnNZRVJp/oVBl24sr3Gbfwc/Qpj4OcWTQMsAEg=="],
|
||||
|
||||
"@radix-ui/react-popover/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.0.2", "", { "dependencies": { "@radix-ui/react-slot": "1.1.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-Ec/0d38EIuvDF+GZjcMU/Ze6MxntVJYO/fRlCPhCaVUyPY9WTalHJw54tp9sXeJo3tlShWpy41vQRgLRGOuz+w=="],
|
||||
|
||||
"@radix-ui/react-popper/@radix-ui/react-compose-refs": ["@radix-ui/react-compose-refs@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-Y9VzoRDSJtgFMUCoiZBDVo084VQ5hfpXxVE+NgkdNsjiDBByiImMZKKhxMwCbdHvhlENG6a833CbFkOQvTricw=="],
|
||||
|
||||
"@radix-ui/react-popper/@radix-ui/react-context": ["@radix-ui/react-context@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-UASk9zi+crv9WteK/NU4PLvOoL3OuE6BWVKNF6hPRBtYBDXQ2u5iu3O59zUlJiTVvkyuycnqrztsHVJwcK9K+Q=="],
|
||||
|
||||
"@radix-ui/react-popper/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.0.2", "", { "dependencies": { "@radix-ui/react-slot": "1.1.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-Ec/0d38EIuvDF+GZjcMU/Ze6MxntVJYO/fRlCPhCaVUyPY9WTalHJw54tp9sXeJo3tlShWpy41vQRgLRGOuz+w=="],
|
||||
|
||||
"@radix-ui/react-popper/@radix-ui/react-use-callback-ref": ["@radix-ui/react-use-callback-ref@1.1.0", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-CasTfvsy+frcFkbXtSJ2Zu9JHpN8TYKxkgJGWbjiZhFivxaeW7rMeZt7QELGVLaYVfFMsKHjb7Ak0nMEe+2Vfw=="],
|
||||
|
||||
"@radix-ui/react-popper/@radix-ui/react-use-layout-effect": ["@radix-ui/react-use-layout-effect@1.1.0", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-+FPE0rOdziWSrH9athwI1R0HDVbWlEhd+FR+aSDk4uWGmSJ9Z54sdZVDQPZAinJhJXwfT+qnj969mCsT2gfm5w=="],
|
||||
|
||||
"@radix-ui/react-portal/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.0.2", "", { "dependencies": { "@radix-ui/react-slot": "1.1.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-Ec/0d38EIuvDF+GZjcMU/Ze6MxntVJYO/fRlCPhCaVUyPY9WTalHJw54tp9sXeJo3tlShWpy41vQRgLRGOuz+w=="],
|
||||
|
||||
"@radix-ui/react-portal/@radix-ui/react-use-layout-effect": ["@radix-ui/react-use-layout-effect@1.1.0", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-+FPE0rOdziWSrH9athwI1R0HDVbWlEhd+FR+aSDk4uWGmSJ9Z54sdZVDQPZAinJhJXwfT+qnj969mCsT2gfm5w=="],
|
||||
|
||||
"@radix-ui/react-primitive/@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.0", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-ujc+V6r0HNDviYqIK3rW4ffgYiZ8g5DEHrGJVk4x7kTlLXRDILnKX9vAUYeIsLOoDpDJ0ujpqMkjH4w2ofuo6w=="],
|
||||
|
||||
"@radix-ui/react-roving-focus/@radix-ui/primitive": ["@radix-ui/primitive@1.1.1", "", {}, "sha512-SJ31y+Q/zAyShtXJc8x83i9TYdbAfHZ++tUZnvjJJqFjzsdUnKsxPL6IEtBlxKkU7yzer//GQtZSV4GbldL3YA=="],
|
||||
|
||||
"@radix-ui/react-roving-focus/@radix-ui/react-compose-refs": ["@radix-ui/react-compose-refs@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-Y9VzoRDSJtgFMUCoiZBDVo084VQ5hfpXxVE+NgkdNsjiDBByiImMZKKhxMwCbdHvhlENG6a833CbFkOQvTricw=="],
|
||||
|
||||
"@radix-ui/react-roving-focus/@radix-ui/react-context": ["@radix-ui/react-context@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-UASk9zi+crv9WteK/NU4PLvOoL3OuE6BWVKNF6hPRBtYBDXQ2u5iu3O59zUlJiTVvkyuycnqrztsHVJwcK9K+Q=="],
|
||||
|
||||
"@radix-ui/react-roving-focus/@radix-ui/react-direction": ["@radix-ui/react-direction@1.1.0", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-BUuBvgThEiAXh2DWu93XsT+a3aWrGqolGlqqw5VU1kG7p/ZH2cuDlM1sRLNnY3QcBS69UIz2mcKhMxDsdewhjg=="],
|
||||
|
||||
"@radix-ui/react-roving-focus/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.0.2", "", { "dependencies": { "@radix-ui/react-slot": "1.1.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-Ec/0d38EIuvDF+GZjcMU/Ze6MxntVJYO/fRlCPhCaVUyPY9WTalHJw54tp9sXeJo3tlShWpy41vQRgLRGOuz+w=="],
|
||||
|
||||
"@radix-ui/react-roving-focus/@radix-ui/react-use-callback-ref": ["@radix-ui/react-use-callback-ref@1.1.0", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-CasTfvsy+frcFkbXtSJ2Zu9JHpN8TYKxkgJGWbjiZhFivxaeW7rMeZt7QELGVLaYVfFMsKHjb7Ak0nMEe+2Vfw=="],
|
||||
|
||||
"@radix-ui/react-slot/@radix-ui/react-compose-refs": ["@radix-ui/react-compose-refs@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-Y9VzoRDSJtgFMUCoiZBDVo084VQ5hfpXxVE+NgkdNsjiDBByiImMZKKhxMwCbdHvhlENG6a833CbFkOQvTricw=="],
|
||||
|
||||
"@radix-ui/react-tabs/@radix-ui/primitive": ["@radix-ui/primitive@1.1.1", "", {}, "sha512-SJ31y+Q/zAyShtXJc8x83i9TYdbAfHZ++tUZnvjJJqFjzsdUnKsxPL6IEtBlxKkU7yzer//GQtZSV4GbldL3YA=="],
|
||||
|
||||
"@radix-ui/react-tabs/@radix-ui/react-context": ["@radix-ui/react-context@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-UASk9zi+crv9WteK/NU4PLvOoL3OuE6BWVKNF6hPRBtYBDXQ2u5iu3O59zUlJiTVvkyuycnqrztsHVJwcK9K+Q=="],
|
||||
|
||||
"@radix-ui/react-tabs/@radix-ui/react-direction": ["@radix-ui/react-direction@1.1.0", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-BUuBvgThEiAXh2DWu93XsT+a3aWrGqolGlqqw5VU1kG7p/ZH2cuDlM1sRLNnY3QcBS69UIz2mcKhMxDsdewhjg=="],
|
||||
|
||||
"@radix-ui/react-tabs/@radix-ui/react-presence": ["@radix-ui/react-presence@1.1.2", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.1", "@radix-ui/react-use-layout-effect": "1.1.0" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-18TFr80t5EVgL9x1SwF/YGtfG+l0BS0PRAlCWBDoBEiDQjeKgnNZRVJp/oVBl24sr3Gbfwc/Qpj4OcWTQMsAEg=="],
|
||||
|
||||
"@radix-ui/react-tabs/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.0.2", "", { "dependencies": { "@radix-ui/react-slot": "1.1.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-Ec/0d38EIuvDF+GZjcMU/Ze6MxntVJYO/fRlCPhCaVUyPY9WTalHJw54tp9sXeJo3tlShWpy41vQRgLRGOuz+w=="],
|
||||
|
||||
"@radix-ui/react-use-controllable-state/@radix-ui/react-use-callback-ref": ["@radix-ui/react-use-callback-ref@1.1.0", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-CasTfvsy+frcFkbXtSJ2Zu9JHpN8TYKxkgJGWbjiZhFivxaeW7rMeZt7QELGVLaYVfFMsKHjb7Ak0nMEe+2Vfw=="],
|
||||
|
||||
"@radix-ui/react-use-escape-keydown/@radix-ui/react-use-callback-ref": ["@radix-ui/react-use-callback-ref@1.1.0", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-CasTfvsy+frcFkbXtSJ2Zu9JHpN8TYKxkgJGWbjiZhFivxaeW7rMeZt7QELGVLaYVfFMsKHjb7Ak0nMEe+2Vfw=="],
|
||||
|
||||
"@radix-ui/react-use-size/@radix-ui/react-use-layout-effect": ["@radix-ui/react-use-layout-effect@1.1.0", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-+FPE0rOdziWSrH9athwI1R0HDVbWlEhd+FR+aSDk4uWGmSJ9Z54sdZVDQPZAinJhJXwfT+qnj969mCsT2gfm5w=="],
|
||||
|
||||
"@radix-ui/react-visually-hidden/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.0.2", "", { "dependencies": { "@radix-ui/react-slot": "1.1.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-Ec/0d38EIuvDF+GZjcMU/Ze6MxntVJYO/fRlCPhCaVUyPY9WTalHJw54tp9sXeJo3tlShWpy41vQRgLRGOuz+w=="],
|
||||
|
||||
"@types/fs-extra/@types/node": ["@types/node@22.13.1", "", { "dependencies": { "undici-types": "~6.20.0" } }, "sha512-jK8uzQlrvXqEU91UxiK5J7pKHyzgnI1Qnl0QDHIgVGuolJhRb9EEl28Cj9b3rGR8B2lhFCtvIm5os8lFnO/1Ew=="],
|
||||
|
||||
"@types/jsonfile/@types/node": ["@types/node@22.13.1", "", { "dependencies": { "undici-types": "~6.20.0" } }, "sha512-jK8uzQlrvXqEU91UxiK5J7pKHyzgnI1Qnl0QDHIgVGuolJhRb9EEl28Cj9b3rGR8B2lhFCtvIm5os8lFnO/1Ew=="],
|
||||
@@ -1783,8 +1914,14 @@
|
||||
|
||||
"fast-glob/glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="],
|
||||
|
||||
"fumadocs-ui/@radix-ui/react-direction": ["@radix-ui/react-direction@1.1.0", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-BUuBvgThEiAXh2DWu93XsT+a3aWrGqolGlqqw5VU1kG7p/ZH2cuDlM1sRLNnY3QcBS69UIz2mcKhMxDsdewhjg=="],
|
||||
|
||||
"fumadocs-ui/@radix-ui/react-scroll-area": ["@radix-ui/react-scroll-area@1.2.3", "", { "dependencies": { "@radix-ui/number": "1.1.0", "@radix-ui/primitive": "1.1.1", "@radix-ui/react-compose-refs": "1.1.1", "@radix-ui/react-context": "1.1.1", "@radix-ui/react-direction": "1.1.0", "@radix-ui/react-presence": "1.1.2", "@radix-ui/react-primitive": "2.0.2", "@radix-ui/react-use-callback-ref": "1.1.0", "@radix-ui/react-use-layout-effect": "1.1.0" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-l7+NNBfBYYJa9tNqVcP2AGvxdE3lmE6kFTBXdvHgUaZuy+4wGCL1Cl2AfaR7RKyimj7lZURGLwFO59k4eBnDJQ=="],
|
||||
|
||||
"fumadocs-ui/lucide-react": ["lucide-react@0.483.0", "", { "peerDependencies": { "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-WldsY17Qb/T3VZdMnVQ9C3DDIP7h1ViDTHVdVGnLZcvHNg30zH/MTQ04RTORjexoGmpsXroiQXZ4QyR0kBy0FA=="],
|
||||
|
||||
"fumadocs-ui/tailwind-merge": ["tailwind-merge@3.0.2", "", {}, "sha512-l7z+OYZ7mu3DTqrL88RiKrKIqO3NcpEO8V/Od04bNpvk0kiIFndGEoqfuzvj4yuhRkHKjRkII2z+KS2HfPcSxw=="],
|
||||
|
||||
"glob/minimatch": ["minimatch@9.0.5", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow=="],
|
||||
|
||||
"gray-matter/js-yaml": ["js-yaml@3.14.1", "", { "dependencies": { "argparse": "^1.0.7", "esprima": "^4.0.0" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g=="],
|
||||
@@ -1885,6 +2022,14 @@
|
||||
|
||||
"@next/eslint-plugin-next/fast-glob/glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="],
|
||||
|
||||
"@radix-ui/react-dialog/@radix-ui/react-presence/@radix-ui/react-use-layout-effect": ["@radix-ui/react-use-layout-effect@1.1.0", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-+FPE0rOdziWSrH9athwI1R0HDVbWlEhd+FR+aSDk4uWGmSJ9Z54sdZVDQPZAinJhJXwfT+qnj969mCsT2gfm5w=="],
|
||||
|
||||
"@radix-ui/react-popover/@radix-ui/react-presence/@radix-ui/react-use-layout-effect": ["@radix-ui/react-use-layout-effect@1.1.0", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-+FPE0rOdziWSrH9athwI1R0HDVbWlEhd+FR+aSDk4uWGmSJ9Z54sdZVDQPZAinJhJXwfT+qnj969mCsT2gfm5w=="],
|
||||
|
||||
"@radix-ui/react-tabs/@radix-ui/react-presence/@radix-ui/react-compose-refs": ["@radix-ui/react-compose-refs@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-Y9VzoRDSJtgFMUCoiZBDVo084VQ5hfpXxVE+NgkdNsjiDBByiImMZKKhxMwCbdHvhlENG6a833CbFkOQvTricw=="],
|
||||
|
||||
"@radix-ui/react-tabs/@radix-ui/react-presence/@radix-ui/react-use-layout-effect": ["@radix-ui/react-use-layout-effect@1.1.0", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-+FPE0rOdziWSrH9athwI1R0HDVbWlEhd+FR+aSDk4uWGmSJ9Z54sdZVDQPZAinJhJXwfT+qnj969mCsT2gfm5w=="],
|
||||
|
||||
"@types/fs-extra/@types/node/undici-types": ["undici-types@6.20.0", "", {}, "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg=="],
|
||||
|
||||
"@types/jsonfile/@types/node/undici-types": ["undici-types@6.20.0", "", {}, "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg=="],
|
||||
@@ -1895,6 +2040,22 @@
|
||||
|
||||
"cli-truncate/string-width/strip-ansi": ["strip-ansi@7.1.0", "", { "dependencies": { "ansi-regex": "^6.0.1" } }, "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ=="],
|
||||
|
||||
"fumadocs-ui/@radix-ui/react-scroll-area/@radix-ui/number": ["@radix-ui/number@1.1.0", "", {}, "sha512-V3gRzhVNU1ldS5XhAPTom1fOIo4ccrjjJgmE+LI2h/WaFpHmx0MQApT+KZHnx8abG6Avtfcz4WoEciMnpFT3HQ=="],
|
||||
|
||||
"fumadocs-ui/@radix-ui/react-scroll-area/@radix-ui/primitive": ["@radix-ui/primitive@1.1.1", "", {}, "sha512-SJ31y+Q/zAyShtXJc8x83i9TYdbAfHZ++tUZnvjJJqFjzsdUnKsxPL6IEtBlxKkU7yzer//GQtZSV4GbldL3YA=="],
|
||||
|
||||
"fumadocs-ui/@radix-ui/react-scroll-area/@radix-ui/react-compose-refs": ["@radix-ui/react-compose-refs@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-Y9VzoRDSJtgFMUCoiZBDVo084VQ5hfpXxVE+NgkdNsjiDBByiImMZKKhxMwCbdHvhlENG6a833CbFkOQvTricw=="],
|
||||
|
||||
"fumadocs-ui/@radix-ui/react-scroll-area/@radix-ui/react-context": ["@radix-ui/react-context@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-UASk9zi+crv9WteK/NU4PLvOoL3OuE6BWVKNF6hPRBtYBDXQ2u5iu3O59zUlJiTVvkyuycnqrztsHVJwcK9K+Q=="],
|
||||
|
||||
"fumadocs-ui/@radix-ui/react-scroll-area/@radix-ui/react-presence": ["@radix-ui/react-presence@1.1.2", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.1", "@radix-ui/react-use-layout-effect": "1.1.0" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-18TFr80t5EVgL9x1SwF/YGtfG+l0BS0PRAlCWBDoBEiDQjeKgnNZRVJp/oVBl24sr3Gbfwc/Qpj4OcWTQMsAEg=="],
|
||||
|
||||
"fumadocs-ui/@radix-ui/react-scroll-area/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.0.2", "", { "dependencies": { "@radix-ui/react-slot": "1.1.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-Ec/0d38EIuvDF+GZjcMU/Ze6MxntVJYO/fRlCPhCaVUyPY9WTalHJw54tp9sXeJo3tlShWpy41vQRgLRGOuz+w=="],
|
||||
|
||||
"fumadocs-ui/@radix-ui/react-scroll-area/@radix-ui/react-use-callback-ref": ["@radix-ui/react-use-callback-ref@1.1.0", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-CasTfvsy+frcFkbXtSJ2Zu9JHpN8TYKxkgJGWbjiZhFivxaeW7rMeZt7QELGVLaYVfFMsKHjb7Ak0nMEe+2Vfw=="],
|
||||
|
||||
"fumadocs-ui/@radix-ui/react-scroll-area/@radix-ui/react-use-layout-effect": ["@radix-ui/react-use-layout-effect@1.1.0", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-+FPE0rOdziWSrH9athwI1R0HDVbWlEhd+FR+aSDk4uWGmSJ9Z54sdZVDQPZAinJhJXwfT+qnj969mCsT2gfm5w=="],
|
||||
|
||||
"glob/minimatch/brace-expansion": ["brace-expansion@2.0.1", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA=="],
|
||||
|
||||
"gray-matter/js-yaml/argparse": ["argparse@1.0.10", "", { "dependencies": { "sprintf-js": "~1.0.2" } }, "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg=="],
|
||||
|
||||
Reference in New Issue
Block a user