├── .gitignore ├── README.md ├── frontend ├── .gitignore ├── .prettierignore ├── .prettierrc ├── README.md ├── app │ ├── dashboard │ │ └── page.tsx │ ├── globals.css │ ├── layout.tsx │ ├── login │ │ └── page.tsx │ └── register │ │ └── page.tsx ├── components.json ├── components │ ├── builds-panel.tsx │ ├── command-input.tsx │ ├── geo-map.tsx │ ├── logs-panel.tsx │ ├── terminal.tsx │ ├── theme-provider.tsx │ └── ui │ │ ├── accordion.tsx │ │ ├── alert-dialog.tsx │ │ ├── alert.tsx │ │ ├── aspect-ratio.tsx │ │ ├── avatar.tsx │ │ ├── badge.tsx │ │ ├── breadcrumb.tsx │ │ ├── button.tsx │ │ ├── calendar.tsx │ │ ├── card.tsx │ │ ├── carousel.tsx │ │ ├── chart.tsx │ │ ├── checkbox.tsx │ │ ├── collapsible.tsx │ │ ├── command.tsx │ │ ├── context-menu.tsx │ │ ├── dialog.tsx │ │ ├── drawer.tsx │ │ ├── dropdown-menu.tsx │ │ ├── form.tsx │ │ ├── hover-card.tsx │ │ ├── input-otp.tsx │ │ ├── input.tsx │ │ ├── label.tsx │ │ ├── menubar.tsx │ │ ├── navigation-menu.tsx │ │ ├── pagination.tsx │ │ ├── popover.tsx │ │ ├── progress.tsx │ │ ├── radio-group.tsx │ │ ├── resizable.tsx │ │ ├── scroll-area.tsx │ │ ├── select.tsx │ │ ├── separator.tsx │ │ ├── sheet.tsx │ │ ├── sidebar.tsx │ │ ├── skeleton.tsx │ │ ├── slider.tsx │ │ ├── sonner.tsx │ │ ├── switch.tsx │ │ ├── table.tsx │ │ ├── tabs.tsx │ │ ├── textarea.tsx │ │ ├── toast.tsx │ │ ├── toaster.tsx │ │ ├── toggle-group.tsx │ │ ├── toggle.tsx │ │ ├── tooltip.tsx │ │ ├── use-mobile.tsx │ │ └── use-toast.ts ├── cz.yaml ├── eslint.config.js ├── hooks │ ├── use-mobile.tsx │ └── use-toast.ts ├── index.html ├── knip.config.ts ├── lib │ ├── api.ts │ ├── auth.tsx │ └── utils.ts ├── middleware.ts ├── next.config.mjs ├── package-lock.json ├── package.json ├── pnpm-lock.yaml ├── postcss.config.js ├── postcss.config.mjs ├── public │ ├── earth-dark.jpg │ ├── images │ │ ├── favicon.png │ │ ├── favicon.svg │ │ ├── favicon_light.png │ │ ├── favicon_light.svg │ │ └── shadcn-admin.png │ ├── placeholder-logo.png │ ├── placeholder-logo.svg │ ├── placeholder-user.jpg │ ├── placeholder.jpg │ └── placeholder.svg ├── src │ ├── assets │ │ └── vite.svg │ ├── components │ │ ├── coming-soon.tsx │ │ ├── command-menu.tsx │ │ ├── confirm-dialog.tsx │ │ ├── layout │ │ │ ├── app-sidebar.tsx │ │ │ ├── data │ │ │ │ └── sidebar-data.ts │ │ │ ├── header.tsx │ │ │ ├── main.tsx │ │ │ ├── nav-group.tsx │ │ │ ├── nav-user.tsx │ │ │ ├── team-switcher.tsx │ │ │ ├── top-nav.tsx │ │ │ └── types.ts │ │ ├── long-text.tsx │ │ ├── navigation-progress.tsx │ │ ├── password-input.tsx │ │ ├── pin-input.tsx │ │ ├── profile-dropdown.tsx │ │ ├── search.tsx │ │ ├── select-dropdown.tsx │ │ ├── skip-to-main.tsx │ │ ├── theme-switch.tsx │ │ └── ui │ │ │ ├── alert-dialog.tsx │ │ │ ├── alert.tsx │ │ │ ├── avatar.tsx │ │ │ ├── badge.tsx │ │ │ ├── button.tsx │ │ │ ├── calendar.tsx │ │ │ ├── card.tsx │ │ │ ├── checkbox.tsx │ │ │ ├── collapsible.tsx │ │ │ ├── command.tsx │ │ │ ├── dialog.tsx │ │ │ ├── dropdown-menu.tsx │ │ │ ├── form.tsx │ │ │ ├── input.tsx │ │ │ ├── label.tsx │ │ │ ├── popover.tsx │ │ │ ├── radio-group.tsx │ │ │ ├── scroll-area.tsx │ │ │ ├── select.tsx │ │ │ ├── separator.tsx │ │ │ ├── sheet.tsx │ │ │ ├── sidebar.tsx │ │ │ ├── skeleton.tsx │ │ │ ├── switch.tsx │ │ │ ├── table.tsx │ │ │ ├── tabs.tsx │ │ │ ├── textarea.tsx │ │ │ ├── toast.tsx │ │ │ ├── toaster.tsx │ │ │ └── tooltip.tsx │ ├── config │ │ └── fonts.ts │ ├── context │ │ ├── font-context.tsx │ │ ├── search-context.tsx │ │ └── theme-context.tsx │ ├── features │ │ ├── apps │ │ │ ├── data │ │ │ │ └── apps.tsx │ │ │ └── index.tsx │ │ ├── auth │ │ │ ├── auth-layout.tsx │ │ │ ├── forgot-password │ │ │ │ ├── components │ │ │ │ │ └── forgot-password-form.tsx │ │ │ │ └── index.tsx │ │ │ ├── otp │ │ │ │ ├── components │ │ │ │ │ └── otp-form.tsx │ │ │ │ └── index.tsx │ │ │ ├── sign-in │ │ │ │ ├── components │ │ │ │ │ └── user-auth-form.tsx │ │ │ │ ├── index.tsx │ │ │ │ └── sign-in-2.tsx │ │ │ └── sign-up │ │ │ │ ├── components │ │ │ │ └── sign-up-form.tsx │ │ │ │ └── index.tsx │ │ ├── chats │ │ │ ├── components │ │ │ │ └── new-chat.tsx │ │ │ ├── data │ │ │ │ ├── chat-types.ts │ │ │ │ └── convo.json │ │ │ └── index.tsx │ │ ├── dashboard │ │ │ ├── components │ │ │ │ ├── overview.tsx │ │ │ │ └── recent-sales.tsx │ │ │ └── index.tsx │ │ ├── errors │ │ │ ├── forbidden.tsx │ │ │ ├── general-error.tsx │ │ │ ├── maintenance-error.tsx │ │ │ ├── not-found-error.tsx │ │ │ └── unauthorized-error.tsx │ │ ├── settings │ │ │ ├── account │ │ │ │ ├── account-form.tsx │ │ │ │ └── index.tsx │ │ │ ├── appearance │ │ │ │ ├── appearance-form.tsx │ │ │ │ └── index.tsx │ │ │ ├── components │ │ │ │ ├── content-section.tsx │ │ │ │ └── sidebar-nav.tsx │ │ │ ├── display │ │ │ │ ├── display-form.tsx │ │ │ │ └── index.tsx │ │ │ ├── index.tsx │ │ │ ├── notifications │ │ │ │ ├── index.tsx │ │ │ │ └── notifications-form.tsx │ │ │ └── profile │ │ │ │ ├── index.tsx │ │ │ │ └── profile-form.tsx │ │ ├── tasks │ │ │ ├── components │ │ │ │ ├── columns.tsx │ │ │ │ ├── data-table-column-header.tsx │ │ │ │ ├── data-table-faceted-filter.tsx │ │ │ │ ├── data-table-pagination.tsx │ │ │ │ ├── data-table-row-actions.tsx │ │ │ │ ├── data-table-toolbar.tsx │ │ │ │ ├── data-table-view-options.tsx │ │ │ │ ├── data-table.tsx │ │ │ │ ├── tasks-dialogs.tsx │ │ │ │ ├── tasks-import-dialog.tsx │ │ │ │ ├── tasks-mutate-drawer.tsx │ │ │ │ └── tasks-primary-buttons.tsx │ │ │ ├── context │ │ │ │ └── tasks-context.tsx │ │ │ ├── data │ │ │ │ ├── data.tsx │ │ │ │ ├── schema.ts │ │ │ │ └── tasks.ts │ │ │ └── index.tsx │ │ └── users │ │ │ ├── components │ │ │ ├── data-table-column-header.tsx │ │ │ ├── data-table-faceted-filter.tsx │ │ │ ├── data-table-pagination.tsx │ │ │ ├── data-table-row-actions.tsx │ │ │ ├── data-table-toolbar.tsx │ │ │ ├── data-table-view-options.tsx │ │ │ ├── users-action-dialog.tsx │ │ │ ├── users-columns.tsx │ │ │ ├── users-delete-dialog.tsx │ │ │ ├── users-dialogs.tsx │ │ │ ├── users-invite-dialog.tsx │ │ │ ├── users-primary-buttons.tsx │ │ │ └── users-table.tsx │ │ │ ├── context │ │ │ └── users-context.tsx │ │ │ ├── data │ │ │ ├── data.ts │ │ │ ├── schema.ts │ │ │ └── users.ts │ │ │ └── index.tsx │ ├── hooks │ │ ├── use-dialog-state.tsx │ │ ├── use-mobile.tsx │ │ └── use-toast.ts │ ├── index.css │ ├── lib │ │ └── utils.ts │ ├── main.tsx │ ├── routeTree.gen.ts │ ├── routes │ │ ├── (auth) │ │ │ ├── 500.tsx │ │ │ ├── forgot-password.lazy.tsx │ │ │ ├── otp.tsx │ │ │ ├── sign-in-2.lazy.tsx │ │ │ ├── sign-in.tsx │ │ │ └── sign-up.lazy.tsx │ │ ├── (errors) │ │ │ ├── 401.lazy.tsx │ │ │ ├── 403.lazy.tsx │ │ │ ├── 404.lazy.tsx │ │ │ ├── 500.lazy.tsx │ │ │ └── 503.lazy.tsx │ │ ├── __root.tsx │ │ └── _authenticated │ │ │ ├── apps │ │ │ └── index.lazy.tsx │ │ │ ├── chats │ │ │ └── index.lazy.tsx │ │ │ ├── help-center │ │ │ └── index.lazy.tsx │ │ │ ├── index.tsx │ │ │ ├── route.tsx │ │ │ ├── settings │ │ │ ├── account.lazy.tsx │ │ │ ├── appearance.lazy.tsx │ │ │ ├── display.lazy.tsx │ │ │ ├── index.lazy.tsx │ │ │ ├── notifications.lazy.tsx │ │ │ └── route.lazy.tsx │ │ │ ├── tasks │ │ │ └── index.lazy.tsx │ │ │ └── users │ │ │ └── index.lazy.tsx │ ├── stores │ │ └── authStore.ts │ ├── utils │ │ └── handle-server-error.ts │ └── vite-env.d.ts ├── styles │ └── globals.css ├── tailwind.config.js ├── tailwind.config.ts ├── tsconfig.app.json ├── tsconfig.json ├── tsconfig.node.json └── vite.config.ts ├── payload ├── discovery │ ├── browser.go │ ├── crypto.go │ ├── discovery.go │ └── misc.go ├── extraction │ ├── browser.go │ ├── system.go │ └── zip.go ├── fileops │ └── operations.go ├── go.mod ├── main.go └── static │ └── static.go └── server ├── chainbreak ├── .gitignore ├── LICENSE ├── README.md ├── chainbreaker.py └── schema.py ├── crypto ├── aes.go ├── auth.go ├── gecko.go ├── pkcs.go └── utils.go ├── decode ├── chromium.go ├── gecko.go ├── keychain.go └── utils.go ├── extract ├── chromium.go └── gecko.go ├── fileops └── fileops.go ├── go.mod ├── go.sum ├── handlers ├── auth.go ├── builder.go └── upload.go ├── main.go ├── models └── models.go ├── routes ├── build.go ├── logs.go └── upload.go └── static └── static.go /.gitignore: -------------------------------------------------------------------------------- 1 | **/.DS_Store 2 | *.keychain-db 3 | /server/log-directory/* 4 | /server/decoded-output/* 5 | /built/* 6 | /server/builds.db 7 | /server/paradox_server 8 | /payload/paradox_payload 9 | /frontend/node_modules 10 | /frontend/dist 11 | -------------------------------------------------------------------------------- /frontend/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | 6 | # next.js 7 | /.next/ 8 | /out/ 9 | 10 | # production 11 | /build 12 | 13 | # debug 14 | npm-debug.log* 15 | yarn-debug.log* 16 | yarn-error.log* 17 | .pnpm-debug.log* 18 | 19 | # env files 20 | .env* 21 | 22 | # vercel 23 | .vercel 24 | 25 | # typescript 26 | *.tsbuildinfo 27 | next-env.d.ts -------------------------------------------------------------------------------- /frontend/.prettierignore: -------------------------------------------------------------------------------- 1 | # Ignore everything 2 | /* 3 | 4 | # Except these files & folders 5 | !/src 6 | !index.html 7 | !package.json 8 | !tailwind.config.js 9 | !tsconfig.json 10 | !tsconfig.node.json 11 | !vite.config.ts 12 | !.prettierrc 13 | !README.md 14 | !eslint.config.js 15 | !postcss.config.js 16 | 17 | # Ignore auto generated routeTree.gen.ts 18 | /src/routeTree.gen.ts -------------------------------------------------------------------------------- /frontend/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "arrowParens": "always", 3 | "semi": false, 4 | "tabWidth": 2, 5 | "printWidth": 80, 6 | "singleQuote": true, 7 | "jsxSingleQuote": true, 8 | "trailingComma": "es5", 9 | "bracketSpacing": true, 10 | "endOfLine": "lf", 11 | "plugins": [ 12 | "@trivago/prettier-plugin-sort-imports", 13 | "prettier-plugin-tailwindcss" 14 | ], 15 | "importOrder": [ 16 | "^path$", 17 | "^vite$", 18 | "^@vitejs/(.*)$", 19 | "^react$", 20 | "^react-dom/client$", 21 | "^react/(.*)$", 22 | "^globals$", 23 | "^zod$", 24 | "^axios$", 25 | "^date-fns$", 26 | "^js-cookie$", 27 | "^react-hook-form$", 28 | "^use-intl$", 29 | "^@radix-ui/(.*)$", 30 | "^@hookform/resolvers/zod$", 31 | "^@tanstack/react-query$", 32 | "^@tanstack/react-router$", 33 | "^@tanstack/react-table$", 34 | "^@tabler/icons-react$", 35 | "", 36 | "^@/assets/(.*)", 37 | "^@/api/(.*)$", 38 | "^@/stores/(.*)$", 39 | "^@/lib/(.*)$", 40 | "^@/utils/(.*)$", 41 | "^@/constants/(.*)$", 42 | "^@/context/(.*)$", 43 | "^@/hooks/(.*)$", 44 | "^@/components/layouts/(.*)$", 45 | "^@/components/ui/(.*)$", 46 | "^@/components/errors/(.*)$", 47 | "^@/components/(.*)$", 48 | "^@/features/(.*)$", 49 | "^[./]" 50 | ] 51 | } 52 | -------------------------------------------------------------------------------- /frontend/README.md: -------------------------------------------------------------------------------- 1 | # frontend pasted with v0 and claude :thumbsup: -------------------------------------------------------------------------------- /frontend/app/globals.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | 5 | :root { 6 | --foreground-rgb: 0, 0, 0; 7 | --background-start-rgb: 214, 219, 220; 8 | --background-end-rgb: 255, 255, 255; 9 | } 10 | 11 | @media (prefers-color-scheme: dark) { 12 | :root { 13 | --foreground-rgb: 255, 255, 255; 14 | --background-rgb: 0, 0, 0; 15 | } 16 | } 17 | 18 | body { 19 | color: rgb(var(--foreground-rgb)); 20 | background: rgb(var(--background-rgb)); 21 | } 22 | 23 | .custom-scrollbar::-webkit-scrollbar { 24 | width: 8px; 25 | height: 8px; 26 | } 27 | 28 | .custom-scrollbar::-webkit-scrollbar-track { 29 | background: rgba(0, 0, 0, 0.2); 30 | } 31 | 32 | .custom-scrollbar::-webkit-scrollbar-thumb { 33 | background: rgba(76, 175, 80, 0.3); 34 | border-radius: 4px; 35 | } 36 | 37 | .custom-scrollbar::-webkit-scrollbar-thumb:hover { 38 | background: rgba(76, 175, 80, 0.5); 39 | } 40 | 41 | .glitch-text { 42 | position: relative; 43 | text-shadow: 0 0 5px rgba(76, 175, 80, 0.7); 44 | animation: glitch 5s infinite; 45 | } 46 | 47 | @keyframes glitch { 48 | 0% { 49 | text-shadow: 0 0 5px rgba(76, 175, 80, 0.7); 50 | } 51 | 2% { 52 | text-shadow: 0.5px 0 0 rgba(255, 0, 0, 0.7), -0.5px 0 0 rgba(0, 255, 0, 0.7); 53 | } 54 | 4% { 55 | text-shadow: 0.5px 0 0 rgba(255, 0, 0, 0.7), -0.5px 0 0 rgba(0, 255, 0, 0.7); 56 | } 57 | 6% { 58 | text-shadow: 0.5px 0 0 rgba(255, 0, 0, 0), -0.5px 0 0 rgba(0, 255, 0, 0); 59 | } 60 | 8% { 61 | text-shadow: 0.5px 0 0 rgba(255, 0, 0, 0.7), -0.5px 0 0 rgba(0, 255, 0, 0.7); 62 | } 63 | 10% { 64 | text-shadow: 0 0 5px rgba(76, 175, 80, 0.7); 65 | } 66 | 100% { 67 | text-shadow: 0 0 5px rgba(76, 175, 80, 0.7); 68 | } 69 | } 70 | 71 | .terminal-suggestion { 72 | color: rgba(76, 175, 80, 0.5); 73 | position: absolute; 74 | pointer-events: none; 75 | } 76 | 77 | /* Add a blinking cursor effect */ 78 | .cursor-blink { 79 | animation: blink 1s step-end infinite; 80 | } 81 | 82 | @keyframes blink { 83 | from, 84 | to { 85 | opacity: 1; 86 | } 87 | 50% { 88 | opacity: 0; 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /frontend/app/layout.tsx: -------------------------------------------------------------------------------- 1 | import type { Metadata } from 'next' 2 | import { Inter } from 'next/font/google' 3 | import './globals.css' 4 | import { AuthProvider } from '@/lib/auth' 5 | 6 | const inter = Inter({ subsets: ['latin'] }) 7 | 8 | export const metadata: Metadata = { 9 | title: 'Paradox Dashboard', 10 | description: 'Secure dashboard for managing Paradox server', 11 | } 12 | 13 | export default function RootLayout({ 14 | children, 15 | }: { 16 | children: React.ReactNode 17 | }) { 18 | return ( 19 | 20 | 21 | 22 | {children} 23 | 24 | 25 | 26 | ) 27 | } 28 | -------------------------------------------------------------------------------- /frontend/components.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://ui.shadcn.com/schema.json", 3 | "style": "default", 4 | "rsc": true, 5 | "tsx": true, 6 | "tailwind": { 7 | "config": "tailwind.config.ts", 8 | "css": "app/globals.css", 9 | "baseColor": "neutral", 10 | "cssVariables": true, 11 | "prefix": "" 12 | }, 13 | "aliases": { 14 | "components": "@/components", 15 | "utils": "@/lib/utils", 16 | "ui": "@/components/ui", 17 | "lib": "@/lib", 18 | "hooks": "@/hooks" 19 | }, 20 | "iconLibrary": "lucide" 21 | } -------------------------------------------------------------------------------- /frontend/components/command-input.tsx: -------------------------------------------------------------------------------- 1 | "use client" 2 | 3 | import { useState, type KeyboardEvent } from "react" 4 | 5 | interface CommandInputProps { 6 | onSubmit: (command: string) => void 7 | } 8 | 9 | export function CommandInput({ onSubmit }: CommandInputProps) { 10 | const [command, setCommand] = useState("") 11 | const [commandHistory, setCommandHistory] = useState([]) 12 | const [historyIndex, setHistoryIndex] = useState(-1) 13 | 14 | const handleKeyDown = (e: KeyboardEvent) => { 15 | if (e.key === "Enter" && command.trim()) { 16 | onSubmit(command) 17 | setCommandHistory((prev) => [command, ...prev]) 18 | setCommand("") 19 | setHistoryIndex(-1) 20 | } else if (e.key === "ArrowUp") { 21 | e.preventDefault() 22 | if (commandHistory.length > 0 && historyIndex < commandHistory.length - 1) { 23 | const newIndex = historyIndex + 1 24 | setHistoryIndex(newIndex) 25 | setCommand(commandHistory[newIndex]) 26 | } 27 | } else if (e.key === "ArrowDown") { 28 | e.preventDefault() 29 | if (historyIndex > 0) { 30 | const newIndex = historyIndex - 1 31 | setHistoryIndex(newIndex) 32 | setCommand(commandHistory[newIndex]) 33 | } else if (historyIndex === 0) { 34 | setHistoryIndex(-1) 35 | setCommand("") 36 | } 37 | } else if (e.key === "Tab") { 38 | e.preventDefault() 39 | 40 | 41 | const commandStart = command.toLowerCase().trim() 42 | 43 | if (commandStart === "") return 44 | 45 | const completions = [ 46 | "help", 47 | "build", 48 | "logs", 49 | "builds", 50 | "clear", 51 | "download build ", 52 | "download log ", 53 | "list builds", 54 | "list logs", 55 | ] 56 | 57 | const matches = completions.filter((c) => c.startsWith(commandStart)) 58 | 59 | if (matches.length === 1) { 60 | setCommand(matches[0]) 61 | } else if (matches.length > 1 && commandStart === "download") { 62 | setCommand("download ") 63 | } else if (matches.length > 1 && commandStart === "list") { 64 | setCommand("list ") 65 | } 66 | } 67 | } 68 | 69 | return ( 70 |
71 | $ 72 | setCommand(e.target.value)} 76 | onKeyDown={handleKeyDown} 77 | className="flex-1 bg-transparent border-none outline-none text-green-400 font-mono" 78 | placeholder="Enter command..." 79 | autoFocus 80 | /> 81 |
82 | ) 83 | } 84 | -------------------------------------------------------------------------------- /frontend/components/terminal.tsx: -------------------------------------------------------------------------------- 1 | "use client" 2 | 3 | import { useEffect, useRef } from "react" 4 | 5 | interface TerminalProps { 6 | lines: string[] 7 | } 8 | 9 | export function Terminal({ lines }: TerminalProps) { 10 | const terminalRef = useRef(null) 11 | 12 | useEffect(() => { 13 | if (terminalRef.current) { 14 | terminalRef.current.scrollTop = terminalRef.current.scrollHeight 15 | } 16 | }, [lines]) 17 | 18 | 19 | const formatLine = (line: string) => { 20 | if (line.startsWith(">")) { 21 | return ( 22 |
23 | $ 24 | {line.substring(2)} 25 |
26 | ) 27 | } else if (line.includes("downloaded successfully")) { 28 | return
{line}
29 | } else if (line.includes("Error:") || line.includes("failed")) { 30 | return
{line}
31 | } else if (line.includes("UUID:") || line.includes("ID:")) { 32 | return ( 33 |
34 | {line.split("|").map((segment, i) => ( 35 | 36 | {segment} 37 | {i < line.split("|").length - 1 ? "|" : ""} 38 | 39 | ))} 40 |
41 | ) 42 | } else if (line.startsWith("Available commands:") || line.startsWith(" ")) { 43 | return
{line}
44 | } else if (line.includes("Downloading")) { 45 | return
{line}
46 | } else { 47 | return
{line}
48 | } 49 | } 50 | 51 | return ( 52 |
56 | {lines.map((line, index) => ( 57 |
58 | {formatLine(line)} 59 |
60 | ))} 61 |
62 | ) 63 | } 64 | -------------------------------------------------------------------------------- /frontend/components/theme-provider.tsx: -------------------------------------------------------------------------------- 1 | 'use client' 2 | 3 | import * as React from 'react' 4 | import { 5 | ThemeProvider as NextThemesProvider, 6 | type ThemeProviderProps, 7 | } from 'next-themes' 8 | 9 | export function ThemeProvider({ children, ...props }: ThemeProviderProps) { 10 | return {children} 11 | } 12 | -------------------------------------------------------------------------------- /frontend/components/ui/accordion.tsx: -------------------------------------------------------------------------------- 1 | "use client" 2 | 3 | import * as React from "react" 4 | import * as AccordionPrimitive from "@radix-ui/react-accordion" 5 | import { ChevronDown } from "lucide-react" 6 | 7 | import { cn } from "@/lib/utils" 8 | 9 | const Accordion = AccordionPrimitive.Root 10 | 11 | const AccordionItem = React.forwardRef< 12 | React.ElementRef, 13 | React.ComponentPropsWithoutRef 14 | >(({ className, ...props }, ref) => ( 15 | 20 | )) 21 | AccordionItem.displayName = "AccordionItem" 22 | 23 | const AccordionTrigger = React.forwardRef< 24 | React.ElementRef, 25 | React.ComponentPropsWithoutRef 26 | >(({ className, children, ...props }, ref) => ( 27 | 28 | svg]:rotate-180", 32 | className 33 | )} 34 | {...props} 35 | > 36 | {children} 37 | 38 | 39 | 40 | )) 41 | AccordionTrigger.displayName = AccordionPrimitive.Trigger.displayName 42 | 43 | const AccordionContent = React.forwardRef< 44 | React.ElementRef, 45 | React.ComponentPropsWithoutRef 46 | >(({ className, children, ...props }, ref) => ( 47 | 52 |
{children}
53 |
54 | )) 55 | 56 | AccordionContent.displayName = AccordionPrimitive.Content.displayName 57 | 58 | export { Accordion, AccordionItem, AccordionTrigger, AccordionContent } 59 | -------------------------------------------------------------------------------- /frontend/components/ui/alert.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react" 2 | import { cva, type VariantProps } from "class-variance-authority" 3 | 4 | import { cn } from "@/lib/utils" 5 | 6 | const alertVariants = cva( 7 | "relative w-full rounded-lg border p-4 [&>svg~*]:pl-7 [&>svg+div]:translate-y-[-3px] [&>svg]:absolute [&>svg]:left-4 [&>svg]:top-4 [&>svg]:text-foreground", 8 | { 9 | variants: { 10 | variant: { 11 | default: "bg-background text-foreground", 12 | destructive: 13 | "border-destructive/50 text-destructive dark:border-destructive [&>svg]:text-destructive", 14 | }, 15 | }, 16 | defaultVariants: { 17 | variant: "default", 18 | }, 19 | } 20 | ) 21 | 22 | const Alert = React.forwardRef< 23 | HTMLDivElement, 24 | React.HTMLAttributes & VariantProps 25 | >(({ className, variant, ...props }, ref) => ( 26 |
32 | )) 33 | Alert.displayName = "Alert" 34 | 35 | const AlertTitle = React.forwardRef< 36 | HTMLParagraphElement, 37 | React.HTMLAttributes 38 | >(({ className, ...props }, ref) => ( 39 |
44 | )) 45 | AlertTitle.displayName = "AlertTitle" 46 | 47 | const AlertDescription = React.forwardRef< 48 | HTMLParagraphElement, 49 | React.HTMLAttributes 50 | >(({ className, ...props }, ref) => ( 51 |
56 | )) 57 | AlertDescription.displayName = "AlertDescription" 58 | 59 | export { Alert, AlertTitle, AlertDescription } 60 | -------------------------------------------------------------------------------- /frontend/components/ui/aspect-ratio.tsx: -------------------------------------------------------------------------------- 1 | "use client" 2 | 3 | import * as AspectRatioPrimitive from "@radix-ui/react-aspect-ratio" 4 | 5 | const AspectRatio = AspectRatioPrimitive.Root 6 | 7 | export { AspectRatio } 8 | -------------------------------------------------------------------------------- /frontend/components/ui/avatar.tsx: -------------------------------------------------------------------------------- 1 | "use client" 2 | 3 | import * as React from "react" 4 | import * as AvatarPrimitive from "@radix-ui/react-avatar" 5 | 6 | import { cn } from "@/lib/utils" 7 | 8 | const Avatar = React.forwardRef< 9 | React.ElementRef, 10 | React.ComponentPropsWithoutRef 11 | >(({ className, ...props }, ref) => ( 12 | 20 | )) 21 | Avatar.displayName = AvatarPrimitive.Root.displayName 22 | 23 | const AvatarImage = React.forwardRef< 24 | React.ElementRef, 25 | React.ComponentPropsWithoutRef 26 | >(({ className, ...props }, ref) => ( 27 | 32 | )) 33 | AvatarImage.displayName = AvatarPrimitive.Image.displayName 34 | 35 | const AvatarFallback = React.forwardRef< 36 | React.ElementRef, 37 | React.ComponentPropsWithoutRef 38 | >(({ className, ...props }, ref) => ( 39 | 47 | )) 48 | AvatarFallback.displayName = AvatarPrimitive.Fallback.displayName 49 | 50 | export { Avatar, AvatarImage, AvatarFallback } 51 | -------------------------------------------------------------------------------- /frontend/components/ui/badge.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react" 2 | import { cva, type VariantProps } from "class-variance-authority" 3 | 4 | import { cn } from "@/lib/utils" 5 | 6 | const badgeVariants = cva( 7 | "inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2", 8 | { 9 | variants: { 10 | variant: { 11 | default: 12 | "border-transparent bg-primary text-primary-foreground hover:bg-primary/80", 13 | secondary: 14 | "border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80", 15 | destructive: 16 | "border-transparent bg-destructive text-destructive-foreground hover:bg-destructive/80", 17 | outline: "text-foreground", 18 | }, 19 | }, 20 | defaultVariants: { 21 | variant: "default", 22 | }, 23 | } 24 | ) 25 | 26 | export interface BadgeProps 27 | extends React.HTMLAttributes, 28 | VariantProps {} 29 | 30 | function Badge({ className, variant, ...props }: BadgeProps) { 31 | return ( 32 |
33 | ) 34 | } 35 | 36 | export { Badge, badgeVariants } 37 | -------------------------------------------------------------------------------- /frontend/components/ui/button.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react" 2 | import { Slot } from "@radix-ui/react-slot" 3 | import { cva, type VariantProps } from "class-variance-authority" 4 | 5 | import { cn } from "@/lib/utils" 6 | 7 | const buttonVariants = cva( 8 | "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0", 9 | { 10 | variants: { 11 | variant: { 12 | default: "bg-primary text-primary-foreground hover:bg-primary/90", 13 | destructive: 14 | "bg-destructive text-destructive-foreground hover:bg-destructive/90", 15 | outline: 16 | "border border-input bg-background hover:bg-accent hover:text-accent-foreground", 17 | secondary: 18 | "bg-secondary text-secondary-foreground hover:bg-secondary/80", 19 | ghost: "hover:bg-accent hover:text-accent-foreground", 20 | link: "text-primary underline-offset-4 hover:underline", 21 | }, 22 | size: { 23 | default: "h-10 px-4 py-2", 24 | sm: "h-9 rounded-md px-3", 25 | lg: "h-11 rounded-md px-8", 26 | icon: "h-10 w-10", 27 | }, 28 | }, 29 | defaultVariants: { 30 | variant: "default", 31 | size: "default", 32 | }, 33 | } 34 | ) 35 | 36 | export interface ButtonProps 37 | extends React.ButtonHTMLAttributes, 38 | VariantProps { 39 | asChild?: boolean 40 | } 41 | 42 | const Button = React.forwardRef( 43 | ({ className, variant, size, asChild = false, ...props }, ref) => { 44 | const Comp = asChild ? Slot : "button" 45 | return ( 46 | 51 | ) 52 | } 53 | ) 54 | Button.displayName = "Button" 55 | 56 | export { Button, buttonVariants } 57 | -------------------------------------------------------------------------------- /frontend/components/ui/card.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react" 2 | 3 | import { cn } from "@/lib/utils" 4 | 5 | const Card = React.forwardRef< 6 | HTMLDivElement, 7 | React.HTMLAttributes 8 | >(({ className, ...props }, ref) => ( 9 |
17 | )) 18 | Card.displayName = "Card" 19 | 20 | const CardHeader = React.forwardRef< 21 | HTMLDivElement, 22 | React.HTMLAttributes 23 | >(({ className, ...props }, ref) => ( 24 |
29 | )) 30 | CardHeader.displayName = "CardHeader" 31 | 32 | const CardTitle = React.forwardRef< 33 | HTMLDivElement, 34 | React.HTMLAttributes 35 | >(({ className, ...props }, ref) => ( 36 |
44 | )) 45 | CardTitle.displayName = "CardTitle" 46 | 47 | const CardDescription = React.forwardRef< 48 | HTMLDivElement, 49 | React.HTMLAttributes 50 | >(({ className, ...props }, ref) => ( 51 |
56 | )) 57 | CardDescription.displayName = "CardDescription" 58 | 59 | const CardContent = React.forwardRef< 60 | HTMLDivElement, 61 | React.HTMLAttributes 62 | >(({ className, ...props }, ref) => ( 63 |
64 | )) 65 | CardContent.displayName = "CardContent" 66 | 67 | const CardFooter = React.forwardRef< 68 | HTMLDivElement, 69 | React.HTMLAttributes 70 | >(({ className, ...props }, ref) => ( 71 |
76 | )) 77 | CardFooter.displayName = "CardFooter" 78 | 79 | export { Card, CardHeader, CardFooter, CardTitle, CardDescription, CardContent } 80 | -------------------------------------------------------------------------------- /frontend/components/ui/checkbox.tsx: -------------------------------------------------------------------------------- 1 | "use client" 2 | 3 | import * as React from "react" 4 | import * as CheckboxPrimitive from "@radix-ui/react-checkbox" 5 | import { Check } from "lucide-react" 6 | 7 | import { cn } from "@/lib/utils" 8 | 9 | const Checkbox = React.forwardRef< 10 | React.ElementRef, 11 | React.ComponentPropsWithoutRef 12 | >(({ className, ...props }, ref) => ( 13 | 21 | 24 | 25 | 26 | 27 | )) 28 | Checkbox.displayName = CheckboxPrimitive.Root.displayName 29 | 30 | export { Checkbox } 31 | -------------------------------------------------------------------------------- /frontend/components/ui/collapsible.tsx: -------------------------------------------------------------------------------- 1 | "use client" 2 | 3 | import * as CollapsiblePrimitive from "@radix-ui/react-collapsible" 4 | 5 | const Collapsible = CollapsiblePrimitive.Root 6 | 7 | const CollapsibleTrigger = CollapsiblePrimitive.CollapsibleTrigger 8 | 9 | const CollapsibleContent = CollapsiblePrimitive.CollapsibleContent 10 | 11 | export { Collapsible, CollapsibleTrigger, CollapsibleContent } 12 | -------------------------------------------------------------------------------- /frontend/components/ui/hover-card.tsx: -------------------------------------------------------------------------------- 1 | "use client" 2 | 3 | import * as React from "react" 4 | import * as HoverCardPrimitive from "@radix-ui/react-hover-card" 5 | 6 | import { cn } from "@/lib/utils" 7 | 8 | const HoverCard = HoverCardPrimitive.Root 9 | 10 | const HoverCardTrigger = HoverCardPrimitive.Trigger 11 | 12 | const HoverCardContent = React.forwardRef< 13 | React.ElementRef, 14 | React.ComponentPropsWithoutRef 15 | >(({ className, align = "center", sideOffset = 4, ...props }, ref) => ( 16 | 26 | )) 27 | HoverCardContent.displayName = HoverCardPrimitive.Content.displayName 28 | 29 | export { HoverCard, HoverCardTrigger, HoverCardContent } 30 | -------------------------------------------------------------------------------- /frontend/components/ui/input-otp.tsx: -------------------------------------------------------------------------------- 1 | "use client" 2 | 3 | import * as React from "react" 4 | import { OTPInput, OTPInputContext } from "input-otp" 5 | import { Dot } from "lucide-react" 6 | 7 | import { cn } from "@/lib/utils" 8 | 9 | const InputOTP = React.forwardRef< 10 | React.ElementRef, 11 | React.ComponentPropsWithoutRef 12 | >(({ className, containerClassName, ...props }, ref) => ( 13 | 22 | )) 23 | InputOTP.displayName = "InputOTP" 24 | 25 | const InputOTPGroup = React.forwardRef< 26 | React.ElementRef<"div">, 27 | React.ComponentPropsWithoutRef<"div"> 28 | >(({ className, ...props }, ref) => ( 29 |
30 | )) 31 | InputOTPGroup.displayName = "InputOTPGroup" 32 | 33 | const InputOTPSlot = React.forwardRef< 34 | React.ElementRef<"div">, 35 | React.ComponentPropsWithoutRef<"div"> & { index: number } 36 | >(({ index, className, ...props }, ref) => { 37 | const inputOTPContext = React.useContext(OTPInputContext) 38 | const { char, hasFakeCaret, isActive } = inputOTPContext.slots[index] 39 | 40 | return ( 41 |
50 | {char} 51 | {hasFakeCaret && ( 52 |
53 |
54 |
55 | )} 56 |
57 | ) 58 | }) 59 | InputOTPSlot.displayName = "InputOTPSlot" 60 | 61 | const InputOTPSeparator = React.forwardRef< 62 | React.ElementRef<"div">, 63 | React.ComponentPropsWithoutRef<"div"> 64 | >(({ ...props }, ref) => ( 65 |
66 | 67 |
68 | )) 69 | InputOTPSeparator.displayName = "InputOTPSeparator" 70 | 71 | export { InputOTP, InputOTPGroup, InputOTPSlot, InputOTPSeparator } 72 | -------------------------------------------------------------------------------- /frontend/components/ui/input.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react" 2 | 3 | import { cn } from "@/lib/utils" 4 | 5 | const Input = React.forwardRef>( 6 | ({ className, type, ...props }, ref) => { 7 | return ( 8 | 17 | ) 18 | } 19 | ) 20 | Input.displayName = "Input" 21 | 22 | export { Input } 23 | -------------------------------------------------------------------------------- /frontend/components/ui/label.tsx: -------------------------------------------------------------------------------- 1 | "use client" 2 | 3 | import * as React from "react" 4 | import * as LabelPrimitive from "@radix-ui/react-label" 5 | import { cva, type VariantProps } from "class-variance-authority" 6 | 7 | import { cn } from "@/lib/utils" 8 | 9 | const labelVariants = cva( 10 | "text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70" 11 | ) 12 | 13 | const Label = React.forwardRef< 14 | React.ElementRef, 15 | React.ComponentPropsWithoutRef & 16 | VariantProps 17 | >(({ className, ...props }, ref) => ( 18 | 23 | )) 24 | Label.displayName = LabelPrimitive.Root.displayName 25 | 26 | export { Label } 27 | -------------------------------------------------------------------------------- /frontend/components/ui/popover.tsx: -------------------------------------------------------------------------------- 1 | "use client" 2 | 3 | import * as React from "react" 4 | import * as PopoverPrimitive from "@radix-ui/react-popover" 5 | 6 | import { cn } from "@/lib/utils" 7 | 8 | const Popover = PopoverPrimitive.Root 9 | 10 | const PopoverTrigger = PopoverPrimitive.Trigger 11 | 12 | const PopoverContent = React.forwardRef< 13 | React.ElementRef, 14 | React.ComponentPropsWithoutRef 15 | >(({ className, align = "center", sideOffset = 4, ...props }, ref) => ( 16 | 17 | 27 | 28 | )) 29 | PopoverContent.displayName = PopoverPrimitive.Content.displayName 30 | 31 | export { Popover, PopoverTrigger, PopoverContent } 32 | -------------------------------------------------------------------------------- /frontend/components/ui/progress.tsx: -------------------------------------------------------------------------------- 1 | "use client" 2 | 3 | import * as React from "react" 4 | import * as ProgressPrimitive from "@radix-ui/react-progress" 5 | 6 | import { cn } from "@/lib/utils" 7 | 8 | const Progress = React.forwardRef< 9 | React.ElementRef, 10 | React.ComponentPropsWithoutRef 11 | >(({ className, value, ...props }, ref) => ( 12 | 20 | 24 | 25 | )) 26 | Progress.displayName = ProgressPrimitive.Root.displayName 27 | 28 | export { Progress } 29 | -------------------------------------------------------------------------------- /frontend/components/ui/radio-group.tsx: -------------------------------------------------------------------------------- 1 | "use client" 2 | 3 | import * as React from "react" 4 | import * as RadioGroupPrimitive from "@radix-ui/react-radio-group" 5 | import { Circle } from "lucide-react" 6 | 7 | import { cn } from "@/lib/utils" 8 | 9 | const RadioGroup = React.forwardRef< 10 | React.ElementRef, 11 | React.ComponentPropsWithoutRef 12 | >(({ className, ...props }, ref) => { 13 | return ( 14 | 19 | ) 20 | }) 21 | RadioGroup.displayName = RadioGroupPrimitive.Root.displayName 22 | 23 | const RadioGroupItem = React.forwardRef< 24 | React.ElementRef, 25 | React.ComponentPropsWithoutRef 26 | >(({ className, ...props }, ref) => { 27 | return ( 28 | 36 | 37 | 38 | 39 | 40 | ) 41 | }) 42 | RadioGroupItem.displayName = RadioGroupPrimitive.Item.displayName 43 | 44 | export { RadioGroup, RadioGroupItem } 45 | -------------------------------------------------------------------------------- /frontend/components/ui/resizable.tsx: -------------------------------------------------------------------------------- 1 | "use client" 2 | 3 | import { GripVertical } from "lucide-react" 4 | import * as ResizablePrimitive from "react-resizable-panels" 5 | 6 | import { cn } from "@/lib/utils" 7 | 8 | const ResizablePanelGroup = ({ 9 | className, 10 | ...props 11 | }: React.ComponentProps) => ( 12 | 19 | ) 20 | 21 | const ResizablePanel = ResizablePrimitive.Panel 22 | 23 | const ResizableHandle = ({ 24 | withHandle, 25 | className, 26 | ...props 27 | }: React.ComponentProps & { 28 | withHandle?: boolean 29 | }) => ( 30 | div]:rotate-90", 33 | className 34 | )} 35 | {...props} 36 | > 37 | {withHandle && ( 38 |
39 | 40 |
41 | )} 42 |
43 | ) 44 | 45 | export { ResizablePanelGroup, ResizablePanel, ResizableHandle } 46 | -------------------------------------------------------------------------------- /frontend/components/ui/scroll-area.tsx: -------------------------------------------------------------------------------- 1 | "use client" 2 | 3 | import * as React from "react" 4 | import * as ScrollAreaPrimitive from "@radix-ui/react-scroll-area" 5 | 6 | import { cn } from "@/lib/utils" 7 | 8 | const ScrollArea = React.forwardRef< 9 | React.ElementRef, 10 | React.ComponentPropsWithoutRef 11 | >(({ className, children, ...props }, ref) => ( 12 | 17 | 18 | {children} 19 | 20 | 21 | 22 | 23 | )) 24 | ScrollArea.displayName = ScrollAreaPrimitive.Root.displayName 25 | 26 | const ScrollBar = React.forwardRef< 27 | React.ElementRef, 28 | React.ComponentPropsWithoutRef 29 | >(({ className, orientation = "vertical", ...props }, ref) => ( 30 | 43 | 44 | 45 | )) 46 | ScrollBar.displayName = ScrollAreaPrimitive.ScrollAreaScrollbar.displayName 47 | 48 | export { ScrollArea, ScrollBar } 49 | -------------------------------------------------------------------------------- /frontend/components/ui/separator.tsx: -------------------------------------------------------------------------------- 1 | "use client" 2 | 3 | import * as React from "react" 4 | import * as SeparatorPrimitive from "@radix-ui/react-separator" 5 | 6 | import { cn } from "@/lib/utils" 7 | 8 | const Separator = React.forwardRef< 9 | React.ElementRef, 10 | React.ComponentPropsWithoutRef 11 | >( 12 | ( 13 | { className, orientation = "horizontal", decorative = true, ...props }, 14 | ref 15 | ) => ( 16 | 27 | ) 28 | ) 29 | Separator.displayName = SeparatorPrimitive.Root.displayName 30 | 31 | export { Separator } 32 | -------------------------------------------------------------------------------- /frontend/components/ui/skeleton.tsx: -------------------------------------------------------------------------------- 1 | import { cn } from "@/lib/utils" 2 | 3 | function Skeleton({ 4 | className, 5 | ...props 6 | }: React.HTMLAttributes) { 7 | return ( 8 |
12 | ) 13 | } 14 | 15 | export { Skeleton } 16 | -------------------------------------------------------------------------------- /frontend/components/ui/slider.tsx: -------------------------------------------------------------------------------- 1 | "use client" 2 | 3 | import * as React from "react" 4 | import * as SliderPrimitive from "@radix-ui/react-slider" 5 | 6 | import { cn } from "@/lib/utils" 7 | 8 | const Slider = React.forwardRef< 9 | React.ElementRef, 10 | React.ComponentPropsWithoutRef 11 | >(({ className, ...props }, ref) => ( 12 | 20 | 21 | 22 | 23 | 24 | 25 | )) 26 | Slider.displayName = SliderPrimitive.Root.displayName 27 | 28 | export { Slider } 29 | -------------------------------------------------------------------------------- /frontend/components/ui/sonner.tsx: -------------------------------------------------------------------------------- 1 | "use client" 2 | 3 | import { useTheme } from "next-themes" 4 | import { Toaster as Sonner } from "sonner" 5 | 6 | type ToasterProps = React.ComponentProps 7 | 8 | const Toaster = ({ ...props }: ToasterProps) => { 9 | const { theme = "system" } = useTheme() 10 | 11 | return ( 12 | 28 | ) 29 | } 30 | 31 | export { Toaster } 32 | -------------------------------------------------------------------------------- /frontend/components/ui/switch.tsx: -------------------------------------------------------------------------------- 1 | "use client" 2 | 3 | import * as React from "react" 4 | import * as SwitchPrimitives from "@radix-ui/react-switch" 5 | 6 | import { cn } from "@/lib/utils" 7 | 8 | const Switch = React.forwardRef< 9 | React.ElementRef, 10 | React.ComponentPropsWithoutRef 11 | >(({ className, ...props }, ref) => ( 12 | 20 | 25 | 26 | )) 27 | Switch.displayName = SwitchPrimitives.Root.displayName 28 | 29 | export { Switch } 30 | -------------------------------------------------------------------------------- /frontend/components/ui/tabs.tsx: -------------------------------------------------------------------------------- 1 | "use client" 2 | 3 | import * as React from "react" 4 | import * as TabsPrimitive from "@radix-ui/react-tabs" 5 | 6 | import { cn } from "@/lib/utils" 7 | 8 | const Tabs = TabsPrimitive.Root 9 | 10 | const TabsList = React.forwardRef< 11 | React.ElementRef, 12 | React.ComponentPropsWithoutRef 13 | >(({ className, ...props }, ref) => ( 14 | 22 | )) 23 | TabsList.displayName = TabsPrimitive.List.displayName 24 | 25 | const TabsTrigger = React.forwardRef< 26 | React.ElementRef, 27 | React.ComponentPropsWithoutRef 28 | >(({ className, ...props }, ref) => ( 29 | 37 | )) 38 | TabsTrigger.displayName = TabsPrimitive.Trigger.displayName 39 | 40 | const TabsContent = React.forwardRef< 41 | React.ElementRef, 42 | React.ComponentPropsWithoutRef 43 | >(({ className, ...props }, ref) => ( 44 | 52 | )) 53 | TabsContent.displayName = TabsPrimitive.Content.displayName 54 | 55 | export { Tabs, TabsList, TabsTrigger, TabsContent } 56 | -------------------------------------------------------------------------------- /frontend/components/ui/textarea.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react" 2 | 3 | import { cn } from "@/lib/utils" 4 | 5 | const Textarea = React.forwardRef< 6 | HTMLTextAreaElement, 7 | React.ComponentProps<"textarea"> 8 | >(({ className, ...props }, ref) => { 9 | return ( 10 |