stunning screenshots in seconds https://moocup.jaydip.me
5
fork

Configure Feed

Select the types of activity you want to include in your feed.

Initial commit

Jaydip Sanghani 5efef71b

+12675
+24
.gitignore
··· 1 + # Logs 2 + logs 3 + *.log 4 + npm-debug.log* 5 + yarn-debug.log* 6 + yarn-error.log* 7 + pnpm-debug.log* 8 + lerna-debug.log* 9 + 10 + node_modules 11 + dist 12 + dist-ssr 13 + *.local 14 + 15 + # Editor directories and files 16 + .vscode/* 17 + !.vscode/extensions.json 18 + .idea 19 + .DS_Store 20 + *.suo 21 + *.ntvs* 22 + *.njsproj 23 + *.sln 24 + *.sw?
+41
README.md
··· 1 + [![Moocup banner](https://moocup.jaydip.me/og.webp)](https://moocup.jaydip.me/) 2 + 3 + # Moocup 4 + 5 + A visual editor to create stunning screenshots. 6 + 7 + drop your screenshot, we'll do the rest. 8 + 9 + 10 + ## Support 11 + 12 + moocup is completely free to use. If you wish to support moocup, 13 + you can do so at [Ko-fi](https://ko-fi.com/jaydipsanghani) 14 + 15 + Your support allows me to provide better features in moocup. if you'd like you can also help by contributing to moocup. 16 + 17 + [![ko-fi](https://ko-fi.com/img/githubbutton_sm.svg)](https://ko-fi.com/J3J41GWDRD) 18 + 19 + ## Motivation 20 + 21 + I needed good visuals for my portfolio. I am a big fan of simple & functional things, but all the available options, didn't felt quite right to me. 22 + 23 + so, i made one myself. 24 + 25 + ## Feature Request 26 + 27 + Raise a Issue with title "Feature Request : " and what you wish to see to be added in app. 28 + 29 + currently, I'm planning to only add features that gives more benefit to moocup as a tool. so, priority will change based on value. 30 + 31 + ## Contributing 32 + 33 + Contributions and Fixes are more than Welcome. 34 + Raise a PR with relevant details, and it'll be merged 35 + 36 + 37 + ## License 38 + 39 + GNU AGPLv3 40 + 41 +
+20
components.json
··· 1 + { 2 + "$schema": "https://ui.shadcn.com/schema.json", 3 + "style": "default", 4 + "rsc": false, 5 + "tsx": true, 6 + "tailwind": { 7 + "config": "tailwind.config.ts", 8 + "css": "src/index.css", 9 + "baseColor": "slate", 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 + }
+29
eslint.config.js
··· 1 + import js from "@eslint/js"; 2 + import globals from "globals"; 3 + import reactHooks from "eslint-plugin-react-hooks"; 4 + import reactRefresh from "eslint-plugin-react-refresh"; 5 + import tseslint from "typescript-eslint"; 6 + 7 + export default tseslint.config( 8 + { ignores: ["dist"] }, 9 + { 10 + extends: [js.configs.recommended, ...tseslint.configs.recommended], 11 + files: ["**/*.{ts,tsx}"], 12 + languageOptions: { 13 + ecmaVersion: 2020, 14 + globals: globals.browser, 15 + }, 16 + plugins: { 17 + "react-hooks": reactHooks, 18 + "react-refresh": reactRefresh, 19 + }, 20 + rules: { 21 + ...reactHooks.configs.recommended.rules, 22 + "react-refresh/only-export-components": [ 23 + "warn", 24 + { allowConstantExport: true }, 25 + ], 26 + "@typescript-eslint/no-unused-vars": "off", 27 + }, 28 + } 29 + );
+36
index.html
··· 1 + <!DOCTYPE html> 2 + <html lang="en"> 3 + 4 + <head> 5 + <meta charset="UTF-8" /> 6 + <meta name="viewport" content="width=device-width, initial-scale=1.0" /> 7 + <title>Moocup - Beautiful mockups</title> 8 + 9 + <link rel="icon" type="image/svg+xml" href="/favicon.svg"> 10 + 11 + <meta name="description" content="Create beautiful mockups in seconds" /> 12 + <meta name="author" content="Moocup Editor" /> 13 + 14 + <meta property="og:title" content="Moocup - Create beautiful mockups in seconds" /> 15 + <meta property="og:description" content="Create beautiful mockups in seconds" /> 16 + <meta property="og:type" content="website" /> 17 + <meta property="og:image" content="https://moocup.jaydip.me/og.webp" /> 18 + 19 + <meta name="twitter:card" content="summary_large_image" /> 20 + <meta name="twitter:site" content="@JellyDeck" /> 21 + <meta name="twitter:image" content="https://moocup.jaydip.me/og.webp" /> 22 + 23 + 24 + <script defer src="https://cloud.umami.is/script.js" data-website-id="af131436-f3fc-4026-b594-5a51360b164f"></script> 25 + 26 + <link rel="preconnect" href="https://fonts.googleapis.com"> 27 + <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> 28 + <link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap" rel="stylesheet"> 29 + </head> 30 + 31 + <body class="font-inter"> 32 + <div id="root"></div> 33 + <script type="module" src="/src/main.tsx"></script> 34 + </body> 35 + 36 + </html>
+86
package.json
··· 1 + { 2 + "name": "moocup", 3 + "private": true, 4 + "version": "0.0.0", 5 + "type": "module", 6 + "packageManager": "pnpm@10.13.0", 7 + "scripts": { 8 + "dev": "vite --host", 9 + "build": "vite build", 10 + "build:dev": "vite build --mode development", 11 + "lint": "eslint .", 12 + "preview": "vite preview" 13 + }, 14 + "dependencies": { 15 + "@hookform/resolvers": "^3.9.0", 16 + "@radix-ui/react-accordion": "^1.2.0", 17 + "@radix-ui/react-alert-dialog": "^1.1.1", 18 + "@radix-ui/react-aspect-ratio": "^1.1.0", 19 + "@radix-ui/react-avatar": "^1.1.0", 20 + "@radix-ui/react-checkbox": "^1.1.1", 21 + "@radix-ui/react-collapsible": "^1.1.0", 22 + "@radix-ui/react-context-menu": "^2.2.1", 23 + "@radix-ui/react-dialog": "^1.1.2", 24 + "@radix-ui/react-dropdown-menu": "^2.1.1", 25 + "@radix-ui/react-hover-card": "^1.1.1", 26 + "@radix-ui/react-label": "^2.1.0", 27 + "@radix-ui/react-menubar": "^1.1.1", 28 + "@radix-ui/react-navigation-menu": "^1.2.0", 29 + "@radix-ui/react-popover": "^1.1.1", 30 + "@radix-ui/react-progress": "^1.1.0", 31 + "@radix-ui/react-radio-group": "^1.2.0", 32 + "@radix-ui/react-scroll-area": "^1.1.0", 33 + "@radix-ui/react-select": "^2.1.1", 34 + "@radix-ui/react-separator": "^1.1.0", 35 + "@radix-ui/react-slider": "^1.2.0", 36 + "@radix-ui/react-slot": "^1.1.0", 37 + "@radix-ui/react-switch": "^1.1.0", 38 + "@radix-ui/react-tabs": "^1.1.0", 39 + "@radix-ui/react-toast": "^1.2.1", 40 + "@radix-ui/react-toggle": "^1.1.0", 41 + "@radix-ui/react-toggle-group": "^1.1.0", 42 + "@radix-ui/react-tooltip": "^1.1.4", 43 + "@tailwindcss/vite": "^4.1.10", 44 + "class-variance-authority": "^0.7.1", 45 + "clsx": "^2.1.1", 46 + "cmdk": "^1.0.0", 47 + "date-fns": "^3.6.0", 48 + "embla-carousel-react": "^8.3.0", 49 + "html2canvas": "^1.4.1", 50 + "idb-keyval": "^6.2.2", 51 + "input-otp": "^1.2.4", 52 + "lucide-react": "^0.462.0", 53 + "next-themes": "^0.3.0", 54 + "react": "^18.3.1", 55 + "react-day-picker": "^8.10.1", 56 + "react-dom": "^18.3.1", 57 + "react-hook-form": "^7.53.0", 58 + "react-resizable-panels": "^2.1.3", 59 + "react-router-dom": "^6.26.2", 60 + "recharts": "^2.12.7", 61 + "sonner": "^1.5.0", 62 + "tailwind-merge": "^2.5.2", 63 + "tailwindcss-animate": "^1.0.7", 64 + "vaul": "^0.9.3", 65 + "zod": "^3.23.8", 66 + "zustand": "^5.0.5" 67 + }, 68 + "devDependencies": { 69 + "@eslint/js": "^9.9.0", 70 + "@tailwindcss/postcss": "^4.1.10", 71 + "@tailwindcss/typography": "^0.5.15", 72 + "@types/node": "^22.5.5", 73 + "@types/react": "^18.3.3", 74 + "@types/react-dom": "^18.3.0", 75 + "@vitejs/plugin-react-swc": "^3.5.0", 76 + "eslint": "^9.9.0", 77 + "eslint-plugin-react-hooks": "^5.1.0-rc.0", 78 + "eslint-plugin-react-refresh": "^0.4.9", 79 + "globals": "^15.9.0", 80 + "postcss": "^8.4.47", 81 + "tailwindcss": "^4.1.10", 82 + "typescript": "^5.5.3", 83 + "typescript-eslint": "^8.0.1", 84 + "vite": "^5.4.1" 85 + } 86 + }
+4462
pnpm-lock.yaml
··· 1 + lockfileVersion: '9.0' 2 + 3 + settings: 4 + autoInstallPeers: true 5 + excludeLinksFromLockfile: false 6 + 7 + importers: 8 + 9 + .: 10 + dependencies: 11 + '@hookform/resolvers': 12 + specifier: ^3.9.0 13 + version: 3.10.0(react-hook-form@7.58.1(react@18.3.1)) 14 + '@radix-ui/react-accordion': 15 + specifier: ^1.2.0 16 + version: 1.2.11(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) 17 + '@radix-ui/react-alert-dialog': 18 + specifier: ^1.1.1 19 + version: 1.1.14(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) 20 + '@radix-ui/react-aspect-ratio': 21 + specifier: ^1.1.0 22 + version: 1.1.7(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) 23 + '@radix-ui/react-avatar': 24 + specifier: ^1.1.0 25 + version: 1.1.10(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) 26 + '@radix-ui/react-checkbox': 27 + specifier: ^1.1.1 28 + version: 1.3.2(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) 29 + '@radix-ui/react-collapsible': 30 + specifier: ^1.1.0 31 + version: 1.1.11(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) 32 + '@radix-ui/react-context-menu': 33 + specifier: ^2.2.1 34 + version: 2.2.15(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) 35 + '@radix-ui/react-dialog': 36 + specifier: ^1.1.2 37 + version: 1.1.14(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) 38 + '@radix-ui/react-dropdown-menu': 39 + specifier: ^2.1.1 40 + version: 2.1.15(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) 41 + '@radix-ui/react-hover-card': 42 + specifier: ^1.1.1 43 + version: 1.1.14(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) 44 + '@radix-ui/react-label': 45 + specifier: ^2.1.0 46 + version: 2.1.7(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) 47 + '@radix-ui/react-menubar': 48 + specifier: ^1.1.1 49 + version: 1.1.15(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) 50 + '@radix-ui/react-navigation-menu': 51 + specifier: ^1.2.0 52 + version: 1.2.13(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) 53 + '@radix-ui/react-popover': 54 + specifier: ^1.1.1 55 + version: 1.1.14(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) 56 + '@radix-ui/react-progress': 57 + specifier: ^1.1.0 58 + version: 1.1.7(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) 59 + '@radix-ui/react-radio-group': 60 + specifier: ^1.2.0 61 + version: 1.3.7(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) 62 + '@radix-ui/react-scroll-area': 63 + specifier: ^1.1.0 64 + version: 1.2.9(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) 65 + '@radix-ui/react-select': 66 + specifier: ^2.1.1 67 + version: 2.2.5(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) 68 + '@radix-ui/react-separator': 69 + specifier: ^1.1.0 70 + version: 1.1.7(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) 71 + '@radix-ui/react-slider': 72 + specifier: ^1.2.0 73 + version: 1.3.5(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) 74 + '@radix-ui/react-slot': 75 + specifier: ^1.1.0 76 + version: 1.2.3(@types/react@18.3.23)(react@18.3.1) 77 + '@radix-ui/react-switch': 78 + specifier: ^1.1.0 79 + version: 1.2.5(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) 80 + '@radix-ui/react-tabs': 81 + specifier: ^1.1.0 82 + version: 1.1.12(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) 83 + '@radix-ui/react-toast': 84 + specifier: ^1.2.1 85 + version: 1.2.14(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) 86 + '@radix-ui/react-toggle': 87 + specifier: ^1.1.0 88 + version: 1.1.9(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) 89 + '@radix-ui/react-toggle-group': 90 + specifier: ^1.1.0 91 + version: 1.1.10(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) 92 + '@radix-ui/react-tooltip': 93 + specifier: ^1.1.4 94 + version: 1.2.7(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) 95 + '@tailwindcss/vite': 96 + specifier: ^4.1.10 97 + version: 4.1.10(vite@5.4.19(@types/node@22.15.32)(lightningcss@1.30.1)) 98 + class-variance-authority: 99 + specifier: ^0.7.1 100 + version: 0.7.1 101 + clsx: 102 + specifier: ^2.1.1 103 + version: 2.1.1 104 + cmdk: 105 + specifier: ^1.0.0 106 + version: 1.1.1(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) 107 + date-fns: 108 + specifier: ^3.6.0 109 + version: 3.6.0 110 + embla-carousel-react: 111 + specifier: ^8.3.0 112 + version: 8.6.0(react@18.3.1) 113 + html2canvas: 114 + specifier: ^1.4.1 115 + version: 1.4.1 116 + idb-keyval: 117 + specifier: ^6.2.2 118 + version: 6.2.2 119 + input-otp: 120 + specifier: ^1.2.4 121 + version: 1.4.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1) 122 + lucide-react: 123 + specifier: ^0.462.0 124 + version: 0.462.0(react@18.3.1) 125 + next-themes: 126 + specifier: ^0.3.0 127 + version: 0.3.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) 128 + react: 129 + specifier: ^18.3.1 130 + version: 18.3.1 131 + react-day-picker: 132 + specifier: ^8.10.1 133 + version: 8.10.1(date-fns@3.6.0)(react@18.3.1) 134 + react-dom: 135 + specifier: ^18.3.1 136 + version: 18.3.1(react@18.3.1) 137 + react-hook-form: 138 + specifier: ^7.53.0 139 + version: 7.58.1(react@18.3.1) 140 + react-resizable-panels: 141 + specifier: ^2.1.3 142 + version: 2.1.9(react-dom@18.3.1(react@18.3.1))(react@18.3.1) 143 + react-router-dom: 144 + specifier: ^6.26.2 145 + version: 6.30.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) 146 + recharts: 147 + specifier: ^2.12.7 148 + version: 2.15.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1) 149 + sonner: 150 + specifier: ^1.5.0 151 + version: 1.7.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1) 152 + tailwind-merge: 153 + specifier: ^2.5.2 154 + version: 2.6.0 155 + tailwindcss-animate: 156 + specifier: ^1.0.7 157 + version: 1.0.7(tailwindcss@4.1.10) 158 + vaul: 159 + specifier: ^0.9.3 160 + version: 0.9.9(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) 161 + zod: 162 + specifier: ^3.23.8 163 + version: 3.25.67 164 + zustand: 165 + specifier: ^5.0.5 166 + version: 5.0.5(@types/react@18.3.23)(react@18.3.1)(use-sync-external-store@1.5.0(react@18.3.1)) 167 + devDependencies: 168 + '@eslint/js': 169 + specifier: ^9.9.0 170 + version: 9.29.0 171 + '@tailwindcss/postcss': 172 + specifier: ^4.1.10 173 + version: 4.1.10 174 + '@tailwindcss/typography': 175 + specifier: ^0.5.15 176 + version: 0.5.16(tailwindcss@4.1.10) 177 + '@types/node': 178 + specifier: ^22.5.5 179 + version: 22.15.32 180 + '@types/react': 181 + specifier: ^18.3.3 182 + version: 18.3.23 183 + '@types/react-dom': 184 + specifier: ^18.3.0 185 + version: 18.3.7(@types/react@18.3.23) 186 + '@vitejs/plugin-react-swc': 187 + specifier: ^3.5.0 188 + version: 3.10.2(vite@5.4.19(@types/node@22.15.32)(lightningcss@1.30.1)) 189 + eslint: 190 + specifier: ^9.9.0 191 + version: 9.29.0(jiti@2.4.2) 192 + eslint-plugin-react-hooks: 193 + specifier: ^5.1.0-rc.0 194 + version: 5.2.0(eslint@9.29.0(jiti@2.4.2)) 195 + eslint-plugin-react-refresh: 196 + specifier: ^0.4.9 197 + version: 0.4.20(eslint@9.29.0(jiti@2.4.2)) 198 + globals: 199 + specifier: ^15.9.0 200 + version: 15.15.0 201 + postcss: 202 + specifier: ^8.4.47 203 + version: 8.5.6 204 + tailwindcss: 205 + specifier: ^4.1.10 206 + version: 4.1.10 207 + typescript: 208 + specifier: ^5.5.3 209 + version: 5.8.3 210 + typescript-eslint: 211 + specifier: ^8.0.1 212 + version: 8.34.1(eslint@9.29.0(jiti@2.4.2))(typescript@5.8.3) 213 + vite: 214 + specifier: ^5.4.1 215 + version: 5.4.19(@types/node@22.15.32)(lightningcss@1.30.1) 216 + 217 + packages: 218 + 219 + '@alloc/quick-lru@5.2.0': 220 + resolution: {integrity: sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==} 221 + engines: {node: '>=10'} 222 + 223 + '@ampproject/remapping@2.3.0': 224 + resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==} 225 + engines: {node: '>=6.0.0'} 226 + 227 + '@babel/runtime@7.27.6': 228 + resolution: {integrity: sha512-vbavdySgbTTrmFE+EsiqUTzlOr5bzlnJtUv9PynGCAKvfQqjIXbvFdumPM/GxMDfyuGMJaJAU6TO4zc1Jf1i8Q==} 229 + engines: {node: '>=6.9.0'} 230 + 231 + '@esbuild/aix-ppc64@0.21.5': 232 + resolution: {integrity: sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==} 233 + engines: {node: '>=12'} 234 + cpu: [ppc64] 235 + os: [aix] 236 + 237 + '@esbuild/android-arm64@0.21.5': 238 + resolution: {integrity: sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==} 239 + engines: {node: '>=12'} 240 + cpu: [arm64] 241 + os: [android] 242 + 243 + '@esbuild/android-arm@0.21.5': 244 + resolution: {integrity: sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==} 245 + engines: {node: '>=12'} 246 + cpu: [arm] 247 + os: [android] 248 + 249 + '@esbuild/android-x64@0.21.5': 250 + resolution: {integrity: sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==} 251 + engines: {node: '>=12'} 252 + cpu: [x64] 253 + os: [android] 254 + 255 + '@esbuild/darwin-arm64@0.21.5': 256 + resolution: {integrity: sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==} 257 + engines: {node: '>=12'} 258 + cpu: [arm64] 259 + os: [darwin] 260 + 261 + '@esbuild/darwin-x64@0.21.5': 262 + resolution: {integrity: sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==} 263 + engines: {node: '>=12'} 264 + cpu: [x64] 265 + os: [darwin] 266 + 267 + '@esbuild/freebsd-arm64@0.21.5': 268 + resolution: {integrity: sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==} 269 + engines: {node: '>=12'} 270 + cpu: [arm64] 271 + os: [freebsd] 272 + 273 + '@esbuild/freebsd-x64@0.21.5': 274 + resolution: {integrity: sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==} 275 + engines: {node: '>=12'} 276 + cpu: [x64] 277 + os: [freebsd] 278 + 279 + '@esbuild/linux-arm64@0.21.5': 280 + resolution: {integrity: sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==} 281 + engines: {node: '>=12'} 282 + cpu: [arm64] 283 + os: [linux] 284 + 285 + '@esbuild/linux-arm@0.21.5': 286 + resolution: {integrity: sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==} 287 + engines: {node: '>=12'} 288 + cpu: [arm] 289 + os: [linux] 290 + 291 + '@esbuild/linux-ia32@0.21.5': 292 + resolution: {integrity: sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==} 293 + engines: {node: '>=12'} 294 + cpu: [ia32] 295 + os: [linux] 296 + 297 + '@esbuild/linux-loong64@0.21.5': 298 + resolution: {integrity: sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==} 299 + engines: {node: '>=12'} 300 + cpu: [loong64] 301 + os: [linux] 302 + 303 + '@esbuild/linux-mips64el@0.21.5': 304 + resolution: {integrity: sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==} 305 + engines: {node: '>=12'} 306 + cpu: [mips64el] 307 + os: [linux] 308 + 309 + '@esbuild/linux-ppc64@0.21.5': 310 + resolution: {integrity: sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==} 311 + engines: {node: '>=12'} 312 + cpu: [ppc64] 313 + os: [linux] 314 + 315 + '@esbuild/linux-riscv64@0.21.5': 316 + resolution: {integrity: sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==} 317 + engines: {node: '>=12'} 318 + cpu: [riscv64] 319 + os: [linux] 320 + 321 + '@esbuild/linux-s390x@0.21.5': 322 + resolution: {integrity: sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==} 323 + engines: {node: '>=12'} 324 + cpu: [s390x] 325 + os: [linux] 326 + 327 + '@esbuild/linux-x64@0.21.5': 328 + resolution: {integrity: sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==} 329 + engines: {node: '>=12'} 330 + cpu: [x64] 331 + os: [linux] 332 + 333 + '@esbuild/netbsd-x64@0.21.5': 334 + resolution: {integrity: sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==} 335 + engines: {node: '>=12'} 336 + cpu: [x64] 337 + os: [netbsd] 338 + 339 + '@esbuild/openbsd-x64@0.21.5': 340 + resolution: {integrity: sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==} 341 + engines: {node: '>=12'} 342 + cpu: [x64] 343 + os: [openbsd] 344 + 345 + '@esbuild/sunos-x64@0.21.5': 346 + resolution: {integrity: sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==} 347 + engines: {node: '>=12'} 348 + cpu: [x64] 349 + os: [sunos] 350 + 351 + '@esbuild/win32-arm64@0.21.5': 352 + resolution: {integrity: sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==} 353 + engines: {node: '>=12'} 354 + cpu: [arm64] 355 + os: [win32] 356 + 357 + '@esbuild/win32-ia32@0.21.5': 358 + resolution: {integrity: sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==} 359 + engines: {node: '>=12'} 360 + cpu: [ia32] 361 + os: [win32] 362 + 363 + '@esbuild/win32-x64@0.21.5': 364 + resolution: {integrity: sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==} 365 + engines: {node: '>=12'} 366 + cpu: [x64] 367 + os: [win32] 368 + 369 + '@eslint-community/eslint-utils@4.7.0': 370 + resolution: {integrity: sha512-dyybb3AcajC7uha6CvhdVRJqaKyn7w2YKqKyAN37NKYgZT36w+iRb0Dymmc5qEJ549c/S31cMMSFd75bteCpCw==} 371 + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} 372 + peerDependencies: 373 + eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 374 + 375 + '@eslint-community/regexpp@4.12.1': 376 + resolution: {integrity: sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==} 377 + engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} 378 + 379 + '@eslint/config-array@0.20.1': 380 + resolution: {integrity: sha512-OL0RJzC/CBzli0DrrR31qzj6d6i6Mm3HByuhflhl4LOBiWxN+3i6/t/ZQQNii4tjksXi8r2CRW1wMpWA2ULUEw==} 381 + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} 382 + 383 + '@eslint/config-helpers@0.2.3': 384 + resolution: {integrity: sha512-u180qk2Um1le4yf0ruXH3PYFeEZeYC3p/4wCTKrr2U1CmGdzGi3KtY0nuPDH48UJxlKCC5RDzbcbh4X0XlqgHg==} 385 + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} 386 + 387 + '@eslint/core@0.14.0': 388 + resolution: {integrity: sha512-qIbV0/JZr7iSDjqAc60IqbLdsj9GDt16xQtWD+B78d/HAlvysGdZZ6rpJHGAc2T0FQx1X6thsSPdnoiGKdNtdg==} 389 + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} 390 + 391 + '@eslint/core@0.15.0': 392 + resolution: {integrity: sha512-b7ePw78tEWWkpgZCDYkbqDOP8dmM6qe+AOC6iuJqlq1R/0ahMAeH3qynpnqKFGkMltrp44ohV4ubGyvLX28tzw==} 393 + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} 394 + 395 + '@eslint/eslintrc@3.3.1': 396 + resolution: {integrity: sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==} 397 + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} 398 + 399 + '@eslint/js@9.29.0': 400 + resolution: {integrity: sha512-3PIF4cBw/y+1u2EazflInpV+lYsSG0aByVIQzAgb1m1MhHFSbqTyNqtBKHgWf/9Ykud+DhILS9EGkmekVhbKoQ==} 401 + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} 402 + 403 + '@eslint/object-schema@2.1.6': 404 + resolution: {integrity: sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==} 405 + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} 406 + 407 + '@eslint/plugin-kit@0.3.2': 408 + resolution: {integrity: sha512-4SaFZCNfJqvk/kenHpI8xvN42DMaoycy4PzKc5otHxRswww1kAt82OlBuwRVLofCACCTZEcla2Ydxv8scMXaTg==} 409 + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} 410 + 411 + '@floating-ui/core@1.7.1': 412 + resolution: {integrity: sha512-azI0DrjMMfIug/ExbBaeDVJXcY0a7EPvPjb2xAJPa4HeimBX+Z18HK8QQR3jb6356SnDDdxx+hinMLcJEDdOjw==} 413 + 414 + '@floating-ui/dom@1.7.1': 415 + resolution: {integrity: sha512-cwsmW/zyw5ltYTUeeYJ60CnQuPqmGwuGVhG9w0PRaRKkAyi38BT5CKrpIbb+jtahSwUl04cWzSx9ZOIxeS6RsQ==} 416 + 417 + '@floating-ui/react-dom@2.1.3': 418 + resolution: {integrity: sha512-huMBfiU9UnQ2oBwIhgzyIiSpVgvlDstU8CX0AF+wS+KzmYMs0J2a3GwuFHV1Lz+jlrQGeC1fF+Nv0QoumyV0bA==} 419 + peerDependencies: 420 + react: '>=16.8.0' 421 + react-dom: '>=16.8.0' 422 + 423 + '@floating-ui/utils@0.2.9': 424 + resolution: {integrity: sha512-MDWhGtE+eHw5JW7lq4qhc5yRLS11ERl1c7Z6Xd0a58DozHES6EnNNwUWbMiG4J9Cgj053Bhk8zvlhFYKVhULwg==} 425 + 426 + '@hookform/resolvers@3.10.0': 427 + resolution: {integrity: sha512-79Dv+3mDF7i+2ajj7SkypSKHhl1cbln1OGavqrsF7p6mbUv11xpqpacPsGDCTRvCSjEEIez2ef1NveSVL3b0Ag==} 428 + peerDependencies: 429 + react-hook-form: ^7.0.0 430 + 431 + '@humanfs/core@0.19.1': 432 + resolution: {integrity: sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==} 433 + engines: {node: '>=18.18.0'} 434 + 435 + '@humanfs/node@0.16.6': 436 + resolution: {integrity: sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw==} 437 + engines: {node: '>=18.18.0'} 438 + 439 + '@humanwhocodes/module-importer@1.0.1': 440 + resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==} 441 + engines: {node: '>=12.22'} 442 + 443 + '@humanwhocodes/retry@0.3.1': 444 + resolution: {integrity: sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==} 445 + engines: {node: '>=18.18'} 446 + 447 + '@humanwhocodes/retry@0.4.3': 448 + resolution: {integrity: sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==} 449 + engines: {node: '>=18.18'} 450 + 451 + '@isaacs/fs-minipass@4.0.1': 452 + resolution: {integrity: sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==} 453 + engines: {node: '>=18.0.0'} 454 + 455 + '@jridgewell/gen-mapping@0.3.8': 456 + resolution: {integrity: sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==} 457 + engines: {node: '>=6.0.0'} 458 + 459 + '@jridgewell/resolve-uri@3.1.2': 460 + resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} 461 + engines: {node: '>=6.0.0'} 462 + 463 + '@jridgewell/set-array@1.2.1': 464 + resolution: {integrity: sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==} 465 + engines: {node: '>=6.0.0'} 466 + 467 + '@jridgewell/sourcemap-codec@1.5.0': 468 + resolution: {integrity: sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==} 469 + 470 + '@jridgewell/trace-mapping@0.3.25': 471 + resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==} 472 + 473 + '@nodelib/fs.scandir@2.1.5': 474 + resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} 475 + engines: {node: '>= 8'} 476 + 477 + '@nodelib/fs.stat@2.0.5': 478 + resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} 479 + engines: {node: '>= 8'} 480 + 481 + '@nodelib/fs.walk@1.2.8': 482 + resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} 483 + engines: {node: '>= 8'} 484 + 485 + '@radix-ui/number@1.1.1': 486 + resolution: {integrity: sha512-MkKCwxlXTgz6CFoJx3pCwn07GKp36+aZyu/u2Ln2VrA5DcdyCZkASEDBTd8x5whTQQL5CiYf4prXKLcgQdv29g==} 487 + 488 + '@radix-ui/primitive@1.1.2': 489 + resolution: {integrity: sha512-XnbHrrprsNqZKQhStrSwgRUQzoCI1glLzdw79xiZPoofhGICeZRSQ3dIxAKH1gb3OHfNf4d6f+vAv3kil2eggA==} 490 + 491 + '@radix-ui/react-accordion@1.2.11': 492 + resolution: {integrity: sha512-l3W5D54emV2ues7jjeG1xcyN7S3jnK3zE2zHqgn0CmMsy9lNJwmgcrmaxS+7ipw15FAivzKNzH3d5EcGoFKw0A==} 493 + peerDependencies: 494 + '@types/react': '*' 495 + '@types/react-dom': '*' 496 + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc 497 + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc 498 + peerDependenciesMeta: 499 + '@types/react': 500 + optional: true 501 + '@types/react-dom': 502 + optional: true 503 + 504 + '@radix-ui/react-alert-dialog@1.1.14': 505 + resolution: {integrity: sha512-IOZfZ3nPvN6lXpJTBCunFQPRSvK8MDgSc1FB85xnIpUKOw9en0dJj8JmCAxV7BiZdtYlUpmrQjoTFkVYtdoWzQ==} 506 + peerDependencies: 507 + '@types/react': '*' 508 + '@types/react-dom': '*' 509 + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc 510 + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc 511 + peerDependenciesMeta: 512 + '@types/react': 513 + optional: true 514 + '@types/react-dom': 515 + optional: true 516 + 517 + '@radix-ui/react-arrow@1.1.7': 518 + resolution: {integrity: sha512-F+M1tLhO+mlQaOWspE8Wstg+z6PwxwRd8oQ8IXceWz92kfAmalTRf0EjrouQeo7QssEPfCn05B4Ihs1K9WQ/7w==} 519 + peerDependencies: 520 + '@types/react': '*' 521 + '@types/react-dom': '*' 522 + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc 523 + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc 524 + peerDependenciesMeta: 525 + '@types/react': 526 + optional: true 527 + '@types/react-dom': 528 + optional: true 529 + 530 + '@radix-ui/react-aspect-ratio@1.1.7': 531 + resolution: {integrity: sha512-Yq6lvO9HQyPwev1onK1daHCHqXVLzPhSVjmsNjCa2Zcxy2f7uJD2itDtxknv6FzAKCwD1qQkeVDmX/cev13n/g==} 532 + peerDependencies: 533 + '@types/react': '*' 534 + '@types/react-dom': '*' 535 + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc 536 + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc 537 + peerDependenciesMeta: 538 + '@types/react': 539 + optional: true 540 + '@types/react-dom': 541 + optional: true 542 + 543 + '@radix-ui/react-avatar@1.1.10': 544 + resolution: {integrity: sha512-V8piFfWapM5OmNCXTzVQY+E1rDa53zY+MQ4Y7356v4fFz6vqCyUtIz2rUD44ZEdwg78/jKmMJHj07+C/Z/rcog==} 545 + peerDependencies: 546 + '@types/react': '*' 547 + '@types/react-dom': '*' 548 + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc 549 + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc 550 + peerDependenciesMeta: 551 + '@types/react': 552 + optional: true 553 + '@types/react-dom': 554 + optional: true 555 + 556 + '@radix-ui/react-checkbox@1.3.2': 557 + resolution: {integrity: sha512-yd+dI56KZqawxKZrJ31eENUwqc1QSqg4OZ15rybGjF2ZNwMO+wCyHzAVLRp9qoYJf7kYy0YpZ2b0JCzJ42HZpA==} 558 + peerDependencies: 559 + '@types/react': '*' 560 + '@types/react-dom': '*' 561 + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc 562 + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc 563 + peerDependenciesMeta: 564 + '@types/react': 565 + optional: true 566 + '@types/react-dom': 567 + optional: true 568 + 569 + '@radix-ui/react-collapsible@1.1.11': 570 + resolution: {integrity: sha512-2qrRsVGSCYasSz1RFOorXwl0H7g7J1frQtgpQgYrt+MOidtPAINHn9CPovQXb83r8ahapdx3Tu0fa/pdFFSdPg==} 571 + peerDependencies: 572 + '@types/react': '*' 573 + '@types/react-dom': '*' 574 + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc 575 + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc 576 + peerDependenciesMeta: 577 + '@types/react': 578 + optional: true 579 + '@types/react-dom': 580 + optional: true 581 + 582 + '@radix-ui/react-collection@1.1.7': 583 + resolution: {integrity: sha512-Fh9rGN0MoI4ZFUNyfFVNU4y9LUz93u9/0K+yLgA2bwRojxM8JU1DyvvMBabnZPBgMWREAJvU2jjVzq+LrFUglw==} 584 + peerDependencies: 585 + '@types/react': '*' 586 + '@types/react-dom': '*' 587 + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc 588 + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc 589 + peerDependenciesMeta: 590 + '@types/react': 591 + optional: true 592 + '@types/react-dom': 593 + optional: true 594 + 595 + '@radix-ui/react-compose-refs@1.1.2': 596 + resolution: {integrity: sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg==} 597 + peerDependencies: 598 + '@types/react': '*' 599 + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc 600 + peerDependenciesMeta: 601 + '@types/react': 602 + optional: true 603 + 604 + '@radix-ui/react-context-menu@2.2.15': 605 + resolution: {integrity: sha512-UsQUMjcYTsBjTSXw0P3GO0werEQvUY2plgRQuKoCTtkNr45q1DiL51j4m7gxhABzZ0BadoXNsIbg7F3KwiUBbw==} 606 + peerDependencies: 607 + '@types/react': '*' 608 + '@types/react-dom': '*' 609 + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc 610 + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc 611 + peerDependenciesMeta: 612 + '@types/react': 613 + optional: true 614 + '@types/react-dom': 615 + optional: true 616 + 617 + '@radix-ui/react-context@1.1.2': 618 + resolution: {integrity: sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA==} 619 + peerDependencies: 620 + '@types/react': '*' 621 + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc 622 + peerDependenciesMeta: 623 + '@types/react': 624 + optional: true 625 + 626 + '@radix-ui/react-dialog@1.1.14': 627 + resolution: {integrity: sha512-+CpweKjqpzTmwRwcYECQcNYbI8V9VSQt0SNFKeEBLgfucbsLssU6Ppq7wUdNXEGb573bMjFhVjKVll8rmV6zMw==} 628 + peerDependencies: 629 + '@types/react': '*' 630 + '@types/react-dom': '*' 631 + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc 632 + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc 633 + peerDependenciesMeta: 634 + '@types/react': 635 + optional: true 636 + '@types/react-dom': 637 + optional: true 638 + 639 + '@radix-ui/react-direction@1.1.1': 640 + resolution: {integrity: sha512-1UEWRX6jnOA2y4H5WczZ44gOOjTEmlqv1uNW4GAJEO5+bauCBhv8snY65Iw5/VOS/ghKN9gr2KjnLKxrsvoMVw==} 641 + peerDependencies: 642 + '@types/react': '*' 643 + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc 644 + peerDependenciesMeta: 645 + '@types/react': 646 + optional: true 647 + 648 + '@radix-ui/react-dismissable-layer@1.1.10': 649 + resolution: {integrity: sha512-IM1zzRV4W3HtVgftdQiiOmA0AdJlCtMLe00FXaHwgt3rAnNsIyDqshvkIW3hj/iu5hu8ERP7KIYki6NkqDxAwQ==} 650 + peerDependencies: 651 + '@types/react': '*' 652 + '@types/react-dom': '*' 653 + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc 654 + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc 655 + peerDependenciesMeta: 656 + '@types/react': 657 + optional: true 658 + '@types/react-dom': 659 + optional: true 660 + 661 + '@radix-ui/react-dropdown-menu@2.1.15': 662 + resolution: {integrity: sha512-mIBnOjgwo9AH3FyKaSWoSu/dYj6VdhJ7frEPiGTeXCdUFHjl9h3mFh2wwhEtINOmYXWhdpf1rY2minFsmaNgVQ==} 663 + peerDependencies: 664 + '@types/react': '*' 665 + '@types/react-dom': '*' 666 + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc 667 + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc 668 + peerDependenciesMeta: 669 + '@types/react': 670 + optional: true 671 + '@types/react-dom': 672 + optional: true 673 + 674 + '@radix-ui/react-focus-guards@1.1.2': 675 + resolution: {integrity: sha512-fyjAACV62oPV925xFCrH8DR5xWhg9KYtJT4s3u54jxp+L/hbpTY2kIeEFFbFe+a/HCE94zGQMZLIpVTPVZDhaA==} 676 + peerDependencies: 677 + '@types/react': '*' 678 + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc 679 + peerDependenciesMeta: 680 + '@types/react': 681 + optional: true 682 + 683 + '@radix-ui/react-focus-scope@1.1.7': 684 + resolution: {integrity: sha512-t2ODlkXBQyn7jkl6TNaw/MtVEVvIGelJDCG41Okq/KwUsJBwQ4XVZsHAVUkK4mBv3ewiAS3PGuUWuY2BoK4ZUw==} 685 + peerDependencies: 686 + '@types/react': '*' 687 + '@types/react-dom': '*' 688 + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc 689 + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc 690 + peerDependenciesMeta: 691 + '@types/react': 692 + optional: true 693 + '@types/react-dom': 694 + optional: true 695 + 696 + '@radix-ui/react-hover-card@1.1.14': 697 + resolution: {integrity: sha512-CPYZ24Mhirm+g6D8jArmLzjYu4Eyg3TTUHswR26QgzXBHBe64BO/RHOJKzmF/Dxb4y4f9PKyJdwm/O/AhNkb+Q==} 698 + peerDependencies: 699 + '@types/react': '*' 700 + '@types/react-dom': '*' 701 + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc 702 + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc 703 + peerDependenciesMeta: 704 + '@types/react': 705 + optional: true 706 + '@types/react-dom': 707 + optional: true 708 + 709 + '@radix-ui/react-id@1.1.1': 710 + resolution: {integrity: sha512-kGkGegYIdQsOb4XjsfM97rXsiHaBwco+hFI66oO4s9LU+PLAC5oJ7khdOVFxkhsmlbpUqDAvXw11CluXP+jkHg==} 711 + peerDependencies: 712 + '@types/react': '*' 713 + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc 714 + peerDependenciesMeta: 715 + '@types/react': 716 + optional: true 717 + 718 + '@radix-ui/react-label@2.1.7': 719 + resolution: {integrity: sha512-YT1GqPSL8kJn20djelMX7/cTRp/Y9w5IZHvfxQTVHrOqa2yMl7i/UfMqKRU5V7mEyKTrUVgJXhNQPVCG8PBLoQ==} 720 + peerDependencies: 721 + '@types/react': '*' 722 + '@types/react-dom': '*' 723 + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc 724 + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc 725 + peerDependenciesMeta: 726 + '@types/react': 727 + optional: true 728 + '@types/react-dom': 729 + optional: true 730 + 731 + '@radix-ui/react-menu@2.1.15': 732 + resolution: {integrity: sha512-tVlmA3Vb9n8SZSd+YSbuFR66l87Wiy4du+YE+0hzKQEANA+7cWKH1WgqcEX4pXqxUFQKrWQGHdvEfw00TjFiew==} 733 + peerDependencies: 734 + '@types/react': '*' 735 + '@types/react-dom': '*' 736 + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc 737 + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc 738 + peerDependenciesMeta: 739 + '@types/react': 740 + optional: true 741 + '@types/react-dom': 742 + optional: true 743 + 744 + '@radix-ui/react-menubar@1.1.15': 745 + resolution: {integrity: sha512-Z71C7LGD+YDYo3TV81paUs8f3Zbmkvg6VLRQpKYfzioOE6n7fOhA3ApK/V/2Odolxjoc4ENk8AYCjohCNayd5A==} 746 + peerDependencies: 747 + '@types/react': '*' 748 + '@types/react-dom': '*' 749 + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc 750 + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc 751 + peerDependenciesMeta: 752 + '@types/react': 753 + optional: true 754 + '@types/react-dom': 755 + optional: true 756 + 757 + '@radix-ui/react-navigation-menu@1.2.13': 758 + resolution: {integrity: sha512-WG8wWfDiJlSF5hELjwfjSGOXcBR/ZMhBFCGYe8vERpC39CQYZeq1PQ2kaYHdye3V95d06H89KGMsVCIE4LWo3g==} 759 + peerDependencies: 760 + '@types/react': '*' 761 + '@types/react-dom': '*' 762 + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc 763 + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc 764 + peerDependenciesMeta: 765 + '@types/react': 766 + optional: true 767 + '@types/react-dom': 768 + optional: true 769 + 770 + '@radix-ui/react-popover@1.1.14': 771 + resolution: {integrity: sha512-ODz16+1iIbGUfFEfKx2HTPKizg2MN39uIOV8MXeHnmdd3i/N9Wt7vU46wbHsqA0xoaQyXVcs0KIlBdOA2Y95bw==} 772 + peerDependencies: 773 + '@types/react': '*' 774 + '@types/react-dom': '*' 775 + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc 776 + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc 777 + peerDependenciesMeta: 778 + '@types/react': 779 + optional: true 780 + '@types/react-dom': 781 + optional: true 782 + 783 + '@radix-ui/react-popper@1.2.7': 784 + resolution: {integrity: sha512-IUFAccz1JyKcf/RjB552PlWwxjeCJB8/4KxT7EhBHOJM+mN7LdW+B3kacJXILm32xawcMMjb2i0cIZpo+f9kiQ==} 785 + peerDependencies: 786 + '@types/react': '*' 787 + '@types/react-dom': '*' 788 + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc 789 + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc 790 + peerDependenciesMeta: 791 + '@types/react': 792 + optional: true 793 + '@types/react-dom': 794 + optional: true 795 + 796 + '@radix-ui/react-portal@1.1.9': 797 + resolution: {integrity: sha512-bpIxvq03if6UNwXZ+HTK71JLh4APvnXntDc6XOX8UVq4XQOVl7lwok0AvIl+b8zgCw3fSaVTZMpAPPagXbKmHQ==} 798 + peerDependencies: 799 + '@types/react': '*' 800 + '@types/react-dom': '*' 801 + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc 802 + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc 803 + peerDependenciesMeta: 804 + '@types/react': 805 + optional: true 806 + '@types/react-dom': 807 + optional: true 808 + 809 + '@radix-ui/react-presence@1.1.4': 810 + resolution: {integrity: sha512-ueDqRbdc4/bkaQT3GIpLQssRlFgWaL/U2z/S31qRwwLWoxHLgry3SIfCwhxeQNbirEUXFa+lq3RL3oBYXtcmIA==} 811 + peerDependencies: 812 + '@types/react': '*' 813 + '@types/react-dom': '*' 814 + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc 815 + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc 816 + peerDependenciesMeta: 817 + '@types/react': 818 + optional: true 819 + '@types/react-dom': 820 + optional: true 821 + 822 + '@radix-ui/react-primitive@2.1.3': 823 + resolution: {integrity: sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ==} 824 + peerDependencies: 825 + '@types/react': '*' 826 + '@types/react-dom': '*' 827 + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc 828 + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc 829 + peerDependenciesMeta: 830 + '@types/react': 831 + optional: true 832 + '@types/react-dom': 833 + optional: true 834 + 835 + '@radix-ui/react-progress@1.1.7': 836 + resolution: {integrity: sha512-vPdg/tF6YC/ynuBIJlk1mm7Le0VgW6ub6J2UWnTQ7/D23KXcPI1qy+0vBkgKgd38RCMJavBXpB83HPNFMTb0Fg==} 837 + peerDependencies: 838 + '@types/react': '*' 839 + '@types/react-dom': '*' 840 + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc 841 + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc 842 + peerDependenciesMeta: 843 + '@types/react': 844 + optional: true 845 + '@types/react-dom': 846 + optional: true 847 + 848 + '@radix-ui/react-radio-group@1.3.7': 849 + resolution: {integrity: sha512-9w5XhD0KPOrm92OTTE0SysH3sYzHsSTHNvZgUBo/VZ80VdYyB5RneDbc0dKpURS24IxkoFRu/hI0i4XyfFwY6g==} 850 + peerDependencies: 851 + '@types/react': '*' 852 + '@types/react-dom': '*' 853 + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc 854 + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc 855 + peerDependenciesMeta: 856 + '@types/react': 857 + optional: true 858 + '@types/react-dom': 859 + optional: true 860 + 861 + '@radix-ui/react-roving-focus@1.1.10': 862 + resolution: {integrity: sha512-dT9aOXUen9JSsxnMPv/0VqySQf5eDQ6LCk5Sw28kamz8wSOW2bJdlX2Bg5VUIIcV+6XlHpWTIuTPCf/UNIyq8Q==} 863 + peerDependencies: 864 + '@types/react': '*' 865 + '@types/react-dom': '*' 866 + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc 867 + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc 868 + peerDependenciesMeta: 869 + '@types/react': 870 + optional: true 871 + '@types/react-dom': 872 + optional: true 873 + 874 + '@radix-ui/react-scroll-area@1.2.9': 875 + resolution: {integrity: sha512-YSjEfBXnhUELsO2VzjdtYYD4CfQjvao+lhhrX5XsHD7/cyUNzljF1FHEbgTPN7LH2MClfwRMIsYlqTYpKTTe2A==} 876 + peerDependencies: 877 + '@types/react': '*' 878 + '@types/react-dom': '*' 879 + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc 880 + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc 881 + peerDependenciesMeta: 882 + '@types/react': 883 + optional: true 884 + '@types/react-dom': 885 + optional: true 886 + 887 + '@radix-ui/react-select@2.2.5': 888 + resolution: {integrity: sha512-HnMTdXEVuuyzx63ME0ut4+sEMYW6oouHWNGUZc7ddvUWIcfCva/AMoqEW/3wnEllriMWBa0RHspCYnfCWJQYmA==} 889 + peerDependencies: 890 + '@types/react': '*' 891 + '@types/react-dom': '*' 892 + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc 893 + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc 894 + peerDependenciesMeta: 895 + '@types/react': 896 + optional: true 897 + '@types/react-dom': 898 + optional: true 899 + 900 + '@radix-ui/react-separator@1.1.7': 901 + resolution: {integrity: sha512-0HEb8R9E8A+jZjvmFCy/J4xhbXy3TV+9XSnGJ3KvTtjlIUy/YQ/p6UYZvi7YbeoeXdyU9+Y3scizK6hkY37baA==} 902 + peerDependencies: 903 + '@types/react': '*' 904 + '@types/react-dom': '*' 905 + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc 906 + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc 907 + peerDependenciesMeta: 908 + '@types/react': 909 + optional: true 910 + '@types/react-dom': 911 + optional: true 912 + 913 + '@radix-ui/react-slider@1.3.5': 914 + resolution: {integrity: sha512-rkfe2pU2NBAYfGaxa3Mqosi7VZEWX5CxKaanRv0vZd4Zhl9fvQrg0VM93dv3xGLGfrHuoTRF3JXH8nb9g+B3fw==} 915 + peerDependencies: 916 + '@types/react': '*' 917 + '@types/react-dom': '*' 918 + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc 919 + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc 920 + peerDependenciesMeta: 921 + '@types/react': 922 + optional: true 923 + '@types/react-dom': 924 + optional: true 925 + 926 + '@radix-ui/react-slot@1.2.3': 927 + resolution: {integrity: sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==} 928 + peerDependencies: 929 + '@types/react': '*' 930 + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc 931 + peerDependenciesMeta: 932 + '@types/react': 933 + optional: true 934 + 935 + '@radix-ui/react-switch@1.2.5': 936 + resolution: {integrity: sha512-5ijLkak6ZMylXsaImpZ8u4Rlf5grRmoc0p0QeX9VJtlrM4f5m3nCTX8tWga/zOA8PZYIR/t0p2Mnvd7InrJ6yQ==} 937 + peerDependencies: 938 + '@types/react': '*' 939 + '@types/react-dom': '*' 940 + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc 941 + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc 942 + peerDependenciesMeta: 943 + '@types/react': 944 + optional: true 945 + '@types/react-dom': 946 + optional: true 947 + 948 + '@radix-ui/react-tabs@1.1.12': 949 + resolution: {integrity: sha512-GTVAlRVrQrSw3cEARM0nAx73ixrWDPNZAruETn3oHCNP6SbZ/hNxdxp+u7VkIEv3/sFoLq1PfcHrl7Pnp0CDpw==} 950 + peerDependencies: 951 + '@types/react': '*' 952 + '@types/react-dom': '*' 953 + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc 954 + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc 955 + peerDependenciesMeta: 956 + '@types/react': 957 + optional: true 958 + '@types/react-dom': 959 + optional: true 960 + 961 + '@radix-ui/react-toast@1.2.14': 962 + resolution: {integrity: sha512-nAP5FBxBJGQ/YfUB+r+O6USFVkWq3gAInkxyEnmvEV5jtSbfDhfa4hwX8CraCnbjMLsE7XSf/K75l9xXY7joWg==} 963 + peerDependencies: 964 + '@types/react': '*' 965 + '@types/react-dom': '*' 966 + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc 967 + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc 968 + peerDependenciesMeta: 969 + '@types/react': 970 + optional: true 971 + '@types/react-dom': 972 + optional: true 973 + 974 + '@radix-ui/react-toggle-group@1.1.10': 975 + resolution: {integrity: sha512-kiU694Km3WFLTC75DdqgM/3Jauf3rD9wxeS9XtyWFKsBUeZA337lC+6uUazT7I1DhanZ5gyD5Stf8uf2dbQxOQ==} 976 + peerDependencies: 977 + '@types/react': '*' 978 + '@types/react-dom': '*' 979 + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc 980 + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc 981 + peerDependenciesMeta: 982 + '@types/react': 983 + optional: true 984 + '@types/react-dom': 985 + optional: true 986 + 987 + '@radix-ui/react-toggle@1.1.9': 988 + resolution: {integrity: sha512-ZoFkBBz9zv9GWer7wIjvdRxmh2wyc2oKWw6C6CseWd6/yq1DK/l5lJ+wnsmFwJZbBYqr02mrf8A2q/CVCuM3ZA==} 989 + peerDependencies: 990 + '@types/react': '*' 991 + '@types/react-dom': '*' 992 + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc 993 + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc 994 + peerDependenciesMeta: 995 + '@types/react': 996 + optional: true 997 + '@types/react-dom': 998 + optional: true 999 + 1000 + '@radix-ui/react-tooltip@1.2.7': 1001 + resolution: {integrity: sha512-Ap+fNYwKTYJ9pzqW+Xe2HtMRbQ/EeWkj2qykZ6SuEV4iS/o1bZI5ssJbk4D2r8XuDuOBVz/tIx2JObtuqU+5Zw==} 1002 + peerDependencies: 1003 + '@types/react': '*' 1004 + '@types/react-dom': '*' 1005 + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc 1006 + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc 1007 + peerDependenciesMeta: 1008 + '@types/react': 1009 + optional: true 1010 + '@types/react-dom': 1011 + optional: true 1012 + 1013 + '@radix-ui/react-use-callback-ref@1.1.1': 1014 + resolution: {integrity: sha512-FkBMwD+qbGQeMu1cOHnuGB6x4yzPjho8ap5WtbEJ26umhgqVXbhekKUQO+hZEL1vU92a3wHwdp0HAcqAUF5iDg==} 1015 + peerDependencies: 1016 + '@types/react': '*' 1017 + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc 1018 + peerDependenciesMeta: 1019 + '@types/react': 1020 + optional: true 1021 + 1022 + '@radix-ui/react-use-controllable-state@1.2.2': 1023 + resolution: {integrity: sha512-BjasUjixPFdS+NKkypcyyN5Pmg83Olst0+c6vGov0diwTEo6mgdqVR6hxcEgFuh4QrAs7Rc+9KuGJ9TVCj0Zzg==} 1024 + peerDependencies: 1025 + '@types/react': '*' 1026 + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc 1027 + peerDependenciesMeta: 1028 + '@types/react': 1029 + optional: true 1030 + 1031 + '@radix-ui/react-use-effect-event@0.0.2': 1032 + resolution: {integrity: sha512-Qp8WbZOBe+blgpuUT+lw2xheLP8q0oatc9UpmiemEICxGvFLYmHm9QowVZGHtJlGbS6A6yJ3iViad/2cVjnOiA==} 1033 + peerDependencies: 1034 + '@types/react': '*' 1035 + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc 1036 + peerDependenciesMeta: 1037 + '@types/react': 1038 + optional: true 1039 + 1040 + '@radix-ui/react-use-escape-keydown@1.1.1': 1041 + resolution: {integrity: sha512-Il0+boE7w/XebUHyBjroE+DbByORGR9KKmITzbR7MyQ4akpORYP/ZmbhAr0DG7RmmBqoOnZdy2QlvajJ2QA59g==} 1042 + peerDependencies: 1043 + '@types/react': '*' 1044 + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc 1045 + peerDependenciesMeta: 1046 + '@types/react': 1047 + optional: true 1048 + 1049 + '@radix-ui/react-use-is-hydrated@0.1.0': 1050 + resolution: {integrity: sha512-U+UORVEq+cTnRIaostJv9AGdV3G6Y+zbVd+12e18jQ5A3c0xL03IhnHuiU4UV69wolOQp5GfR58NW/EgdQhwOA==} 1051 + peerDependencies: 1052 + '@types/react': '*' 1053 + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc 1054 + peerDependenciesMeta: 1055 + '@types/react': 1056 + optional: true 1057 + 1058 + '@radix-ui/react-use-layout-effect@1.1.1': 1059 + resolution: {integrity: sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ==} 1060 + peerDependencies: 1061 + '@types/react': '*' 1062 + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc 1063 + peerDependenciesMeta: 1064 + '@types/react': 1065 + optional: true 1066 + 1067 + '@radix-ui/react-use-previous@1.1.1': 1068 + resolution: {integrity: sha512-2dHfToCj/pzca2Ck724OZ5L0EVrr3eHRNsG/b3xQJLA2hZpVCS99bLAX+hm1IHXDEnzU6by5z/5MIY794/a8NQ==} 1069 + peerDependencies: 1070 + '@types/react': '*' 1071 + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc 1072 + peerDependenciesMeta: 1073 + '@types/react': 1074 + optional: true 1075 + 1076 + '@radix-ui/react-use-rect@1.1.1': 1077 + resolution: {integrity: sha512-QTYuDesS0VtuHNNvMh+CjlKJ4LJickCMUAqjlE3+j8w+RlRpwyX3apEQKGFzbZGdo7XNG1tXa+bQqIE7HIXT2w==} 1078 + peerDependencies: 1079 + '@types/react': '*' 1080 + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc 1081 + peerDependenciesMeta: 1082 + '@types/react': 1083 + optional: true 1084 + 1085 + '@radix-ui/react-use-size@1.1.1': 1086 + resolution: {integrity: sha512-ewrXRDTAqAXlkl6t/fkXWNAhFX9I+CkKlw6zjEwk86RSPKwZr3xpBRso655aqYafwtnbpHLj6toFzmd6xdVptQ==} 1087 + peerDependencies: 1088 + '@types/react': '*' 1089 + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc 1090 + peerDependenciesMeta: 1091 + '@types/react': 1092 + optional: true 1093 + 1094 + '@radix-ui/react-visually-hidden@1.2.3': 1095 + resolution: {integrity: sha512-pzJq12tEaaIhqjbzpCuv/OypJY/BPavOofm+dbab+MHLajy277+1lLm6JFcGgF5eskJ6mquGirhXY2GD/8u8Ug==} 1096 + peerDependencies: 1097 + '@types/react': '*' 1098 + '@types/react-dom': '*' 1099 + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc 1100 + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc 1101 + peerDependenciesMeta: 1102 + '@types/react': 1103 + optional: true 1104 + '@types/react-dom': 1105 + optional: true 1106 + 1107 + '@radix-ui/rect@1.1.1': 1108 + resolution: {integrity: sha512-HPwpGIzkl28mWyZqG52jiqDJ12waP11Pa1lGoiyUkIEuMLBP0oeK/C89esbXrxsky5we7dfd8U58nm0SgAWpVw==} 1109 + 1110 + '@remix-run/router@1.23.0': 1111 + resolution: {integrity: sha512-O3rHJzAQKamUz1fvE0Qaw0xSFqsA/yafi2iqeE0pvdFtCO1viYx8QL6f3Ln/aCCTLxs68SLf0KPM9eSeM8yBnA==} 1112 + engines: {node: '>=14.0.0'} 1113 + 1114 + '@rolldown/pluginutils@1.0.0-beta.11': 1115 + resolution: {integrity: sha512-L/gAA/hyCSuzTF1ftlzUSI/IKr2POHsv1Dd78GfqkR83KMNuswWD61JxGV2L7nRwBBBSDr6R1gCkdTmoN7W4ag==} 1116 + 1117 + '@rollup/rollup-android-arm-eabi@4.44.0': 1118 + resolution: {integrity: sha512-xEiEE5oDW6tK4jXCAyliuntGR+amEMO7HLtdSshVuhFnKTYoeYMyXQK7pLouAJJj5KHdwdn87bfHAR2nSdNAUA==} 1119 + cpu: [arm] 1120 + os: [android] 1121 + 1122 + '@rollup/rollup-android-arm64@4.44.0': 1123 + resolution: {integrity: sha512-uNSk/TgvMbskcHxXYHzqwiyBlJ/lGcv8DaUfcnNwict8ba9GTTNxfn3/FAoFZYgkaXXAdrAA+SLyKplyi349Jw==} 1124 + cpu: [arm64] 1125 + os: [android] 1126 + 1127 + '@rollup/rollup-darwin-arm64@4.44.0': 1128 + resolution: {integrity: sha512-VGF3wy0Eq1gcEIkSCr8Ke03CWT+Pm2yveKLaDvq51pPpZza3JX/ClxXOCmTYYq3us5MvEuNRTaeyFThCKRQhOA==} 1129 + cpu: [arm64] 1130 + os: [darwin] 1131 + 1132 + '@rollup/rollup-darwin-x64@4.44.0': 1133 + resolution: {integrity: sha512-fBkyrDhwquRvrTxSGH/qqt3/T0w5Rg0L7ZIDypvBPc1/gzjJle6acCpZ36blwuwcKD/u6oCE/sRWlUAcxLWQbQ==} 1134 + cpu: [x64] 1135 + os: [darwin] 1136 + 1137 + '@rollup/rollup-freebsd-arm64@4.44.0': 1138 + resolution: {integrity: sha512-u5AZzdQJYJXByB8giQ+r4VyfZP+walV+xHWdaFx/1VxsOn6eWJhK2Vl2eElvDJFKQBo/hcYIBg/jaKS8ZmKeNQ==} 1139 + cpu: [arm64] 1140 + os: [freebsd] 1141 + 1142 + '@rollup/rollup-freebsd-x64@4.44.0': 1143 + resolution: {integrity: sha512-qC0kS48c/s3EtdArkimctY7h3nHicQeEUdjJzYVJYR3ct3kWSafmn6jkNCA8InbUdge6PVx6keqjk5lVGJf99g==} 1144 + cpu: [x64] 1145 + os: [freebsd] 1146 + 1147 + '@rollup/rollup-linux-arm-gnueabihf@4.44.0': 1148 + resolution: {integrity: sha512-x+e/Z9H0RAWckn4V2OZZl6EmV0L2diuX3QB0uM1r6BvhUIv6xBPL5mrAX2E3e8N8rEHVPwFfz/ETUbV4oW9+lQ==} 1149 + cpu: [arm] 1150 + os: [linux] 1151 + 1152 + '@rollup/rollup-linux-arm-musleabihf@4.44.0': 1153 + resolution: {integrity: sha512-1exwiBFf4PU/8HvI8s80icyCcnAIB86MCBdst51fwFmH5dyeoWVPVgmQPcKrMtBQ0W5pAs7jBCWuRXgEpRzSCg==} 1154 + cpu: [arm] 1155 + os: [linux] 1156 + 1157 + '@rollup/rollup-linux-arm64-gnu@4.44.0': 1158 + resolution: {integrity: sha512-ZTR2mxBHb4tK4wGf9b8SYg0Y6KQPjGpR4UWwTFdnmjB4qRtoATZ5dWn3KsDwGa5Z2ZBOE7K52L36J9LueKBdOQ==} 1159 + cpu: [arm64] 1160 + os: [linux] 1161 + 1162 + '@rollup/rollup-linux-arm64-musl@4.44.0': 1163 + resolution: {integrity: sha512-GFWfAhVhWGd4r6UxmnKRTBwP1qmModHtd5gkraeW2G490BpFOZkFtem8yuX2NyafIP/mGpRJgTJ2PwohQkUY/Q==} 1164 + cpu: [arm64] 1165 + os: [linux] 1166 + 1167 + '@rollup/rollup-linux-loongarch64-gnu@4.44.0': 1168 + resolution: {integrity: sha512-xw+FTGcov/ejdusVOqKgMGW3c4+AgqrfvzWEVXcNP6zq2ue+lsYUgJ+5Rtn/OTJf7e2CbgTFvzLW2j0YAtj0Gg==} 1169 + cpu: [loong64] 1170 + os: [linux] 1171 + 1172 + '@rollup/rollup-linux-powerpc64le-gnu@4.44.0': 1173 + resolution: {integrity: sha512-bKGibTr9IdF0zr21kMvkZT4K6NV+jjRnBoVMt2uNMG0BYWm3qOVmYnXKzx7UhwrviKnmK46IKMByMgvpdQlyJQ==} 1174 + cpu: [ppc64] 1175 + os: [linux] 1176 + 1177 + '@rollup/rollup-linux-riscv64-gnu@4.44.0': 1178 + resolution: {integrity: sha512-vV3cL48U5kDaKZtXrti12YRa7TyxgKAIDoYdqSIOMOFBXqFj2XbChHAtXquEn2+n78ciFgr4KIqEbydEGPxXgA==} 1179 + cpu: [riscv64] 1180 + os: [linux] 1181 + 1182 + '@rollup/rollup-linux-riscv64-musl@4.44.0': 1183 + resolution: {integrity: sha512-TDKO8KlHJuvTEdfw5YYFBjhFts2TR0VpZsnLLSYmB7AaohJhM8ctDSdDnUGq77hUh4m/djRafw+9zQpkOanE2Q==} 1184 + cpu: [riscv64] 1185 + os: [linux] 1186 + 1187 + '@rollup/rollup-linux-s390x-gnu@4.44.0': 1188 + resolution: {integrity: sha512-8541GEyktXaw4lvnGp9m84KENcxInhAt6vPWJ9RodsB/iGjHoMB2Pp5MVBCiKIRxrxzJhGCxmNzdu+oDQ7kwRA==} 1189 + cpu: [s390x] 1190 + os: [linux] 1191 + 1192 + '@rollup/rollup-linux-x64-gnu@4.44.0': 1193 + resolution: {integrity: sha512-iUVJc3c0o8l9Sa/qlDL2Z9UP92UZZW1+EmQ4xfjTc1akr0iUFZNfxrXJ/R1T90h/ILm9iXEY6+iPrmYB3pXKjw==} 1194 + cpu: [x64] 1195 + os: [linux] 1196 + 1197 + '@rollup/rollup-linux-x64-musl@4.44.0': 1198 + resolution: {integrity: sha512-PQUobbhLTQT5yz/SPg116VJBgz+XOtXt8D1ck+sfJJhuEsMj2jSej5yTdp8CvWBSceu+WW+ibVL6dm0ptG5fcA==} 1199 + cpu: [x64] 1200 + os: [linux] 1201 + 1202 + '@rollup/rollup-win32-arm64-msvc@4.44.0': 1203 + resolution: {integrity: sha512-M0CpcHf8TWn+4oTxJfh7LQuTuaYeXGbk0eageVjQCKzYLsajWS/lFC94qlRqOlyC2KvRT90ZrfXULYmukeIy7w==} 1204 + cpu: [arm64] 1205 + os: [win32] 1206 + 1207 + '@rollup/rollup-win32-ia32-msvc@4.44.0': 1208 + resolution: {integrity: sha512-3XJ0NQtMAXTWFW8FqZKcw3gOQwBtVWP/u8TpHP3CRPXD7Pd6s8lLdH3sHWh8vqKCyyiI8xW5ltJScQmBU9j7WA==} 1209 + cpu: [ia32] 1210 + os: [win32] 1211 + 1212 + '@rollup/rollup-win32-x64-msvc@4.44.0': 1213 + resolution: {integrity: sha512-Q2Mgwt+D8hd5FIPUuPDsvPR7Bguza6yTkJxspDGkZj7tBRn2y4KSWYuIXpftFSjBra76TbKerCV7rgFPQrn+wQ==} 1214 + cpu: [x64] 1215 + os: [win32] 1216 + 1217 + '@swc/core-darwin-arm64@1.12.4': 1218 + resolution: {integrity: sha512-HihKfeitjZU2ab94Zf893sxzFryLKX0TweGsNXXOLNtkSMLw50auuYfpRM0BOL9/uXXtuCWgRIF6P030SAX5xQ==} 1219 + engines: {node: '>=10'} 1220 + cpu: [arm64] 1221 + os: [darwin] 1222 + 1223 + '@swc/core-darwin-x64@1.12.4': 1224 + resolution: {integrity: sha512-meYCXHyYb6RDdu2N5PNAf0EelyxPBFhRcVo4kBFLuvuNb0m6EUg///VWy8MUMXq9/s9uzGS9kJVXXdRdr/d6FA==} 1225 + engines: {node: '>=10'} 1226 + cpu: [x64] 1227 + os: [darwin] 1228 + 1229 + '@swc/core-linux-arm-gnueabihf@1.12.4': 1230 + resolution: {integrity: sha512-szfDbf7mE8V64of0q/LSqbk+em+T+TD3uqnH40Z7Qu/aL8vi5CHgyLjWG2SLkLLpyjgkAUF6AKrupgnBYcC2NA==} 1231 + engines: {node: '>=10'} 1232 + cpu: [arm] 1233 + os: [linux] 1234 + 1235 + '@swc/core-linux-arm64-gnu@1.12.4': 1236 + resolution: {integrity: sha512-n0IY76w+Scx8m3HIVRvLkoResuwsQgjDfAk9bxn99dq4leQO+mE0fkPl0Yw/1BIsPh+kxGfopIJH9zsZ1Z2YrA==} 1237 + engines: {node: '>=10'} 1238 + cpu: [arm64] 1239 + os: [linux] 1240 + 1241 + '@swc/core-linux-arm64-musl@1.12.4': 1242 + resolution: {integrity: sha512-wE5jmFi5cEQyLy8WmCWmNwfKETrnzy2D8YNi/xpYWpLPWqPhcelpa6tswkfYlbsMmmOh7hQNoTba1QdGu0jvHQ==} 1243 + engines: {node: '>=10'} 1244 + cpu: [arm64] 1245 + os: [linux] 1246 + 1247 + '@swc/core-linux-x64-gnu@1.12.4': 1248 + resolution: {integrity: sha512-6S50Xd/7ePjEwrXyHMxpKTZ+KBrgUwMA8hQPbArUOwH4S5vHBr51heL0iXbUkppn1bkSr0J0IbOove5hzn+iqQ==} 1249 + engines: {node: '>=10'} 1250 + cpu: [x64] 1251 + os: [linux] 1252 + 1253 + '@swc/core-linux-x64-musl@1.12.4': 1254 + resolution: {integrity: sha512-hbYRyaHhC13vYKuGG5BrAG5fjjWEQFfQetuFp/4QKEoXDzdnabJoixxWTQACDL3m0JW32nJ+gUzsYIPtFYkwXg==} 1255 + engines: {node: '>=10'} 1256 + cpu: [x64] 1257 + os: [linux] 1258 + 1259 + '@swc/core-win32-arm64-msvc@1.12.4': 1260 + resolution: {integrity: sha512-e6EbfjPL8GA/bb1lc9Omtxjlz+1ThTsAuBsy4Q3Kpbuh6B3jclg8KzxU/6t91v23wG593mieTyR5f3Pr7X3AWw==} 1261 + engines: {node: '>=10'} 1262 + cpu: [arm64] 1263 + os: [win32] 1264 + 1265 + '@swc/core-win32-ia32-msvc@1.12.4': 1266 + resolution: {integrity: sha512-RG2FzmllBTUf4EksANlIvLckcBrLZEA0t13LIa6L213UZKQfEuDNHezqESgoVhJMg2S/tWauitATOCFgZNSmjg==} 1267 + engines: {node: '>=10'} 1268 + cpu: [ia32] 1269 + os: [win32] 1270 + 1271 + '@swc/core-win32-x64-msvc@1.12.4': 1272 + resolution: {integrity: sha512-oRHKnZlR83zaMeVUCmHENa4j5uNRAWbmEpjYbzRcfC45LPFNWKGWGAGERLx0u87XMUtTGqnVYxnBTHN/rzDHOw==} 1273 + engines: {node: '>=10'} 1274 + cpu: [x64] 1275 + os: [win32] 1276 + 1277 + '@swc/core@1.12.4': 1278 + resolution: {integrity: sha512-hn30ebV4njAn0NAUM+3a0qCF+MJgqTNSrfA/hUAbC6TVjOQy2OYGQwkUvCu/V7S2+rZxrUsTpKOnZ7qqECZV9Q==} 1279 + engines: {node: '>=10'} 1280 + peerDependencies: 1281 + '@swc/helpers': '>=0.5.17' 1282 + peerDependenciesMeta: 1283 + '@swc/helpers': 1284 + optional: true 1285 + 1286 + '@swc/counter@0.1.3': 1287 + resolution: {integrity: sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==} 1288 + 1289 + '@swc/types@0.1.23': 1290 + resolution: {integrity: sha512-u1iIVZV9Q0jxY+yM2vw/hZGDNudsN85bBpTqzAQ9rzkxW9D+e3aEM4Han+ow518gSewkXgjmEK0BD79ZcNVgPw==} 1291 + 1292 + '@tailwindcss/node@4.1.10': 1293 + resolution: {integrity: sha512-2ACf1znY5fpRBwRhMgj9ZXvb2XZW8qs+oTfotJ2C5xR0/WNL7UHZ7zXl6s+rUqedL1mNi+0O+WQr5awGowS3PQ==} 1294 + 1295 + '@tailwindcss/oxide-android-arm64@4.1.10': 1296 + resolution: {integrity: sha512-VGLazCoRQ7rtsCzThaI1UyDu/XRYVyH4/EWiaSX6tFglE+xZB5cvtC5Omt0OQ+FfiIVP98su16jDVHDEIuH4iQ==} 1297 + engines: {node: '>= 10'} 1298 + cpu: [arm64] 1299 + os: [android] 1300 + 1301 + '@tailwindcss/oxide-darwin-arm64@4.1.10': 1302 + resolution: {integrity: sha512-ZIFqvR1irX2yNjWJzKCqTCcHZbgkSkSkZKbRM3BPzhDL/18idA8uWCoopYA2CSDdSGFlDAxYdU2yBHwAwx8euQ==} 1303 + engines: {node: '>= 10'} 1304 + cpu: [arm64] 1305 + os: [darwin] 1306 + 1307 + '@tailwindcss/oxide-darwin-x64@4.1.10': 1308 + resolution: {integrity: sha512-eCA4zbIhWUFDXoamNztmS0MjXHSEJYlvATzWnRiTqJkcUteSjO94PoRHJy1Xbwp9bptjeIxxBHh+zBWFhttbrQ==} 1309 + engines: {node: '>= 10'} 1310 + cpu: [x64] 1311 + os: [darwin] 1312 + 1313 + '@tailwindcss/oxide-freebsd-x64@4.1.10': 1314 + resolution: {integrity: sha512-8/392Xu12R0cc93DpiJvNpJ4wYVSiciUlkiOHOSOQNH3adq9Gi/dtySK7dVQjXIOzlpSHjeCL89RUUI8/GTI6g==} 1315 + engines: {node: '>= 10'} 1316 + cpu: [x64] 1317 + os: [freebsd] 1318 + 1319 + '@tailwindcss/oxide-linux-arm-gnueabihf@4.1.10': 1320 + resolution: {integrity: sha512-t9rhmLT6EqeuPT+MXhWhlRYIMSfh5LZ6kBrC4FS6/+M1yXwfCtp24UumgCWOAJVyjQwG+lYva6wWZxrfvB+NhQ==} 1321 + engines: {node: '>= 10'} 1322 + cpu: [arm] 1323 + os: [linux] 1324 + 1325 + '@tailwindcss/oxide-linux-arm64-gnu@4.1.10': 1326 + resolution: {integrity: sha512-3oWrlNlxLRxXejQ8zImzrVLuZ/9Z2SeKoLhtCu0hpo38hTO2iL86eFOu4sVR8cZc6n3z7eRXXqtHJECa6mFOvA==} 1327 + engines: {node: '>= 10'} 1328 + cpu: [arm64] 1329 + os: [linux] 1330 + 1331 + '@tailwindcss/oxide-linux-arm64-musl@4.1.10': 1332 + resolution: {integrity: sha512-saScU0cmWvg/Ez4gUmQWr9pvY9Kssxt+Xenfx1LG7LmqjcrvBnw4r9VjkFcqmbBb7GCBwYNcZi9X3/oMda9sqQ==} 1333 + engines: {node: '>= 10'} 1334 + cpu: [arm64] 1335 + os: [linux] 1336 + 1337 + '@tailwindcss/oxide-linux-x64-gnu@4.1.10': 1338 + resolution: {integrity: sha512-/G3ao/ybV9YEEgAXeEg28dyH6gs1QG8tvdN9c2MNZdUXYBaIY/Gx0N6RlJzfLy/7Nkdok4kaxKPHKJUlAaoTdA==} 1339 + engines: {node: '>= 10'} 1340 + cpu: [x64] 1341 + os: [linux] 1342 + 1343 + '@tailwindcss/oxide-linux-x64-musl@4.1.10': 1344 + resolution: {integrity: sha512-LNr7X8fTiKGRtQGOerSayc2pWJp/9ptRYAa4G+U+cjw9kJZvkopav1AQc5HHD+U364f71tZv6XamaHKgrIoVzA==} 1345 + engines: {node: '>= 10'} 1346 + cpu: [x64] 1347 + os: [linux] 1348 + 1349 + '@tailwindcss/oxide-wasm32-wasi@4.1.10': 1350 + resolution: {integrity: sha512-d6ekQpopFQJAcIK2i7ZzWOYGZ+A6NzzvQ3ozBvWFdeyqfOZdYHU66g5yr+/HC4ipP1ZgWsqa80+ISNILk+ae/Q==} 1351 + engines: {node: '>=14.0.0'} 1352 + cpu: [wasm32] 1353 + bundledDependencies: 1354 + - '@napi-rs/wasm-runtime' 1355 + - '@emnapi/core' 1356 + - '@emnapi/runtime' 1357 + - '@tybys/wasm-util' 1358 + - '@emnapi/wasi-threads' 1359 + - tslib 1360 + 1361 + '@tailwindcss/oxide-win32-arm64-msvc@4.1.10': 1362 + resolution: {integrity: sha512-i1Iwg9gRbwNVOCYmnigWCCgow8nDWSFmeTUU5nbNx3rqbe4p0kRbEqLwLJbYZKmSSp23g4N6rCDmm7OuPBXhDA==} 1363 + engines: {node: '>= 10'} 1364 + cpu: [arm64] 1365 + os: [win32] 1366 + 1367 + '@tailwindcss/oxide-win32-x64-msvc@4.1.10': 1368 + resolution: {integrity: sha512-sGiJTjcBSfGq2DVRtaSljq5ZgZS2SDHSIfhOylkBvHVjwOsodBhnb3HdmiKkVuUGKD0I7G63abMOVaskj1KpOA==} 1369 + engines: {node: '>= 10'} 1370 + cpu: [x64] 1371 + os: [win32] 1372 + 1373 + '@tailwindcss/oxide@4.1.10': 1374 + resolution: {integrity: sha512-v0C43s7Pjw+B9w21htrQwuFObSkio2aV/qPx/mhrRldbqxbWJK6KizM+q7BF1/1CmuLqZqX3CeYF7s7P9fbA8Q==} 1375 + engines: {node: '>= 10'} 1376 + 1377 + '@tailwindcss/postcss@4.1.10': 1378 + resolution: {integrity: sha512-B+7r7ABZbkXJwpvt2VMnS6ujcDoR2OOcFaqrLIo1xbcdxje4Vf+VgJdBzNNbrAjBj/rLZ66/tlQ1knIGNLKOBQ==} 1379 + 1380 + '@tailwindcss/typography@0.5.16': 1381 + resolution: {integrity: sha512-0wDLwCVF5V3x3b1SGXPCDcdsbDHMBe+lkFzBRaHeLvNi+nrrnZ1lA18u+OTWO8iSWU2GxUOCvlXtDuqftc1oiA==} 1382 + peerDependencies: 1383 + tailwindcss: '>=3.0.0 || insiders || >=4.0.0-alpha.20 || >=4.0.0-beta.1' 1384 + 1385 + '@tailwindcss/vite@4.1.10': 1386 + resolution: {integrity: sha512-QWnD5HDY2IADv+vYR82lOhqOlS1jSCUUAmfem52cXAhRTKxpDh3ARX8TTXJTCCO7Rv7cD2Nlekabv02bwP3a2A==} 1387 + peerDependencies: 1388 + vite: ^5.2.0 || ^6 1389 + 1390 + '@types/d3-array@3.2.1': 1391 + resolution: {integrity: sha512-Y2Jn2idRrLzUfAKV2LyRImR+y4oa2AntrgID95SHJxuMUrkNXmanDSed71sRNZysveJVt1hLLemQZIady0FpEg==} 1392 + 1393 + '@types/d3-color@3.1.3': 1394 + resolution: {integrity: sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A==} 1395 + 1396 + '@types/d3-ease@3.0.2': 1397 + resolution: {integrity: sha512-NcV1JjO5oDzoK26oMzbILE6HW7uVXOHLQvHshBUW4UMdZGfiY6v5BeQwh9a9tCzv+CeefZQHJt5SRgK154RtiA==} 1398 + 1399 + '@types/d3-interpolate@3.0.4': 1400 + resolution: {integrity: sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA==} 1401 + 1402 + '@types/d3-path@3.1.1': 1403 + resolution: {integrity: sha512-VMZBYyQvbGmWyWVea0EHs/BwLgxc+MKi1zLDCONksozI4YJMcTt8ZEuIR4Sb1MMTE8MMW49v0IwI5+b7RmfWlg==} 1404 + 1405 + '@types/d3-scale@4.0.9': 1406 + resolution: {integrity: sha512-dLmtwB8zkAeO/juAMfnV+sItKjlsw2lKdZVVy6LRr0cBmegxSABiLEpGVmSJJ8O08i4+sGR6qQtb6WtuwJdvVw==} 1407 + 1408 + '@types/d3-shape@3.1.7': 1409 + resolution: {integrity: sha512-VLvUQ33C+3J+8p+Daf+nYSOsjB4GXp19/S/aGo60m9h1v6XaxjiT82lKVWJCfzhtuZ3yD7i/TPeC/fuKLLOSmg==} 1410 + 1411 + '@types/d3-time@3.0.4': 1412 + resolution: {integrity: sha512-yuzZug1nkAAaBlBBikKZTgzCeA+k1uy4ZFwWANOfKw5z5LRhV0gNA7gNkKm7HoK+HRN0wX3EkxGk0fpbWhmB7g==} 1413 + 1414 + '@types/d3-timer@3.0.2': 1415 + resolution: {integrity: sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw==} 1416 + 1417 + '@types/estree@1.0.8': 1418 + resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} 1419 + 1420 + '@types/json-schema@7.0.15': 1421 + resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} 1422 + 1423 + '@types/node@22.15.32': 1424 + resolution: {integrity: sha512-3jigKqgSjsH6gYZv2nEsqdXfZqIFGAV36XYYjf9KGZ3PSG+IhLecqPnI310RvjutyMwifE2hhhNEklOUrvx/wA==} 1425 + 1426 + '@types/prop-types@15.7.15': 1427 + resolution: {integrity: sha512-F6bEyamV9jKGAFBEmlQnesRPGOQqS2+Uwi0Em15xenOxHaf2hv6L8YCVn3rPdPJOiJfPiCnLIRyvwVaqMY3MIw==} 1428 + 1429 + '@types/react-dom@18.3.7': 1430 + resolution: {integrity: sha512-MEe3UeoENYVFXzoXEWsvcpg6ZvlrFNlOQ7EOsvhI3CfAXwzPfO8Qwuxd40nepsYKqyyVQnTdEfv68q91yLcKrQ==} 1431 + peerDependencies: 1432 + '@types/react': ^18.0.0 1433 + 1434 + '@types/react@18.3.23': 1435 + resolution: {integrity: sha512-/LDXMQh55EzZQ0uVAZmKKhfENivEvWz6E+EYzh+/MCjMhNsotd+ZHhBGIjFDTi6+fz0OhQQQLbTgdQIxxCsC0w==} 1436 + 1437 + '@typescript-eslint/eslint-plugin@8.34.1': 1438 + resolution: {integrity: sha512-STXcN6ebF6li4PxwNeFnqF8/2BNDvBupf2OPx2yWNzr6mKNGF7q49VM00Pz5FaomJyqvbXpY6PhO+T9w139YEQ==} 1439 + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} 1440 + peerDependencies: 1441 + '@typescript-eslint/parser': ^8.34.1 1442 + eslint: ^8.57.0 || ^9.0.0 1443 + typescript: '>=4.8.4 <5.9.0' 1444 + 1445 + '@typescript-eslint/parser@8.34.1': 1446 + resolution: {integrity: sha512-4O3idHxhyzjClSMJ0a29AcoK0+YwnEqzI6oz3vlRf3xw0zbzt15MzXwItOlnr5nIth6zlY2RENLsOPvhyrKAQA==} 1447 + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} 1448 + peerDependencies: 1449 + eslint: ^8.57.0 || ^9.0.0 1450 + typescript: '>=4.8.4 <5.9.0' 1451 + 1452 + '@typescript-eslint/project-service@8.34.1': 1453 + resolution: {integrity: sha512-nuHlOmFZfuRwLJKDGQOVc0xnQrAmuq1Mj/ISou5044y1ajGNp2BNliIqp7F2LPQ5sForz8lempMFCovfeS1XoA==} 1454 + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} 1455 + peerDependencies: 1456 + typescript: '>=4.8.4 <5.9.0' 1457 + 1458 + '@typescript-eslint/scope-manager@8.34.1': 1459 + resolution: {integrity: sha512-beu6o6QY4hJAgL1E8RaXNC071G4Kso2MGmJskCFQhRhg8VOH/FDbC8soP8NHN7e/Hdphwp8G8cE6OBzC8o41ZA==} 1460 + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} 1461 + 1462 + '@typescript-eslint/tsconfig-utils@8.34.1': 1463 + resolution: {integrity: sha512-K4Sjdo4/xF9NEeA2khOb7Y5nY6NSXBnod87uniVYW9kHP+hNlDV8trUSFeynA2uxWam4gIWgWoygPrv9VMWrYg==} 1464 + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} 1465 + peerDependencies: 1466 + typescript: '>=4.8.4 <5.9.0' 1467 + 1468 + '@typescript-eslint/type-utils@8.34.1': 1469 + resolution: {integrity: sha512-Tv7tCCr6e5m8hP4+xFugcrwTOucB8lshffJ6zf1mF1TbU67R+ntCc6DzLNKM+s/uzDyv8gLq7tufaAhIBYeV8g==} 1470 + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} 1471 + peerDependencies: 1472 + eslint: ^8.57.0 || ^9.0.0 1473 + typescript: '>=4.8.4 <5.9.0' 1474 + 1475 + '@typescript-eslint/types@8.34.1': 1476 + resolution: {integrity: sha512-rjLVbmE7HR18kDsjNIZQHxmv9RZwlgzavryL5Lnj2ujIRTeXlKtILHgRNmQ3j4daw7zd+mQgy+uyt6Zo6I0IGA==} 1477 + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} 1478 + 1479 + '@typescript-eslint/typescript-estree@8.34.1': 1480 + resolution: {integrity: sha512-rjCNqqYPuMUF5ODD+hWBNmOitjBWghkGKJg6hiCHzUvXRy6rK22Jd3rwbP2Xi+R7oYVvIKhokHVhH41BxPV5mA==} 1481 + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} 1482 + peerDependencies: 1483 + typescript: '>=4.8.4 <5.9.0' 1484 + 1485 + '@typescript-eslint/utils@8.34.1': 1486 + resolution: {integrity: sha512-mqOwUdZ3KjtGk7xJJnLbHxTuWVn3GO2WZZuM+Slhkun4+qthLdXx32C8xIXbO1kfCECb3jIs3eoxK3eryk7aoQ==} 1487 + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} 1488 + peerDependencies: 1489 + eslint: ^8.57.0 || ^9.0.0 1490 + typescript: '>=4.8.4 <5.9.0' 1491 + 1492 + '@typescript-eslint/visitor-keys@8.34.1': 1493 + resolution: {integrity: sha512-xoh5rJ+tgsRKoXnkBPFRLZ7rjKM0AfVbC68UZ/ECXoDbfggb9RbEySN359acY1vS3qZ0jVTVWzbtfapwm5ztxw==} 1494 + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} 1495 + 1496 + '@vitejs/plugin-react-swc@3.10.2': 1497 + resolution: {integrity: sha512-xD3Rdvrt5LgANug7WekBn1KhcvLn1H3jNBfJRL3reeOIua/WnZOEV5qi5qIBq5T8R0jUDmRtxuvk4bPhzGHDWw==} 1498 + peerDependencies: 1499 + vite: ^4 || ^5 || ^6 || ^7.0.0-beta.0 1500 + 1501 + acorn-jsx@5.3.2: 1502 + resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} 1503 + peerDependencies: 1504 + acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 1505 + 1506 + acorn@8.15.0: 1507 + resolution: {integrity: sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==} 1508 + engines: {node: '>=0.4.0'} 1509 + hasBin: true 1510 + 1511 + ajv@6.12.6: 1512 + resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} 1513 + 1514 + ansi-styles@4.3.0: 1515 + resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} 1516 + engines: {node: '>=8'} 1517 + 1518 + argparse@2.0.1: 1519 + resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} 1520 + 1521 + aria-hidden@1.2.6: 1522 + resolution: {integrity: sha512-ik3ZgC9dY/lYVVM++OISsaYDeg1tb0VtP5uL3ouh1koGOaUMDPpbFIei4JkFimWUFPn90sbMNMXQAIVOlnYKJA==} 1523 + engines: {node: '>=10'} 1524 + 1525 + balanced-match@1.0.2: 1526 + resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} 1527 + 1528 + base64-arraybuffer@1.0.2: 1529 + resolution: {integrity: sha512-I3yl4r9QB5ZRY3XuJVEPfc2XhZO6YweFPI+UovAzn+8/hb3oJ6lnysaFcjVpkCPfVWFUDvoZ8kmVDP7WyRtYtQ==} 1530 + engines: {node: '>= 0.6.0'} 1531 + 1532 + brace-expansion@1.1.12: 1533 + resolution: {integrity: sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==} 1534 + 1535 + brace-expansion@2.0.2: 1536 + resolution: {integrity: sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==} 1537 + 1538 + braces@3.0.3: 1539 + resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} 1540 + engines: {node: '>=8'} 1541 + 1542 + callsites@3.1.0: 1543 + resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} 1544 + engines: {node: '>=6'} 1545 + 1546 + chalk@4.1.2: 1547 + resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} 1548 + engines: {node: '>=10'} 1549 + 1550 + chownr@3.0.0: 1551 + resolution: {integrity: sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==} 1552 + engines: {node: '>=18'} 1553 + 1554 + class-variance-authority@0.7.1: 1555 + resolution: {integrity: sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg==} 1556 + 1557 + clsx@2.1.1: 1558 + resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==} 1559 + engines: {node: '>=6'} 1560 + 1561 + cmdk@1.1.1: 1562 + resolution: {integrity: sha512-Vsv7kFaXm+ptHDMZ7izaRsP70GgrW9NBNGswt9OZaVBLlE0SNpDq8eu/VGXyF9r7M0azK3Wy7OlYXsuyYLFzHg==} 1563 + peerDependencies: 1564 + react: ^18 || ^19 || ^19.0.0-rc 1565 + react-dom: ^18 || ^19 || ^19.0.0-rc 1566 + 1567 + color-convert@2.0.1: 1568 + resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} 1569 + engines: {node: '>=7.0.0'} 1570 + 1571 + color-name@1.1.4: 1572 + resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} 1573 + 1574 + concat-map@0.0.1: 1575 + resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} 1576 + 1577 + cross-spawn@7.0.6: 1578 + resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} 1579 + engines: {node: '>= 8'} 1580 + 1581 + css-line-break@2.1.0: 1582 + resolution: {integrity: sha512-FHcKFCZcAha3LwfVBhCQbW2nCNbkZXn7KVUJcsT5/P8YmfsVja0FMPJr0B903j/E69HUphKiV9iQArX8SDYA4w==} 1583 + 1584 + cssesc@3.0.0: 1585 + resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==} 1586 + engines: {node: '>=4'} 1587 + hasBin: true 1588 + 1589 + csstype@3.1.3: 1590 + resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==} 1591 + 1592 + d3-array@3.2.4: 1593 + resolution: {integrity: sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==} 1594 + engines: {node: '>=12'} 1595 + 1596 + d3-color@3.1.0: 1597 + resolution: {integrity: sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==} 1598 + engines: {node: '>=12'} 1599 + 1600 + d3-ease@3.0.1: 1601 + resolution: {integrity: sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==} 1602 + engines: {node: '>=12'} 1603 + 1604 + d3-format@3.1.0: 1605 + resolution: {integrity: sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA==} 1606 + engines: {node: '>=12'} 1607 + 1608 + d3-interpolate@3.0.1: 1609 + resolution: {integrity: sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==} 1610 + engines: {node: '>=12'} 1611 + 1612 + d3-path@3.1.0: 1613 + resolution: {integrity: sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==} 1614 + engines: {node: '>=12'} 1615 + 1616 + d3-scale@4.0.2: 1617 + resolution: {integrity: sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==} 1618 + engines: {node: '>=12'} 1619 + 1620 + d3-shape@3.2.0: 1621 + resolution: {integrity: sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==} 1622 + engines: {node: '>=12'} 1623 + 1624 + d3-time-format@4.1.0: 1625 + resolution: {integrity: sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==} 1626 + engines: {node: '>=12'} 1627 + 1628 + d3-time@3.1.0: 1629 + resolution: {integrity: sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==} 1630 + engines: {node: '>=12'} 1631 + 1632 + d3-timer@3.0.1: 1633 + resolution: {integrity: sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==} 1634 + engines: {node: '>=12'} 1635 + 1636 + date-fns@3.6.0: 1637 + resolution: {integrity: sha512-fRHTG8g/Gif+kSh50gaGEdToemgfj74aRX3swtiouboip5JDLAyDE9F11nHMIcvOaXeOC6D7SpNhi7uFyB7Uww==} 1638 + 1639 + debug@4.4.1: 1640 + resolution: {integrity: sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==} 1641 + engines: {node: '>=6.0'} 1642 + peerDependencies: 1643 + supports-color: '*' 1644 + peerDependenciesMeta: 1645 + supports-color: 1646 + optional: true 1647 + 1648 + decimal.js-light@2.5.1: 1649 + resolution: {integrity: sha512-qIMFpTMZmny+MMIitAB6D7iVPEorVw6YQRWkvarTkT4tBeSLLiHzcwj6q0MmYSFCiVpiqPJTJEYIrpcPzVEIvg==} 1650 + 1651 + deep-is@0.1.4: 1652 + resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} 1653 + 1654 + detect-libc@2.0.4: 1655 + resolution: {integrity: sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA==} 1656 + engines: {node: '>=8'} 1657 + 1658 + detect-node-es@1.1.0: 1659 + resolution: {integrity: sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==} 1660 + 1661 + dom-helpers@5.2.1: 1662 + resolution: {integrity: sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==} 1663 + 1664 + embla-carousel-react@8.6.0: 1665 + resolution: {integrity: sha512-0/PjqU7geVmo6F734pmPqpyHqiM99olvyecY7zdweCw+6tKEXnrE90pBiBbMMU8s5tICemzpQ3hi5EpxzGW+JA==} 1666 + peerDependencies: 1667 + react: ^16.8.0 || ^17.0.1 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc 1668 + 1669 + embla-carousel-reactive-utils@8.6.0: 1670 + resolution: {integrity: sha512-fMVUDUEx0/uIEDM0Mz3dHznDhfX+znCCDCeIophYb1QGVM7YThSWX+wz11zlYwWFOr74b4QLGg0hrGPJeG2s4A==} 1671 + peerDependencies: 1672 + embla-carousel: 8.6.0 1673 + 1674 + embla-carousel@8.6.0: 1675 + resolution: {integrity: sha512-SjWyZBHJPbqxHOzckOfo8lHisEaJWmwd23XppYFYVh10bU66/Pn5tkVkbkCMZVdbUE5eTCI2nD8OyIP4Z+uwkA==} 1676 + 1677 + enhanced-resolve@5.18.1: 1678 + resolution: {integrity: sha512-ZSW3ma5GkcQBIpwZTSRAI8N71Uuwgs93IezB7mf7R60tC8ZbJideoDNKjHn2O9KIlx6rkGTTEk1xUCK2E1Y2Yg==} 1679 + engines: {node: '>=10.13.0'} 1680 + 1681 + esbuild@0.21.5: 1682 + resolution: {integrity: sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==} 1683 + engines: {node: '>=12'} 1684 + hasBin: true 1685 + 1686 + escape-string-regexp@4.0.0: 1687 + resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} 1688 + engines: {node: '>=10'} 1689 + 1690 + eslint-plugin-react-hooks@5.2.0: 1691 + resolution: {integrity: sha512-+f15FfK64YQwZdJNELETdn5ibXEUQmW1DZL6KXhNnc2heoy/sg9VJJeT7n8TlMWouzWqSWavFkIhHyIbIAEapg==} 1692 + engines: {node: '>=10'} 1693 + peerDependencies: 1694 + eslint: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0 1695 + 1696 + eslint-plugin-react-refresh@0.4.20: 1697 + resolution: {integrity: sha512-XpbHQ2q5gUF8BGOX4dHe+71qoirYMhApEPZ7sfhF/dNnOF1UXnCMGZf79SFTBO7Bz5YEIT4TMieSlJBWhP9WBA==} 1698 + peerDependencies: 1699 + eslint: '>=8.40' 1700 + 1701 + eslint-scope@8.4.0: 1702 + resolution: {integrity: sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==} 1703 + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} 1704 + 1705 + eslint-visitor-keys@3.4.3: 1706 + resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==} 1707 + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} 1708 + 1709 + eslint-visitor-keys@4.2.1: 1710 + resolution: {integrity: sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==} 1711 + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} 1712 + 1713 + eslint@9.29.0: 1714 + resolution: {integrity: sha512-GsGizj2Y1rCWDu6XoEekL3RLilp0voSePurjZIkxL3wlm5o5EC9VpgaP7lrCvjnkuLvzFBQWB3vWB3K5KQTveQ==} 1715 + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} 1716 + hasBin: true 1717 + peerDependencies: 1718 + jiti: '*' 1719 + peerDependenciesMeta: 1720 + jiti: 1721 + optional: true 1722 + 1723 + espree@10.4.0: 1724 + resolution: {integrity: sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==} 1725 + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} 1726 + 1727 + esquery@1.6.0: 1728 + resolution: {integrity: sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==} 1729 + engines: {node: '>=0.10'} 1730 + 1731 + esrecurse@4.3.0: 1732 + resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==} 1733 + engines: {node: '>=4.0'} 1734 + 1735 + estraverse@5.3.0: 1736 + resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} 1737 + engines: {node: '>=4.0'} 1738 + 1739 + esutils@2.0.3: 1740 + resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} 1741 + engines: {node: '>=0.10.0'} 1742 + 1743 + eventemitter3@4.0.7: 1744 + resolution: {integrity: sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==} 1745 + 1746 + fast-deep-equal@3.1.3: 1747 + resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} 1748 + 1749 + fast-equals@5.2.2: 1750 + resolution: {integrity: sha512-V7/RktU11J3I36Nwq2JnZEM7tNm17eBJz+u25qdxBZeCKiX6BkVSZQjwWIr+IobgnZy+ag73tTZgZi7tr0LrBw==} 1751 + engines: {node: '>=6.0.0'} 1752 + 1753 + fast-glob@3.3.3: 1754 + resolution: {integrity: sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==} 1755 + engines: {node: '>=8.6.0'} 1756 + 1757 + fast-json-stable-stringify@2.1.0: 1758 + resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} 1759 + 1760 + fast-levenshtein@2.0.6: 1761 + resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} 1762 + 1763 + fastq@1.19.1: 1764 + resolution: {integrity: sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==} 1765 + 1766 + file-entry-cache@8.0.0: 1767 + resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==} 1768 + engines: {node: '>=16.0.0'} 1769 + 1770 + fill-range@7.1.1: 1771 + resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} 1772 + engines: {node: '>=8'} 1773 + 1774 + find-up@5.0.0: 1775 + resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} 1776 + engines: {node: '>=10'} 1777 + 1778 + flat-cache@4.0.1: 1779 + resolution: {integrity: sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==} 1780 + engines: {node: '>=16'} 1781 + 1782 + flatted@3.3.3: 1783 + resolution: {integrity: sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==} 1784 + 1785 + fsevents@2.3.3: 1786 + resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} 1787 + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} 1788 + os: [darwin] 1789 + 1790 + get-nonce@1.0.1: 1791 + resolution: {integrity: sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q==} 1792 + engines: {node: '>=6'} 1793 + 1794 + glob-parent@5.1.2: 1795 + resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} 1796 + engines: {node: '>= 6'} 1797 + 1798 + glob-parent@6.0.2: 1799 + resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} 1800 + engines: {node: '>=10.13.0'} 1801 + 1802 + globals@14.0.0: 1803 + resolution: {integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==} 1804 + engines: {node: '>=18'} 1805 + 1806 + globals@15.15.0: 1807 + resolution: {integrity: sha512-7ACyT3wmyp3I61S4fG682L0VA2RGD9otkqGJIwNUMF1SWUombIIk+af1unuDYgMm082aHYwD+mzJvv9Iu8dsgg==} 1808 + engines: {node: '>=18'} 1809 + 1810 + graceful-fs@4.2.11: 1811 + resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} 1812 + 1813 + graphemer@1.4.0: 1814 + resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==} 1815 + 1816 + has-flag@4.0.0: 1817 + resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} 1818 + engines: {node: '>=8'} 1819 + 1820 + html2canvas@1.4.1: 1821 + resolution: {integrity: sha512-fPU6BHNpsyIhr8yyMpTLLxAbkaK8ArIBcmZIRiBLiDhjeqvXolaEmDGmELFuX9I4xDcaKKcJl+TKZLqruBbmWA==} 1822 + engines: {node: '>=8.0.0'} 1823 + 1824 + idb-keyval@6.2.2: 1825 + resolution: {integrity: sha512-yjD9nARJ/jb1g+CvD0tlhUHOrJ9Sy0P8T9MF3YaLlHnSRpwPfpTX0XIvpmw3gAJUmEu3FiICLBDPXVwyEvrleg==} 1826 + 1827 + ignore@5.3.2: 1828 + resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} 1829 + engines: {node: '>= 4'} 1830 + 1831 + ignore@7.0.5: 1832 + resolution: {integrity: sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==} 1833 + engines: {node: '>= 4'} 1834 + 1835 + import-fresh@3.3.1: 1836 + resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==} 1837 + engines: {node: '>=6'} 1838 + 1839 + imurmurhash@0.1.4: 1840 + resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} 1841 + engines: {node: '>=0.8.19'} 1842 + 1843 + input-otp@1.4.2: 1844 + resolution: {integrity: sha512-l3jWwYNvrEa6NTCt7BECfCm48GvwuZzkoeG3gBL2w4CHeOXW3eKFmf9UNYkNfYc3mxMrthMnxjIE07MT0zLBQA==} 1845 + peerDependencies: 1846 + react: ^16.8 || ^17.0 || ^18.0 || ^19.0.0 || ^19.0.0-rc 1847 + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0.0 || ^19.0.0-rc 1848 + 1849 + internmap@2.0.3: 1850 + resolution: {integrity: sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==} 1851 + engines: {node: '>=12'} 1852 + 1853 + is-extglob@2.1.1: 1854 + resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} 1855 + engines: {node: '>=0.10.0'} 1856 + 1857 + is-glob@4.0.3: 1858 + resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} 1859 + engines: {node: '>=0.10.0'} 1860 + 1861 + is-number@7.0.0: 1862 + resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} 1863 + engines: {node: '>=0.12.0'} 1864 + 1865 + isexe@2.0.0: 1866 + resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} 1867 + 1868 + jiti@2.4.2: 1869 + resolution: {integrity: sha512-rg9zJN+G4n2nfJl5MW3BMygZX56zKPNVEYYqq7adpmMh4Jn2QNEwhvQlFy6jPVdcod7txZtKHWnyZiA3a0zP7A==} 1870 + hasBin: true 1871 + 1872 + js-tokens@4.0.0: 1873 + resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} 1874 + 1875 + js-yaml@4.1.0: 1876 + resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} 1877 + hasBin: true 1878 + 1879 + json-buffer@3.0.1: 1880 + resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} 1881 + 1882 + json-schema-traverse@0.4.1: 1883 + resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} 1884 + 1885 + json-stable-stringify-without-jsonify@1.0.1: 1886 + resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} 1887 + 1888 + keyv@4.5.4: 1889 + resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} 1890 + 1891 + levn@0.4.1: 1892 + resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} 1893 + engines: {node: '>= 0.8.0'} 1894 + 1895 + lightningcss-darwin-arm64@1.30.1: 1896 + resolution: {integrity: sha512-c8JK7hyE65X1MHMN+Viq9n11RRC7hgin3HhYKhrMyaXflk5GVplZ60IxyoVtzILeKr+xAJwg6zK6sjTBJ0FKYQ==} 1897 + engines: {node: '>= 12.0.0'} 1898 + cpu: [arm64] 1899 + os: [darwin] 1900 + 1901 + lightningcss-darwin-x64@1.30.1: 1902 + resolution: {integrity: sha512-k1EvjakfumAQoTfcXUcHQZhSpLlkAuEkdMBsI/ivWw9hL+7FtilQc0Cy3hrx0AAQrVtQAbMI7YjCgYgvn37PzA==} 1903 + engines: {node: '>= 12.0.0'} 1904 + cpu: [x64] 1905 + os: [darwin] 1906 + 1907 + lightningcss-freebsd-x64@1.30.1: 1908 + resolution: {integrity: sha512-kmW6UGCGg2PcyUE59K5r0kWfKPAVy4SltVeut+umLCFoJ53RdCUWxcRDzO1eTaxf/7Q2H7LTquFHPL5R+Gjyig==} 1909 + engines: {node: '>= 12.0.0'} 1910 + cpu: [x64] 1911 + os: [freebsd] 1912 + 1913 + lightningcss-linux-arm-gnueabihf@1.30.1: 1914 + resolution: {integrity: sha512-MjxUShl1v8pit+6D/zSPq9S9dQ2NPFSQwGvxBCYaBYLPlCWuPh9/t1MRS8iUaR8i+a6w7aps+B4N0S1TYP/R+Q==} 1915 + engines: {node: '>= 12.0.0'} 1916 + cpu: [arm] 1917 + os: [linux] 1918 + 1919 + lightningcss-linux-arm64-gnu@1.30.1: 1920 + resolution: {integrity: sha512-gB72maP8rmrKsnKYy8XUuXi/4OctJiuQjcuqWNlJQ6jZiWqtPvqFziskH3hnajfvKB27ynbVCucKSm2rkQp4Bw==} 1921 + engines: {node: '>= 12.0.0'} 1922 + cpu: [arm64] 1923 + os: [linux] 1924 + 1925 + lightningcss-linux-arm64-musl@1.30.1: 1926 + resolution: {integrity: sha512-jmUQVx4331m6LIX+0wUhBbmMX7TCfjF5FoOH6SD1CttzuYlGNVpA7QnrmLxrsub43ClTINfGSYyHe2HWeLl5CQ==} 1927 + engines: {node: '>= 12.0.0'} 1928 + cpu: [arm64] 1929 + os: [linux] 1930 + 1931 + lightningcss-linux-x64-gnu@1.30.1: 1932 + resolution: {integrity: sha512-piWx3z4wN8J8z3+O5kO74+yr6ze/dKmPnI7vLqfSqI8bccaTGY5xiSGVIJBDd5K5BHlvVLpUB3S2YCfelyJ1bw==} 1933 + engines: {node: '>= 12.0.0'} 1934 + cpu: [x64] 1935 + os: [linux] 1936 + 1937 + lightningcss-linux-x64-musl@1.30.1: 1938 + resolution: {integrity: sha512-rRomAK7eIkL+tHY0YPxbc5Dra2gXlI63HL+v1Pdi1a3sC+tJTcFrHX+E86sulgAXeI7rSzDYhPSeHHjqFhqfeQ==} 1939 + engines: {node: '>= 12.0.0'} 1940 + cpu: [x64] 1941 + os: [linux] 1942 + 1943 + lightningcss-win32-arm64-msvc@1.30.1: 1944 + resolution: {integrity: sha512-mSL4rqPi4iXq5YVqzSsJgMVFENoa4nGTT/GjO2c0Yl9OuQfPsIfncvLrEW6RbbB24WtZ3xP/2CCmI3tNkNV4oA==} 1945 + engines: {node: '>= 12.0.0'} 1946 + cpu: [arm64] 1947 + os: [win32] 1948 + 1949 + lightningcss-win32-x64-msvc@1.30.1: 1950 + resolution: {integrity: sha512-PVqXh48wh4T53F/1CCu8PIPCxLzWyCnn/9T5W1Jpmdy5h9Cwd+0YQS6/LwhHXSafuc61/xg9Lv5OrCby6a++jg==} 1951 + engines: {node: '>= 12.0.0'} 1952 + cpu: [x64] 1953 + os: [win32] 1954 + 1955 + lightningcss@1.30.1: 1956 + resolution: {integrity: sha512-xi6IyHML+c9+Q3W0S4fCQJOym42pyurFiJUHEcEyHS0CeKzia4yZDEsLlqOFykxOdHpNy0NmvVO31vcSqAxJCg==} 1957 + engines: {node: '>= 12.0.0'} 1958 + 1959 + locate-path@6.0.0: 1960 + resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} 1961 + engines: {node: '>=10'} 1962 + 1963 + lodash.castarray@4.4.0: 1964 + resolution: {integrity: sha512-aVx8ztPv7/2ULbArGJ2Y42bG1mEQ5mGjpdvrbJcJFU3TbYybe+QlLS4pst9zV52ymy2in1KpFPiZnAOATxD4+Q==} 1965 + 1966 + lodash.isplainobject@4.0.6: 1967 + resolution: {integrity: sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==} 1968 + 1969 + lodash.merge@4.6.2: 1970 + resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} 1971 + 1972 + lodash@4.17.21: 1973 + resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} 1974 + 1975 + loose-envify@1.4.0: 1976 + resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} 1977 + hasBin: true 1978 + 1979 + lucide-react@0.462.0: 1980 + resolution: {integrity: sha512-NTL7EbAao9IFtuSivSZgrAh4fZd09Lr+6MTkqIxuHaH2nnYiYIzXPo06cOxHg9wKLdj6LL8TByG4qpePqwgx/g==} 1981 + peerDependencies: 1982 + react: ^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0-rc 1983 + 1984 + magic-string@0.30.17: 1985 + resolution: {integrity: sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==} 1986 + 1987 + merge2@1.4.1: 1988 + resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} 1989 + engines: {node: '>= 8'} 1990 + 1991 + micromatch@4.0.8: 1992 + resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==} 1993 + engines: {node: '>=8.6'} 1994 + 1995 + minimatch@3.1.2: 1996 + resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} 1997 + 1998 + minimatch@9.0.5: 1999 + resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==} 2000 + engines: {node: '>=16 || 14 >=14.17'} 2001 + 2002 + minipass@7.1.2: 2003 + resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==} 2004 + engines: {node: '>=16 || 14 >=14.17'} 2005 + 2006 + minizlib@3.0.2: 2007 + resolution: {integrity: sha512-oG62iEk+CYt5Xj2YqI5Xi9xWUeZhDI8jjQmC5oThVH5JGCTgIjr7ciJDzC7MBzYd//WvR1OTmP5Q38Q8ShQtVA==} 2008 + engines: {node: '>= 18'} 2009 + 2010 + mkdirp@3.0.1: 2011 + resolution: {integrity: sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==} 2012 + engines: {node: '>=10'} 2013 + hasBin: true 2014 + 2015 + ms@2.1.3: 2016 + resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} 2017 + 2018 + nanoid@3.3.11: 2019 + resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==} 2020 + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} 2021 + hasBin: true 2022 + 2023 + natural-compare@1.4.0: 2024 + resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} 2025 + 2026 + next-themes@0.3.0: 2027 + resolution: {integrity: sha512-/QHIrsYpd6Kfk7xakK4svpDI5mmXP0gfvCoJdGpZQ2TOrQZmsW0QxjaiLn8wbIKjtm4BTSqLoix4lxYYOnLJ/w==} 2028 + peerDependencies: 2029 + react: ^16.8 || ^17 || ^18 2030 + react-dom: ^16.8 || ^17 || ^18 2031 + 2032 + object-assign@4.1.1: 2033 + resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} 2034 + engines: {node: '>=0.10.0'} 2035 + 2036 + optionator@0.9.4: 2037 + resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} 2038 + engines: {node: '>= 0.8.0'} 2039 + 2040 + p-limit@3.1.0: 2041 + resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} 2042 + engines: {node: '>=10'} 2043 + 2044 + p-locate@5.0.0: 2045 + resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} 2046 + engines: {node: '>=10'} 2047 + 2048 + parent-module@1.0.1: 2049 + resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} 2050 + engines: {node: '>=6'} 2051 + 2052 + path-exists@4.0.0: 2053 + resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} 2054 + engines: {node: '>=8'} 2055 + 2056 + path-key@3.1.1: 2057 + resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} 2058 + engines: {node: '>=8'} 2059 + 2060 + picocolors@1.1.1: 2061 + resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} 2062 + 2063 + picomatch@2.3.1: 2064 + resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} 2065 + engines: {node: '>=8.6'} 2066 + 2067 + postcss-selector-parser@6.0.10: 2068 + resolution: {integrity: sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w==} 2069 + engines: {node: '>=4'} 2070 + 2071 + postcss@8.5.6: 2072 + resolution: {integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==} 2073 + engines: {node: ^10 || ^12 || >=14} 2074 + 2075 + prelude-ls@1.2.1: 2076 + resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} 2077 + engines: {node: '>= 0.8.0'} 2078 + 2079 + prop-types@15.8.1: 2080 + resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==} 2081 + 2082 + punycode@2.3.1: 2083 + resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} 2084 + engines: {node: '>=6'} 2085 + 2086 + queue-microtask@1.2.3: 2087 + resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} 2088 + 2089 + react-day-picker@8.10.1: 2090 + resolution: {integrity: sha512-TMx7fNbhLk15eqcMt+7Z7S2KF7mfTId/XJDjKE8f+IUcFn0l08/kI4FiYTL/0yuOLmEcbR4Fwe3GJf/NiiMnPA==} 2091 + peerDependencies: 2092 + date-fns: ^2.28.0 || ^3.0.0 2093 + react: ^16.8.0 || ^17.0.0 || ^18.0.0 2094 + 2095 + react-dom@18.3.1: 2096 + resolution: {integrity: sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==} 2097 + peerDependencies: 2098 + react: ^18.3.1 2099 + 2100 + react-hook-form@7.58.1: 2101 + resolution: {integrity: sha512-Lml/KZYEEFfPhUVgE0RdCVpnC4yhW+PndRhbiTtdvSlQTL8IfVR+iQkBjLIvmmc6+GGoVeM11z37ktKFPAb0FA==} 2102 + engines: {node: '>=18.0.0'} 2103 + peerDependencies: 2104 + react: ^16.8.0 || ^17 || ^18 || ^19 2105 + 2106 + react-is@16.13.1: 2107 + resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==} 2108 + 2109 + react-is@18.3.1: 2110 + resolution: {integrity: sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==} 2111 + 2112 + react-remove-scroll-bar@2.3.8: 2113 + resolution: {integrity: sha512-9r+yi9+mgU33AKcj6IbT9oRCO78WriSj6t/cF8DWBZJ9aOGPOTEDvdUDz1FwKim7QXWwmHqtdHnRJfhAxEG46Q==} 2114 + engines: {node: '>=10'} 2115 + peerDependencies: 2116 + '@types/react': '*' 2117 + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 2118 + peerDependenciesMeta: 2119 + '@types/react': 2120 + optional: true 2121 + 2122 + react-remove-scroll@2.7.1: 2123 + resolution: {integrity: sha512-HpMh8+oahmIdOuS5aFKKY6Pyog+FNaZV/XyJOq7b4YFwsFHe5yYfdbIalI4k3vU2nSDql7YskmUseHsRrJqIPA==} 2124 + engines: {node: '>=10'} 2125 + peerDependencies: 2126 + '@types/react': '*' 2127 + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc 2128 + peerDependenciesMeta: 2129 + '@types/react': 2130 + optional: true 2131 + 2132 + react-resizable-panels@2.1.9: 2133 + resolution: {integrity: sha512-z77+X08YDIrgAes4jl8xhnUu1LNIRp4+E7cv4xHmLOxxUPO/ML7PSrE813b90vj7xvQ1lcf7g2uA9GeMZonjhQ==} 2134 + peerDependencies: 2135 + react: ^16.14.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc 2136 + react-dom: ^16.14.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc 2137 + 2138 + react-router-dom@6.30.1: 2139 + resolution: {integrity: sha512-llKsgOkZdbPU1Eg3zK8lCn+sjD9wMRZZPuzmdWWX5SUs8OFkN5HnFVC0u5KMeMaC9aoancFI/KoLuKPqN+hxHw==} 2140 + engines: {node: '>=14.0.0'} 2141 + peerDependencies: 2142 + react: '>=16.8' 2143 + react-dom: '>=16.8' 2144 + 2145 + react-router@6.30.1: 2146 + resolution: {integrity: sha512-X1m21aEmxGXqENEPG3T6u0Th7g0aS4ZmoNynhbs+Cn+q+QGTLt+d5IQ2bHAXKzKcxGJjxACpVbnYQSCRcfxHlQ==} 2147 + engines: {node: '>=14.0.0'} 2148 + peerDependencies: 2149 + react: '>=16.8' 2150 + 2151 + react-smooth@4.0.4: 2152 + resolution: {integrity: sha512-gnGKTpYwqL0Iii09gHobNolvX4Kiq4PKx6eWBCYYix+8cdw+cGo3do906l1NBPKkSWx1DghC1dlWG9L2uGd61Q==} 2153 + peerDependencies: 2154 + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 2155 + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 2156 + 2157 + react-style-singleton@2.2.3: 2158 + resolution: {integrity: sha512-b6jSvxvVnyptAiLjbkWLE/lOnR4lfTtDAl+eUC7RZy+QQWc6wRzIV2CE6xBuMmDxc2qIihtDCZD5NPOFl7fRBQ==} 2159 + engines: {node: '>=10'} 2160 + peerDependencies: 2161 + '@types/react': '*' 2162 + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc 2163 + peerDependenciesMeta: 2164 + '@types/react': 2165 + optional: true 2166 + 2167 + react-transition-group@4.4.5: 2168 + resolution: {integrity: sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==} 2169 + peerDependencies: 2170 + react: '>=16.6.0' 2171 + react-dom: '>=16.6.0' 2172 + 2173 + react@18.3.1: 2174 + resolution: {integrity: sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==} 2175 + engines: {node: '>=0.10.0'} 2176 + 2177 + recharts-scale@0.4.5: 2178 + resolution: {integrity: sha512-kivNFO+0OcUNu7jQquLXAxz1FIwZj8nrj+YkOKc5694NbjCvcT6aSZiIzNzd2Kul4o4rTto8QVR9lMNtxD4G1w==} 2179 + 2180 + recharts@2.15.3: 2181 + resolution: {integrity: sha512-EdOPzTwcFSuqtvkDoaM5ws/Km1+WTAO2eizL7rqiG0V2UVhTnz0m7J2i0CjVPUCdEkZImaWvXLbZDS2H5t6GFQ==} 2182 + engines: {node: '>=14'} 2183 + peerDependencies: 2184 + react: ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 2185 + react-dom: ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 2186 + 2187 + resolve-from@4.0.0: 2188 + resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} 2189 + engines: {node: '>=4'} 2190 + 2191 + reusify@1.1.0: 2192 + resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==} 2193 + engines: {iojs: '>=1.0.0', node: '>=0.10.0'} 2194 + 2195 + rollup@4.44.0: 2196 + resolution: {integrity: sha512-qHcdEzLCiktQIfwBq420pn2dP+30uzqYxv9ETm91wdt2R9AFcWfjNAmje4NWlnCIQ5RMTzVf0ZyisOKqHR6RwA==} 2197 + engines: {node: '>=18.0.0', npm: '>=8.0.0'} 2198 + hasBin: true 2199 + 2200 + run-parallel@1.2.0: 2201 + resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} 2202 + 2203 + scheduler@0.23.2: 2204 + resolution: {integrity: sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==} 2205 + 2206 + semver@7.7.2: 2207 + resolution: {integrity: sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==} 2208 + engines: {node: '>=10'} 2209 + hasBin: true 2210 + 2211 + shebang-command@2.0.0: 2212 + resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} 2213 + engines: {node: '>=8'} 2214 + 2215 + shebang-regex@3.0.0: 2216 + resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} 2217 + engines: {node: '>=8'} 2218 + 2219 + sonner@1.7.4: 2220 + resolution: {integrity: sha512-DIS8z4PfJRbIyfVFDVnK9rO3eYDtse4Omcm6bt0oEr5/jtLgysmjuBl1frJ9E/EQZrFmKx2A8m/s5s9CRXIzhw==} 2221 + peerDependencies: 2222 + react: ^18.0.0 || ^19.0.0 || ^19.0.0-rc 2223 + react-dom: ^18.0.0 || ^19.0.0 || ^19.0.0-rc 2224 + 2225 + source-map-js@1.2.1: 2226 + resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} 2227 + engines: {node: '>=0.10.0'} 2228 + 2229 + strip-json-comments@3.1.1: 2230 + resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} 2231 + engines: {node: '>=8'} 2232 + 2233 + supports-color@7.2.0: 2234 + resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} 2235 + engines: {node: '>=8'} 2236 + 2237 + tailwind-merge@2.6.0: 2238 + resolution: {integrity: sha512-P+Vu1qXfzediirmHOC3xKGAYeZtPcV9g76X+xg2FD4tYgR71ewMA35Y3sCz3zhiN/dwefRpJX0yBcgwi1fXNQA==} 2239 + 2240 + tailwindcss-animate@1.0.7: 2241 + resolution: {integrity: sha512-bl6mpH3T7I3UFxuvDEXLxy/VuFxBk5bbzplh7tXI68mwMokNYd1t9qPBHlnyTwfa4JGC4zP516I1hYYtQ/vspA==} 2242 + peerDependencies: 2243 + tailwindcss: '>=3.0.0 || insiders' 2244 + 2245 + tailwindcss@4.1.10: 2246 + resolution: {integrity: sha512-P3nr6WkvKV/ONsTzj6Gb57sWPMX29EPNPopo7+FcpkQaNsrNpZ1pv8QmrYI2RqEKD7mlGqLnGovlcYnBK0IqUA==} 2247 + 2248 + tapable@2.2.2: 2249 + resolution: {integrity: sha512-Re10+NauLTMCudc7T5WLFLAwDhQ0JWdrMK+9B2M8zR5hRExKmsRDCBA7/aV/pNJFltmBFO5BAMlQFi/vq3nKOg==} 2250 + engines: {node: '>=6'} 2251 + 2252 + tar@7.4.3: 2253 + resolution: {integrity: sha512-5S7Va8hKfV7W5U6g3aYxXmlPoZVAwUMy9AOKyF2fVuZa2UD3qZjg578OrLRt8PcNN1PleVaL/5/yYATNL0ICUw==} 2254 + engines: {node: '>=18'} 2255 + 2256 + text-segmentation@1.0.3: 2257 + resolution: {integrity: sha512-iOiPUo/BGnZ6+54OsWxZidGCsdU8YbE4PSpdPinp7DeMtUJNJBoJ/ouUSTJjHkh1KntHaltHl/gDs2FC4i5+Nw==} 2258 + 2259 + tiny-invariant@1.3.3: 2260 + resolution: {integrity: sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==} 2261 + 2262 + to-regex-range@5.0.1: 2263 + resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} 2264 + engines: {node: '>=8.0'} 2265 + 2266 + ts-api-utils@2.1.0: 2267 + resolution: {integrity: sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==} 2268 + engines: {node: '>=18.12'} 2269 + peerDependencies: 2270 + typescript: '>=4.8.4' 2271 + 2272 + tslib@2.8.1: 2273 + resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} 2274 + 2275 + type-check@0.4.0: 2276 + resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} 2277 + engines: {node: '>= 0.8.0'} 2278 + 2279 + typescript-eslint@8.34.1: 2280 + resolution: {integrity: sha512-XjS+b6Vg9oT1BaIUfkW3M3LvqZE++rbzAMEHuccCfO/YkP43ha6w3jTEMilQxMF92nVOYCcdjv1ZUhAa1D/0ow==} 2281 + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} 2282 + peerDependencies: 2283 + eslint: ^8.57.0 || ^9.0.0 2284 + typescript: '>=4.8.4 <5.9.0' 2285 + 2286 + typescript@5.8.3: 2287 + resolution: {integrity: sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==} 2288 + engines: {node: '>=14.17'} 2289 + hasBin: true 2290 + 2291 + undici-types@6.21.0: 2292 + resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==} 2293 + 2294 + uri-js@4.4.1: 2295 + resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} 2296 + 2297 + use-callback-ref@1.3.3: 2298 + resolution: {integrity: sha512-jQL3lRnocaFtu3V00JToYz/4QkNWswxijDaCVNZRiRTO3HQDLsdu1ZtmIUvV4yPp+rvWm5j0y0TG/S61cuijTg==} 2299 + engines: {node: '>=10'} 2300 + peerDependencies: 2301 + '@types/react': '*' 2302 + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc 2303 + peerDependenciesMeta: 2304 + '@types/react': 2305 + optional: true 2306 + 2307 + use-sidecar@1.1.3: 2308 + resolution: {integrity: sha512-Fedw0aZvkhynoPYlA5WXrMCAMm+nSWdZt6lzJQ7Ok8S6Q+VsHmHpRWndVRJ8Be0ZbkfPc5LRYH+5XrzXcEeLRQ==} 2309 + engines: {node: '>=10'} 2310 + peerDependencies: 2311 + '@types/react': '*' 2312 + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc 2313 + peerDependenciesMeta: 2314 + '@types/react': 2315 + optional: true 2316 + 2317 + use-sync-external-store@1.5.0: 2318 + resolution: {integrity: sha512-Rb46I4cGGVBmjamjphe8L/UnvJD+uPPtTkNvX5mZgqdbavhI4EbgIWJiIHXJ8bc/i9EQGPRh4DwEURJ552Do0A==} 2319 + peerDependencies: 2320 + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 2321 + 2322 + util-deprecate@1.0.2: 2323 + resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} 2324 + 2325 + utrie@1.0.2: 2326 + resolution: {integrity: sha512-1MLa5ouZiOmQzUbjbu9VmjLzn1QLXBhwpUa7kdLUQK+KQ5KA9I1vk5U4YHe/X2Ch7PYnJfWuWT+VbuxbGwljhw==} 2327 + 2328 + vaul@0.9.9: 2329 + resolution: {integrity: sha512-7afKg48srluhZwIkaU+lgGtFCUsYBSGOl8vcc8N/M3YQlZFlynHD15AE+pwrYdc826o7nrIND4lL9Y6b9WWZZQ==} 2330 + peerDependencies: 2331 + react: ^16.8 || ^17.0 || ^18.0 2332 + react-dom: ^16.8 || ^17.0 || ^18.0 2333 + 2334 + victory-vendor@36.9.2: 2335 + resolution: {integrity: sha512-PnpQQMuxlwYdocC8fIJqVXvkeViHYzotI+NJrCuav0ZYFoq912ZHBk3mCeuj+5/VpodOjPe1z0Fk2ihgzlXqjQ==} 2336 + 2337 + vite@5.4.19: 2338 + resolution: {integrity: sha512-qO3aKv3HoQC8QKiNSTuUM1l9o/XX3+c+VTgLHbJWHZGeTPVAg2XwazI9UWzoxjIJCGCV2zU60uqMzjeLZuULqA==} 2339 + engines: {node: ^18.0.0 || >=20.0.0} 2340 + hasBin: true 2341 + peerDependencies: 2342 + '@types/node': ^18.0.0 || >=20.0.0 2343 + less: '*' 2344 + lightningcss: ^1.21.0 2345 + sass: '*' 2346 + sass-embedded: '*' 2347 + stylus: '*' 2348 + sugarss: '*' 2349 + terser: ^5.4.0 2350 + peerDependenciesMeta: 2351 + '@types/node': 2352 + optional: true 2353 + less: 2354 + optional: true 2355 + lightningcss: 2356 + optional: true 2357 + sass: 2358 + optional: true 2359 + sass-embedded: 2360 + optional: true 2361 + stylus: 2362 + optional: true 2363 + sugarss: 2364 + optional: true 2365 + terser: 2366 + optional: true 2367 + 2368 + which@2.0.2: 2369 + resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} 2370 + engines: {node: '>= 8'} 2371 + hasBin: true 2372 + 2373 + word-wrap@1.2.5: 2374 + resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} 2375 + engines: {node: '>=0.10.0'} 2376 + 2377 + yallist@5.0.0: 2378 + resolution: {integrity: sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==} 2379 + engines: {node: '>=18'} 2380 + 2381 + yocto-queue@0.1.0: 2382 + resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} 2383 + engines: {node: '>=10'} 2384 + 2385 + zod@3.25.67: 2386 + resolution: {integrity: sha512-idA2YXwpCdqUSKRCACDE6ItZD9TZzy3OZMtpfLoh6oPR47lipysRrJfjzMqFxQ3uJuUPyUeWe1r9vLH33xO/Qw==} 2387 + 2388 + zustand@5.0.5: 2389 + resolution: {integrity: sha512-mILtRfKW9xM47hqxGIxCv12gXusoY/xTSHBYApXozR0HmQv299whhBeeAcRy+KrPPybzosvJBCOmVjq6x12fCg==} 2390 + engines: {node: '>=12.20.0'} 2391 + peerDependencies: 2392 + '@types/react': '>=18.0.0' 2393 + immer: '>=9.0.6' 2394 + react: '>=18.0.0' 2395 + use-sync-external-store: '>=1.2.0' 2396 + peerDependenciesMeta: 2397 + '@types/react': 2398 + optional: true 2399 + immer: 2400 + optional: true 2401 + react: 2402 + optional: true 2403 + use-sync-external-store: 2404 + optional: true 2405 + 2406 + snapshots: 2407 + 2408 + '@alloc/quick-lru@5.2.0': {} 2409 + 2410 + '@ampproject/remapping@2.3.0': 2411 + dependencies: 2412 + '@jridgewell/gen-mapping': 0.3.8 2413 + '@jridgewell/trace-mapping': 0.3.25 2414 + 2415 + '@babel/runtime@7.27.6': {} 2416 + 2417 + '@esbuild/aix-ppc64@0.21.5': 2418 + optional: true 2419 + 2420 + '@esbuild/android-arm64@0.21.5': 2421 + optional: true 2422 + 2423 + '@esbuild/android-arm@0.21.5': 2424 + optional: true 2425 + 2426 + '@esbuild/android-x64@0.21.5': 2427 + optional: true 2428 + 2429 + '@esbuild/darwin-arm64@0.21.5': 2430 + optional: true 2431 + 2432 + '@esbuild/darwin-x64@0.21.5': 2433 + optional: true 2434 + 2435 + '@esbuild/freebsd-arm64@0.21.5': 2436 + optional: true 2437 + 2438 + '@esbuild/freebsd-x64@0.21.5': 2439 + optional: true 2440 + 2441 + '@esbuild/linux-arm64@0.21.5': 2442 + optional: true 2443 + 2444 + '@esbuild/linux-arm@0.21.5': 2445 + optional: true 2446 + 2447 + '@esbuild/linux-ia32@0.21.5': 2448 + optional: true 2449 + 2450 + '@esbuild/linux-loong64@0.21.5': 2451 + optional: true 2452 + 2453 + '@esbuild/linux-mips64el@0.21.5': 2454 + optional: true 2455 + 2456 + '@esbuild/linux-ppc64@0.21.5': 2457 + optional: true 2458 + 2459 + '@esbuild/linux-riscv64@0.21.5': 2460 + optional: true 2461 + 2462 + '@esbuild/linux-s390x@0.21.5': 2463 + optional: true 2464 + 2465 + '@esbuild/linux-x64@0.21.5': 2466 + optional: true 2467 + 2468 + '@esbuild/netbsd-x64@0.21.5': 2469 + optional: true 2470 + 2471 + '@esbuild/openbsd-x64@0.21.5': 2472 + optional: true 2473 + 2474 + '@esbuild/sunos-x64@0.21.5': 2475 + optional: true 2476 + 2477 + '@esbuild/win32-arm64@0.21.5': 2478 + optional: true 2479 + 2480 + '@esbuild/win32-ia32@0.21.5': 2481 + optional: true 2482 + 2483 + '@esbuild/win32-x64@0.21.5': 2484 + optional: true 2485 + 2486 + '@eslint-community/eslint-utils@4.7.0(eslint@9.29.0(jiti@2.4.2))': 2487 + dependencies: 2488 + eslint: 9.29.0(jiti@2.4.2) 2489 + eslint-visitor-keys: 3.4.3 2490 + 2491 + '@eslint-community/regexpp@4.12.1': {} 2492 + 2493 + '@eslint/config-array@0.20.1': 2494 + dependencies: 2495 + '@eslint/object-schema': 2.1.6 2496 + debug: 4.4.1 2497 + minimatch: 3.1.2 2498 + transitivePeerDependencies: 2499 + - supports-color 2500 + 2501 + '@eslint/config-helpers@0.2.3': {} 2502 + 2503 + '@eslint/core@0.14.0': 2504 + dependencies: 2505 + '@types/json-schema': 7.0.15 2506 + 2507 + '@eslint/core@0.15.0': 2508 + dependencies: 2509 + '@types/json-schema': 7.0.15 2510 + 2511 + '@eslint/eslintrc@3.3.1': 2512 + dependencies: 2513 + ajv: 6.12.6 2514 + debug: 4.4.1 2515 + espree: 10.4.0 2516 + globals: 14.0.0 2517 + ignore: 5.3.2 2518 + import-fresh: 3.3.1 2519 + js-yaml: 4.1.0 2520 + minimatch: 3.1.2 2521 + strip-json-comments: 3.1.1 2522 + transitivePeerDependencies: 2523 + - supports-color 2524 + 2525 + '@eslint/js@9.29.0': {} 2526 + 2527 + '@eslint/object-schema@2.1.6': {} 2528 + 2529 + '@eslint/plugin-kit@0.3.2': 2530 + dependencies: 2531 + '@eslint/core': 0.15.0 2532 + levn: 0.4.1 2533 + 2534 + '@floating-ui/core@1.7.1': 2535 + dependencies: 2536 + '@floating-ui/utils': 0.2.9 2537 + 2538 + '@floating-ui/dom@1.7.1': 2539 + dependencies: 2540 + '@floating-ui/core': 1.7.1 2541 + '@floating-ui/utils': 0.2.9 2542 + 2543 + '@floating-ui/react-dom@2.1.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': 2544 + dependencies: 2545 + '@floating-ui/dom': 1.7.1 2546 + react: 18.3.1 2547 + react-dom: 18.3.1(react@18.3.1) 2548 + 2549 + '@floating-ui/utils@0.2.9': {} 2550 + 2551 + '@hookform/resolvers@3.10.0(react-hook-form@7.58.1(react@18.3.1))': 2552 + dependencies: 2553 + react-hook-form: 7.58.1(react@18.3.1) 2554 + 2555 + '@humanfs/core@0.19.1': {} 2556 + 2557 + '@humanfs/node@0.16.6': 2558 + dependencies: 2559 + '@humanfs/core': 0.19.1 2560 + '@humanwhocodes/retry': 0.3.1 2561 + 2562 + '@humanwhocodes/module-importer@1.0.1': {} 2563 + 2564 + '@humanwhocodes/retry@0.3.1': {} 2565 + 2566 + '@humanwhocodes/retry@0.4.3': {} 2567 + 2568 + '@isaacs/fs-minipass@4.0.1': 2569 + dependencies: 2570 + minipass: 7.1.2 2571 + 2572 + '@jridgewell/gen-mapping@0.3.8': 2573 + dependencies: 2574 + '@jridgewell/set-array': 1.2.1 2575 + '@jridgewell/sourcemap-codec': 1.5.0 2576 + '@jridgewell/trace-mapping': 0.3.25 2577 + 2578 + '@jridgewell/resolve-uri@3.1.2': {} 2579 + 2580 + '@jridgewell/set-array@1.2.1': {} 2581 + 2582 + '@jridgewell/sourcemap-codec@1.5.0': {} 2583 + 2584 + '@jridgewell/trace-mapping@0.3.25': 2585 + dependencies: 2586 + '@jridgewell/resolve-uri': 3.1.2 2587 + '@jridgewell/sourcemap-codec': 1.5.0 2588 + 2589 + '@nodelib/fs.scandir@2.1.5': 2590 + dependencies: 2591 + '@nodelib/fs.stat': 2.0.5 2592 + run-parallel: 1.2.0 2593 + 2594 + '@nodelib/fs.stat@2.0.5': {} 2595 + 2596 + '@nodelib/fs.walk@1.2.8': 2597 + dependencies: 2598 + '@nodelib/fs.scandir': 2.1.5 2599 + fastq: 1.19.1 2600 + 2601 + '@radix-ui/number@1.1.1': {} 2602 + 2603 + '@radix-ui/primitive@1.1.2': {} 2604 + 2605 + '@radix-ui/react-accordion@1.2.11(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': 2606 + dependencies: 2607 + '@radix-ui/primitive': 1.1.2 2608 + '@radix-ui/react-collapsible': 1.1.11(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) 2609 + '@radix-ui/react-collection': 1.1.7(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) 2610 + '@radix-ui/react-compose-refs': 1.1.2(@types/react@18.3.23)(react@18.3.1) 2611 + '@radix-ui/react-context': 1.1.2(@types/react@18.3.23)(react@18.3.1) 2612 + '@radix-ui/react-direction': 1.1.1(@types/react@18.3.23)(react@18.3.1) 2613 + '@radix-ui/react-id': 1.1.1(@types/react@18.3.23)(react@18.3.1) 2614 + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) 2615 + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@18.3.23)(react@18.3.1) 2616 + react: 18.3.1 2617 + react-dom: 18.3.1(react@18.3.1) 2618 + optionalDependencies: 2619 + '@types/react': 18.3.23 2620 + '@types/react-dom': 18.3.7(@types/react@18.3.23) 2621 + 2622 + '@radix-ui/react-alert-dialog@1.1.14(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': 2623 + dependencies: 2624 + '@radix-ui/primitive': 1.1.2 2625 + '@radix-ui/react-compose-refs': 1.1.2(@types/react@18.3.23)(react@18.3.1) 2626 + '@radix-ui/react-context': 1.1.2(@types/react@18.3.23)(react@18.3.1) 2627 + '@radix-ui/react-dialog': 1.1.14(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) 2628 + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) 2629 + '@radix-ui/react-slot': 1.2.3(@types/react@18.3.23)(react@18.3.1) 2630 + react: 18.3.1 2631 + react-dom: 18.3.1(react@18.3.1) 2632 + optionalDependencies: 2633 + '@types/react': 18.3.23 2634 + '@types/react-dom': 18.3.7(@types/react@18.3.23) 2635 + 2636 + '@radix-ui/react-arrow@1.1.7(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': 2637 + dependencies: 2638 + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) 2639 + react: 18.3.1 2640 + react-dom: 18.3.1(react@18.3.1) 2641 + optionalDependencies: 2642 + '@types/react': 18.3.23 2643 + '@types/react-dom': 18.3.7(@types/react@18.3.23) 2644 + 2645 + '@radix-ui/react-aspect-ratio@1.1.7(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': 2646 + dependencies: 2647 + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) 2648 + react: 18.3.1 2649 + react-dom: 18.3.1(react@18.3.1) 2650 + optionalDependencies: 2651 + '@types/react': 18.3.23 2652 + '@types/react-dom': 18.3.7(@types/react@18.3.23) 2653 + 2654 + '@radix-ui/react-avatar@1.1.10(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': 2655 + dependencies: 2656 + '@radix-ui/react-context': 1.1.2(@types/react@18.3.23)(react@18.3.1) 2657 + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) 2658 + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@18.3.23)(react@18.3.1) 2659 + '@radix-ui/react-use-is-hydrated': 0.1.0(@types/react@18.3.23)(react@18.3.1) 2660 + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@18.3.23)(react@18.3.1) 2661 + react: 18.3.1 2662 + react-dom: 18.3.1(react@18.3.1) 2663 + optionalDependencies: 2664 + '@types/react': 18.3.23 2665 + '@types/react-dom': 18.3.7(@types/react@18.3.23) 2666 + 2667 + '@radix-ui/react-checkbox@1.3.2(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': 2668 + dependencies: 2669 + '@radix-ui/primitive': 1.1.2 2670 + '@radix-ui/react-compose-refs': 1.1.2(@types/react@18.3.23)(react@18.3.1) 2671 + '@radix-ui/react-context': 1.1.2(@types/react@18.3.23)(react@18.3.1) 2672 + '@radix-ui/react-presence': 1.1.4(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) 2673 + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) 2674 + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@18.3.23)(react@18.3.1) 2675 + '@radix-ui/react-use-previous': 1.1.1(@types/react@18.3.23)(react@18.3.1) 2676 + '@radix-ui/react-use-size': 1.1.1(@types/react@18.3.23)(react@18.3.1) 2677 + react: 18.3.1 2678 + react-dom: 18.3.1(react@18.3.1) 2679 + optionalDependencies: 2680 + '@types/react': 18.3.23 2681 + '@types/react-dom': 18.3.7(@types/react@18.3.23) 2682 + 2683 + '@radix-ui/react-collapsible@1.1.11(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': 2684 + dependencies: 2685 + '@radix-ui/primitive': 1.1.2 2686 + '@radix-ui/react-compose-refs': 1.1.2(@types/react@18.3.23)(react@18.3.1) 2687 + '@radix-ui/react-context': 1.1.2(@types/react@18.3.23)(react@18.3.1) 2688 + '@radix-ui/react-id': 1.1.1(@types/react@18.3.23)(react@18.3.1) 2689 + '@radix-ui/react-presence': 1.1.4(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) 2690 + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) 2691 + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@18.3.23)(react@18.3.1) 2692 + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@18.3.23)(react@18.3.1) 2693 + react: 18.3.1 2694 + react-dom: 18.3.1(react@18.3.1) 2695 + optionalDependencies: 2696 + '@types/react': 18.3.23 2697 + '@types/react-dom': 18.3.7(@types/react@18.3.23) 2698 + 2699 + '@radix-ui/react-collection@1.1.7(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': 2700 + dependencies: 2701 + '@radix-ui/react-compose-refs': 1.1.2(@types/react@18.3.23)(react@18.3.1) 2702 + '@radix-ui/react-context': 1.1.2(@types/react@18.3.23)(react@18.3.1) 2703 + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) 2704 + '@radix-ui/react-slot': 1.2.3(@types/react@18.3.23)(react@18.3.1) 2705 + react: 18.3.1 2706 + react-dom: 18.3.1(react@18.3.1) 2707 + optionalDependencies: 2708 + '@types/react': 18.3.23 2709 + '@types/react-dom': 18.3.7(@types/react@18.3.23) 2710 + 2711 + '@radix-ui/react-compose-refs@1.1.2(@types/react@18.3.23)(react@18.3.1)': 2712 + dependencies: 2713 + react: 18.3.1 2714 + optionalDependencies: 2715 + '@types/react': 18.3.23 2716 + 2717 + '@radix-ui/react-context-menu@2.2.15(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': 2718 + dependencies: 2719 + '@radix-ui/primitive': 1.1.2 2720 + '@radix-ui/react-context': 1.1.2(@types/react@18.3.23)(react@18.3.1) 2721 + '@radix-ui/react-menu': 2.1.15(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) 2722 + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) 2723 + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@18.3.23)(react@18.3.1) 2724 + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@18.3.23)(react@18.3.1) 2725 + react: 18.3.1 2726 + react-dom: 18.3.1(react@18.3.1) 2727 + optionalDependencies: 2728 + '@types/react': 18.3.23 2729 + '@types/react-dom': 18.3.7(@types/react@18.3.23) 2730 + 2731 + '@radix-ui/react-context@1.1.2(@types/react@18.3.23)(react@18.3.1)': 2732 + dependencies: 2733 + react: 18.3.1 2734 + optionalDependencies: 2735 + '@types/react': 18.3.23 2736 + 2737 + '@radix-ui/react-dialog@1.1.14(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': 2738 + dependencies: 2739 + '@radix-ui/primitive': 1.1.2 2740 + '@radix-ui/react-compose-refs': 1.1.2(@types/react@18.3.23)(react@18.3.1) 2741 + '@radix-ui/react-context': 1.1.2(@types/react@18.3.23)(react@18.3.1) 2742 + '@radix-ui/react-dismissable-layer': 1.1.10(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) 2743 + '@radix-ui/react-focus-guards': 1.1.2(@types/react@18.3.23)(react@18.3.1) 2744 + '@radix-ui/react-focus-scope': 1.1.7(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) 2745 + '@radix-ui/react-id': 1.1.1(@types/react@18.3.23)(react@18.3.1) 2746 + '@radix-ui/react-portal': 1.1.9(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) 2747 + '@radix-ui/react-presence': 1.1.4(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) 2748 + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) 2749 + '@radix-ui/react-slot': 1.2.3(@types/react@18.3.23)(react@18.3.1) 2750 + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@18.3.23)(react@18.3.1) 2751 + aria-hidden: 1.2.6 2752 + react: 18.3.1 2753 + react-dom: 18.3.1(react@18.3.1) 2754 + react-remove-scroll: 2.7.1(@types/react@18.3.23)(react@18.3.1) 2755 + optionalDependencies: 2756 + '@types/react': 18.3.23 2757 + '@types/react-dom': 18.3.7(@types/react@18.3.23) 2758 + 2759 + '@radix-ui/react-direction@1.1.1(@types/react@18.3.23)(react@18.3.1)': 2760 + dependencies: 2761 + react: 18.3.1 2762 + optionalDependencies: 2763 + '@types/react': 18.3.23 2764 + 2765 + '@radix-ui/react-dismissable-layer@1.1.10(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': 2766 + dependencies: 2767 + '@radix-ui/primitive': 1.1.2 2768 + '@radix-ui/react-compose-refs': 1.1.2(@types/react@18.3.23)(react@18.3.1) 2769 + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) 2770 + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@18.3.23)(react@18.3.1) 2771 + '@radix-ui/react-use-escape-keydown': 1.1.1(@types/react@18.3.23)(react@18.3.1) 2772 + react: 18.3.1 2773 + react-dom: 18.3.1(react@18.3.1) 2774 + optionalDependencies: 2775 + '@types/react': 18.3.23 2776 + '@types/react-dom': 18.3.7(@types/react@18.3.23) 2777 + 2778 + '@radix-ui/react-dropdown-menu@2.1.15(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': 2779 + dependencies: 2780 + '@radix-ui/primitive': 1.1.2 2781 + '@radix-ui/react-compose-refs': 1.1.2(@types/react@18.3.23)(react@18.3.1) 2782 + '@radix-ui/react-context': 1.1.2(@types/react@18.3.23)(react@18.3.1) 2783 + '@radix-ui/react-id': 1.1.1(@types/react@18.3.23)(react@18.3.1) 2784 + '@radix-ui/react-menu': 2.1.15(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) 2785 + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) 2786 + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@18.3.23)(react@18.3.1) 2787 + react: 18.3.1 2788 + react-dom: 18.3.1(react@18.3.1) 2789 + optionalDependencies: 2790 + '@types/react': 18.3.23 2791 + '@types/react-dom': 18.3.7(@types/react@18.3.23) 2792 + 2793 + '@radix-ui/react-focus-guards@1.1.2(@types/react@18.3.23)(react@18.3.1)': 2794 + dependencies: 2795 + react: 18.3.1 2796 + optionalDependencies: 2797 + '@types/react': 18.3.23 2798 + 2799 + '@radix-ui/react-focus-scope@1.1.7(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': 2800 + dependencies: 2801 + '@radix-ui/react-compose-refs': 1.1.2(@types/react@18.3.23)(react@18.3.1) 2802 + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) 2803 + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@18.3.23)(react@18.3.1) 2804 + react: 18.3.1 2805 + react-dom: 18.3.1(react@18.3.1) 2806 + optionalDependencies: 2807 + '@types/react': 18.3.23 2808 + '@types/react-dom': 18.3.7(@types/react@18.3.23) 2809 + 2810 + '@radix-ui/react-hover-card@1.1.14(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': 2811 + dependencies: 2812 + '@radix-ui/primitive': 1.1.2 2813 + '@radix-ui/react-compose-refs': 1.1.2(@types/react@18.3.23)(react@18.3.1) 2814 + '@radix-ui/react-context': 1.1.2(@types/react@18.3.23)(react@18.3.1) 2815 + '@radix-ui/react-dismissable-layer': 1.1.10(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) 2816 + '@radix-ui/react-popper': 1.2.7(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) 2817 + '@radix-ui/react-portal': 1.1.9(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) 2818 + '@radix-ui/react-presence': 1.1.4(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) 2819 + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) 2820 + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@18.3.23)(react@18.3.1) 2821 + react: 18.3.1 2822 + react-dom: 18.3.1(react@18.3.1) 2823 + optionalDependencies: 2824 + '@types/react': 18.3.23 2825 + '@types/react-dom': 18.3.7(@types/react@18.3.23) 2826 + 2827 + '@radix-ui/react-id@1.1.1(@types/react@18.3.23)(react@18.3.1)': 2828 + dependencies: 2829 + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@18.3.23)(react@18.3.1) 2830 + react: 18.3.1 2831 + optionalDependencies: 2832 + '@types/react': 18.3.23 2833 + 2834 + '@radix-ui/react-label@2.1.7(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': 2835 + dependencies: 2836 + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) 2837 + react: 18.3.1 2838 + react-dom: 18.3.1(react@18.3.1) 2839 + optionalDependencies: 2840 + '@types/react': 18.3.23 2841 + '@types/react-dom': 18.3.7(@types/react@18.3.23) 2842 + 2843 + '@radix-ui/react-menu@2.1.15(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': 2844 + dependencies: 2845 + '@radix-ui/primitive': 1.1.2 2846 + '@radix-ui/react-collection': 1.1.7(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) 2847 + '@radix-ui/react-compose-refs': 1.1.2(@types/react@18.3.23)(react@18.3.1) 2848 + '@radix-ui/react-context': 1.1.2(@types/react@18.3.23)(react@18.3.1) 2849 + '@radix-ui/react-direction': 1.1.1(@types/react@18.3.23)(react@18.3.1) 2850 + '@radix-ui/react-dismissable-layer': 1.1.10(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) 2851 + '@radix-ui/react-focus-guards': 1.1.2(@types/react@18.3.23)(react@18.3.1) 2852 + '@radix-ui/react-focus-scope': 1.1.7(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) 2853 + '@radix-ui/react-id': 1.1.1(@types/react@18.3.23)(react@18.3.1) 2854 + '@radix-ui/react-popper': 1.2.7(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) 2855 + '@radix-ui/react-portal': 1.1.9(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) 2856 + '@radix-ui/react-presence': 1.1.4(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) 2857 + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) 2858 + '@radix-ui/react-roving-focus': 1.1.10(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) 2859 + '@radix-ui/react-slot': 1.2.3(@types/react@18.3.23)(react@18.3.1) 2860 + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@18.3.23)(react@18.3.1) 2861 + aria-hidden: 1.2.6 2862 + react: 18.3.1 2863 + react-dom: 18.3.1(react@18.3.1) 2864 + react-remove-scroll: 2.7.1(@types/react@18.3.23)(react@18.3.1) 2865 + optionalDependencies: 2866 + '@types/react': 18.3.23 2867 + '@types/react-dom': 18.3.7(@types/react@18.3.23) 2868 + 2869 + '@radix-ui/react-menubar@1.1.15(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': 2870 + dependencies: 2871 + '@radix-ui/primitive': 1.1.2 2872 + '@radix-ui/react-collection': 1.1.7(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) 2873 + '@radix-ui/react-compose-refs': 1.1.2(@types/react@18.3.23)(react@18.3.1) 2874 + '@radix-ui/react-context': 1.1.2(@types/react@18.3.23)(react@18.3.1) 2875 + '@radix-ui/react-direction': 1.1.1(@types/react@18.3.23)(react@18.3.1) 2876 + '@radix-ui/react-id': 1.1.1(@types/react@18.3.23)(react@18.3.1) 2877 + '@radix-ui/react-menu': 2.1.15(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) 2878 + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) 2879 + '@radix-ui/react-roving-focus': 1.1.10(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) 2880 + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@18.3.23)(react@18.3.1) 2881 + react: 18.3.1 2882 + react-dom: 18.3.1(react@18.3.1) 2883 + optionalDependencies: 2884 + '@types/react': 18.3.23 2885 + '@types/react-dom': 18.3.7(@types/react@18.3.23) 2886 + 2887 + '@radix-ui/react-navigation-menu@1.2.13(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': 2888 + dependencies: 2889 + '@radix-ui/primitive': 1.1.2 2890 + '@radix-ui/react-collection': 1.1.7(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) 2891 + '@radix-ui/react-compose-refs': 1.1.2(@types/react@18.3.23)(react@18.3.1) 2892 + '@radix-ui/react-context': 1.1.2(@types/react@18.3.23)(react@18.3.1) 2893 + '@radix-ui/react-direction': 1.1.1(@types/react@18.3.23)(react@18.3.1) 2894 + '@radix-ui/react-dismissable-layer': 1.1.10(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) 2895 + '@radix-ui/react-id': 1.1.1(@types/react@18.3.23)(react@18.3.1) 2896 + '@radix-ui/react-presence': 1.1.4(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) 2897 + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) 2898 + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@18.3.23)(react@18.3.1) 2899 + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@18.3.23)(react@18.3.1) 2900 + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@18.3.23)(react@18.3.1) 2901 + '@radix-ui/react-use-previous': 1.1.1(@types/react@18.3.23)(react@18.3.1) 2902 + '@radix-ui/react-visually-hidden': 1.2.3(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) 2903 + react: 18.3.1 2904 + react-dom: 18.3.1(react@18.3.1) 2905 + optionalDependencies: 2906 + '@types/react': 18.3.23 2907 + '@types/react-dom': 18.3.7(@types/react@18.3.23) 2908 + 2909 + '@radix-ui/react-popover@1.1.14(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': 2910 + dependencies: 2911 + '@radix-ui/primitive': 1.1.2 2912 + '@radix-ui/react-compose-refs': 1.1.2(@types/react@18.3.23)(react@18.3.1) 2913 + '@radix-ui/react-context': 1.1.2(@types/react@18.3.23)(react@18.3.1) 2914 + '@radix-ui/react-dismissable-layer': 1.1.10(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) 2915 + '@radix-ui/react-focus-guards': 1.1.2(@types/react@18.3.23)(react@18.3.1) 2916 + '@radix-ui/react-focus-scope': 1.1.7(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) 2917 + '@radix-ui/react-id': 1.1.1(@types/react@18.3.23)(react@18.3.1) 2918 + '@radix-ui/react-popper': 1.2.7(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) 2919 + '@radix-ui/react-portal': 1.1.9(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) 2920 + '@radix-ui/react-presence': 1.1.4(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) 2921 + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) 2922 + '@radix-ui/react-slot': 1.2.3(@types/react@18.3.23)(react@18.3.1) 2923 + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@18.3.23)(react@18.3.1) 2924 + aria-hidden: 1.2.6 2925 + react: 18.3.1 2926 + react-dom: 18.3.1(react@18.3.1) 2927 + react-remove-scroll: 2.7.1(@types/react@18.3.23)(react@18.3.1) 2928 + optionalDependencies: 2929 + '@types/react': 18.3.23 2930 + '@types/react-dom': 18.3.7(@types/react@18.3.23) 2931 + 2932 + '@radix-ui/react-popper@1.2.7(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': 2933 + dependencies: 2934 + '@floating-ui/react-dom': 2.1.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1) 2935 + '@radix-ui/react-arrow': 1.1.7(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) 2936 + '@radix-ui/react-compose-refs': 1.1.2(@types/react@18.3.23)(react@18.3.1) 2937 + '@radix-ui/react-context': 1.1.2(@types/react@18.3.23)(react@18.3.1) 2938 + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) 2939 + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@18.3.23)(react@18.3.1) 2940 + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@18.3.23)(react@18.3.1) 2941 + '@radix-ui/react-use-rect': 1.1.1(@types/react@18.3.23)(react@18.3.1) 2942 + '@radix-ui/react-use-size': 1.1.1(@types/react@18.3.23)(react@18.3.1) 2943 + '@radix-ui/rect': 1.1.1 2944 + react: 18.3.1 2945 + react-dom: 18.3.1(react@18.3.1) 2946 + optionalDependencies: 2947 + '@types/react': 18.3.23 2948 + '@types/react-dom': 18.3.7(@types/react@18.3.23) 2949 + 2950 + '@radix-ui/react-portal@1.1.9(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': 2951 + dependencies: 2952 + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) 2953 + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@18.3.23)(react@18.3.1) 2954 + react: 18.3.1 2955 + react-dom: 18.3.1(react@18.3.1) 2956 + optionalDependencies: 2957 + '@types/react': 18.3.23 2958 + '@types/react-dom': 18.3.7(@types/react@18.3.23) 2959 + 2960 + '@radix-ui/react-presence@1.1.4(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': 2961 + dependencies: 2962 + '@radix-ui/react-compose-refs': 1.1.2(@types/react@18.3.23)(react@18.3.1) 2963 + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@18.3.23)(react@18.3.1) 2964 + react: 18.3.1 2965 + react-dom: 18.3.1(react@18.3.1) 2966 + optionalDependencies: 2967 + '@types/react': 18.3.23 2968 + '@types/react-dom': 18.3.7(@types/react@18.3.23) 2969 + 2970 + '@radix-ui/react-primitive@2.1.3(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': 2971 + dependencies: 2972 + '@radix-ui/react-slot': 1.2.3(@types/react@18.3.23)(react@18.3.1) 2973 + react: 18.3.1 2974 + react-dom: 18.3.1(react@18.3.1) 2975 + optionalDependencies: 2976 + '@types/react': 18.3.23 2977 + '@types/react-dom': 18.3.7(@types/react@18.3.23) 2978 + 2979 + '@radix-ui/react-progress@1.1.7(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': 2980 + dependencies: 2981 + '@radix-ui/react-context': 1.1.2(@types/react@18.3.23)(react@18.3.1) 2982 + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) 2983 + react: 18.3.1 2984 + react-dom: 18.3.1(react@18.3.1) 2985 + optionalDependencies: 2986 + '@types/react': 18.3.23 2987 + '@types/react-dom': 18.3.7(@types/react@18.3.23) 2988 + 2989 + '@radix-ui/react-radio-group@1.3.7(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': 2990 + dependencies: 2991 + '@radix-ui/primitive': 1.1.2 2992 + '@radix-ui/react-compose-refs': 1.1.2(@types/react@18.3.23)(react@18.3.1) 2993 + '@radix-ui/react-context': 1.1.2(@types/react@18.3.23)(react@18.3.1) 2994 + '@radix-ui/react-direction': 1.1.1(@types/react@18.3.23)(react@18.3.1) 2995 + '@radix-ui/react-presence': 1.1.4(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) 2996 + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) 2997 + '@radix-ui/react-roving-focus': 1.1.10(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) 2998 + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@18.3.23)(react@18.3.1) 2999 + '@radix-ui/react-use-previous': 1.1.1(@types/react@18.3.23)(react@18.3.1) 3000 + '@radix-ui/react-use-size': 1.1.1(@types/react@18.3.23)(react@18.3.1) 3001 + react: 18.3.1 3002 + react-dom: 18.3.1(react@18.3.1) 3003 + optionalDependencies: 3004 + '@types/react': 18.3.23 3005 + '@types/react-dom': 18.3.7(@types/react@18.3.23) 3006 + 3007 + '@radix-ui/react-roving-focus@1.1.10(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': 3008 + dependencies: 3009 + '@radix-ui/primitive': 1.1.2 3010 + '@radix-ui/react-collection': 1.1.7(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) 3011 + '@radix-ui/react-compose-refs': 1.1.2(@types/react@18.3.23)(react@18.3.1) 3012 + '@radix-ui/react-context': 1.1.2(@types/react@18.3.23)(react@18.3.1) 3013 + '@radix-ui/react-direction': 1.1.1(@types/react@18.3.23)(react@18.3.1) 3014 + '@radix-ui/react-id': 1.1.1(@types/react@18.3.23)(react@18.3.1) 3015 + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) 3016 + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@18.3.23)(react@18.3.1) 3017 + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@18.3.23)(react@18.3.1) 3018 + react: 18.3.1 3019 + react-dom: 18.3.1(react@18.3.1) 3020 + optionalDependencies: 3021 + '@types/react': 18.3.23 3022 + '@types/react-dom': 18.3.7(@types/react@18.3.23) 3023 + 3024 + '@radix-ui/react-scroll-area@1.2.9(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': 3025 + dependencies: 3026 + '@radix-ui/number': 1.1.1 3027 + '@radix-ui/primitive': 1.1.2 3028 + '@radix-ui/react-compose-refs': 1.1.2(@types/react@18.3.23)(react@18.3.1) 3029 + '@radix-ui/react-context': 1.1.2(@types/react@18.3.23)(react@18.3.1) 3030 + '@radix-ui/react-direction': 1.1.1(@types/react@18.3.23)(react@18.3.1) 3031 + '@radix-ui/react-presence': 1.1.4(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) 3032 + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) 3033 + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@18.3.23)(react@18.3.1) 3034 + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@18.3.23)(react@18.3.1) 3035 + react: 18.3.1 3036 + react-dom: 18.3.1(react@18.3.1) 3037 + optionalDependencies: 3038 + '@types/react': 18.3.23 3039 + '@types/react-dom': 18.3.7(@types/react@18.3.23) 3040 + 3041 + '@radix-ui/react-select@2.2.5(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': 3042 + dependencies: 3043 + '@radix-ui/number': 1.1.1 3044 + '@radix-ui/primitive': 1.1.2 3045 + '@radix-ui/react-collection': 1.1.7(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) 3046 + '@radix-ui/react-compose-refs': 1.1.2(@types/react@18.3.23)(react@18.3.1) 3047 + '@radix-ui/react-context': 1.1.2(@types/react@18.3.23)(react@18.3.1) 3048 + '@radix-ui/react-direction': 1.1.1(@types/react@18.3.23)(react@18.3.1) 3049 + '@radix-ui/react-dismissable-layer': 1.1.10(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) 3050 + '@radix-ui/react-focus-guards': 1.1.2(@types/react@18.3.23)(react@18.3.1) 3051 + '@radix-ui/react-focus-scope': 1.1.7(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) 3052 + '@radix-ui/react-id': 1.1.1(@types/react@18.3.23)(react@18.3.1) 3053 + '@radix-ui/react-popper': 1.2.7(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) 3054 + '@radix-ui/react-portal': 1.1.9(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) 3055 + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) 3056 + '@radix-ui/react-slot': 1.2.3(@types/react@18.3.23)(react@18.3.1) 3057 + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@18.3.23)(react@18.3.1) 3058 + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@18.3.23)(react@18.3.1) 3059 + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@18.3.23)(react@18.3.1) 3060 + '@radix-ui/react-use-previous': 1.1.1(@types/react@18.3.23)(react@18.3.1) 3061 + '@radix-ui/react-visually-hidden': 1.2.3(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) 3062 + aria-hidden: 1.2.6 3063 + react: 18.3.1 3064 + react-dom: 18.3.1(react@18.3.1) 3065 + react-remove-scroll: 2.7.1(@types/react@18.3.23)(react@18.3.1) 3066 + optionalDependencies: 3067 + '@types/react': 18.3.23 3068 + '@types/react-dom': 18.3.7(@types/react@18.3.23) 3069 + 3070 + '@radix-ui/react-separator@1.1.7(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': 3071 + dependencies: 3072 + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) 3073 + react: 18.3.1 3074 + react-dom: 18.3.1(react@18.3.1) 3075 + optionalDependencies: 3076 + '@types/react': 18.3.23 3077 + '@types/react-dom': 18.3.7(@types/react@18.3.23) 3078 + 3079 + '@radix-ui/react-slider@1.3.5(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': 3080 + dependencies: 3081 + '@radix-ui/number': 1.1.1 3082 + '@radix-ui/primitive': 1.1.2 3083 + '@radix-ui/react-collection': 1.1.7(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) 3084 + '@radix-ui/react-compose-refs': 1.1.2(@types/react@18.3.23)(react@18.3.1) 3085 + '@radix-ui/react-context': 1.1.2(@types/react@18.3.23)(react@18.3.1) 3086 + '@radix-ui/react-direction': 1.1.1(@types/react@18.3.23)(react@18.3.1) 3087 + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) 3088 + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@18.3.23)(react@18.3.1) 3089 + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@18.3.23)(react@18.3.1) 3090 + '@radix-ui/react-use-previous': 1.1.1(@types/react@18.3.23)(react@18.3.1) 3091 + '@radix-ui/react-use-size': 1.1.1(@types/react@18.3.23)(react@18.3.1) 3092 + react: 18.3.1 3093 + react-dom: 18.3.1(react@18.3.1) 3094 + optionalDependencies: 3095 + '@types/react': 18.3.23 3096 + '@types/react-dom': 18.3.7(@types/react@18.3.23) 3097 + 3098 + '@radix-ui/react-slot@1.2.3(@types/react@18.3.23)(react@18.3.1)': 3099 + dependencies: 3100 + '@radix-ui/react-compose-refs': 1.1.2(@types/react@18.3.23)(react@18.3.1) 3101 + react: 18.3.1 3102 + optionalDependencies: 3103 + '@types/react': 18.3.23 3104 + 3105 + '@radix-ui/react-switch@1.2.5(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': 3106 + dependencies: 3107 + '@radix-ui/primitive': 1.1.2 3108 + '@radix-ui/react-compose-refs': 1.1.2(@types/react@18.3.23)(react@18.3.1) 3109 + '@radix-ui/react-context': 1.1.2(@types/react@18.3.23)(react@18.3.1) 3110 + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) 3111 + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@18.3.23)(react@18.3.1) 3112 + '@radix-ui/react-use-previous': 1.1.1(@types/react@18.3.23)(react@18.3.1) 3113 + '@radix-ui/react-use-size': 1.1.1(@types/react@18.3.23)(react@18.3.1) 3114 + react: 18.3.1 3115 + react-dom: 18.3.1(react@18.3.1) 3116 + optionalDependencies: 3117 + '@types/react': 18.3.23 3118 + '@types/react-dom': 18.3.7(@types/react@18.3.23) 3119 + 3120 + '@radix-ui/react-tabs@1.1.12(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': 3121 + dependencies: 3122 + '@radix-ui/primitive': 1.1.2 3123 + '@radix-ui/react-context': 1.1.2(@types/react@18.3.23)(react@18.3.1) 3124 + '@radix-ui/react-direction': 1.1.1(@types/react@18.3.23)(react@18.3.1) 3125 + '@radix-ui/react-id': 1.1.1(@types/react@18.3.23)(react@18.3.1) 3126 + '@radix-ui/react-presence': 1.1.4(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) 3127 + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) 3128 + '@radix-ui/react-roving-focus': 1.1.10(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) 3129 + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@18.3.23)(react@18.3.1) 3130 + react: 18.3.1 3131 + react-dom: 18.3.1(react@18.3.1) 3132 + optionalDependencies: 3133 + '@types/react': 18.3.23 3134 + '@types/react-dom': 18.3.7(@types/react@18.3.23) 3135 + 3136 + '@radix-ui/react-toast@1.2.14(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': 3137 + dependencies: 3138 + '@radix-ui/primitive': 1.1.2 3139 + '@radix-ui/react-collection': 1.1.7(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) 3140 + '@radix-ui/react-compose-refs': 1.1.2(@types/react@18.3.23)(react@18.3.1) 3141 + '@radix-ui/react-context': 1.1.2(@types/react@18.3.23)(react@18.3.1) 3142 + '@radix-ui/react-dismissable-layer': 1.1.10(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) 3143 + '@radix-ui/react-portal': 1.1.9(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) 3144 + '@radix-ui/react-presence': 1.1.4(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) 3145 + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) 3146 + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@18.3.23)(react@18.3.1) 3147 + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@18.3.23)(react@18.3.1) 3148 + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@18.3.23)(react@18.3.1) 3149 + '@radix-ui/react-visually-hidden': 1.2.3(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) 3150 + react: 18.3.1 3151 + react-dom: 18.3.1(react@18.3.1) 3152 + optionalDependencies: 3153 + '@types/react': 18.3.23 3154 + '@types/react-dom': 18.3.7(@types/react@18.3.23) 3155 + 3156 + '@radix-ui/react-toggle-group@1.1.10(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': 3157 + dependencies: 3158 + '@radix-ui/primitive': 1.1.2 3159 + '@radix-ui/react-context': 1.1.2(@types/react@18.3.23)(react@18.3.1) 3160 + '@radix-ui/react-direction': 1.1.1(@types/react@18.3.23)(react@18.3.1) 3161 + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) 3162 + '@radix-ui/react-roving-focus': 1.1.10(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) 3163 + '@radix-ui/react-toggle': 1.1.9(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) 3164 + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@18.3.23)(react@18.3.1) 3165 + react: 18.3.1 3166 + react-dom: 18.3.1(react@18.3.1) 3167 + optionalDependencies: 3168 + '@types/react': 18.3.23 3169 + '@types/react-dom': 18.3.7(@types/react@18.3.23) 3170 + 3171 + '@radix-ui/react-toggle@1.1.9(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': 3172 + dependencies: 3173 + '@radix-ui/primitive': 1.1.2 3174 + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) 3175 + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@18.3.23)(react@18.3.1) 3176 + react: 18.3.1 3177 + react-dom: 18.3.1(react@18.3.1) 3178 + optionalDependencies: 3179 + '@types/react': 18.3.23 3180 + '@types/react-dom': 18.3.7(@types/react@18.3.23) 3181 + 3182 + '@radix-ui/react-tooltip@1.2.7(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': 3183 + dependencies: 3184 + '@radix-ui/primitive': 1.1.2 3185 + '@radix-ui/react-compose-refs': 1.1.2(@types/react@18.3.23)(react@18.3.1) 3186 + '@radix-ui/react-context': 1.1.2(@types/react@18.3.23)(react@18.3.1) 3187 + '@radix-ui/react-dismissable-layer': 1.1.10(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) 3188 + '@radix-ui/react-id': 1.1.1(@types/react@18.3.23)(react@18.3.1) 3189 + '@radix-ui/react-popper': 1.2.7(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) 3190 + '@radix-ui/react-portal': 1.1.9(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) 3191 + '@radix-ui/react-presence': 1.1.4(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) 3192 + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) 3193 + '@radix-ui/react-slot': 1.2.3(@types/react@18.3.23)(react@18.3.1) 3194 + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@18.3.23)(react@18.3.1) 3195 + '@radix-ui/react-visually-hidden': 1.2.3(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) 3196 + react: 18.3.1 3197 + react-dom: 18.3.1(react@18.3.1) 3198 + optionalDependencies: 3199 + '@types/react': 18.3.23 3200 + '@types/react-dom': 18.3.7(@types/react@18.3.23) 3201 + 3202 + '@radix-ui/react-use-callback-ref@1.1.1(@types/react@18.3.23)(react@18.3.1)': 3203 + dependencies: 3204 + react: 18.3.1 3205 + optionalDependencies: 3206 + '@types/react': 18.3.23 3207 + 3208 + '@radix-ui/react-use-controllable-state@1.2.2(@types/react@18.3.23)(react@18.3.1)': 3209 + dependencies: 3210 + '@radix-ui/react-use-effect-event': 0.0.2(@types/react@18.3.23)(react@18.3.1) 3211 + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@18.3.23)(react@18.3.1) 3212 + react: 18.3.1 3213 + optionalDependencies: 3214 + '@types/react': 18.3.23 3215 + 3216 + '@radix-ui/react-use-effect-event@0.0.2(@types/react@18.3.23)(react@18.3.1)': 3217 + dependencies: 3218 + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@18.3.23)(react@18.3.1) 3219 + react: 18.3.1 3220 + optionalDependencies: 3221 + '@types/react': 18.3.23 3222 + 3223 + '@radix-ui/react-use-escape-keydown@1.1.1(@types/react@18.3.23)(react@18.3.1)': 3224 + dependencies: 3225 + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@18.3.23)(react@18.3.1) 3226 + react: 18.3.1 3227 + optionalDependencies: 3228 + '@types/react': 18.3.23 3229 + 3230 + '@radix-ui/react-use-is-hydrated@0.1.0(@types/react@18.3.23)(react@18.3.1)': 3231 + dependencies: 3232 + react: 18.3.1 3233 + use-sync-external-store: 1.5.0(react@18.3.1) 3234 + optionalDependencies: 3235 + '@types/react': 18.3.23 3236 + 3237 + '@radix-ui/react-use-layout-effect@1.1.1(@types/react@18.3.23)(react@18.3.1)': 3238 + dependencies: 3239 + react: 18.3.1 3240 + optionalDependencies: 3241 + '@types/react': 18.3.23 3242 + 3243 + '@radix-ui/react-use-previous@1.1.1(@types/react@18.3.23)(react@18.3.1)': 3244 + dependencies: 3245 + react: 18.3.1 3246 + optionalDependencies: 3247 + '@types/react': 18.3.23 3248 + 3249 + '@radix-ui/react-use-rect@1.1.1(@types/react@18.3.23)(react@18.3.1)': 3250 + dependencies: 3251 + '@radix-ui/rect': 1.1.1 3252 + react: 18.3.1 3253 + optionalDependencies: 3254 + '@types/react': 18.3.23 3255 + 3256 + '@radix-ui/react-use-size@1.1.1(@types/react@18.3.23)(react@18.3.1)': 3257 + dependencies: 3258 + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@18.3.23)(react@18.3.1) 3259 + react: 18.3.1 3260 + optionalDependencies: 3261 + '@types/react': 18.3.23 3262 + 3263 + '@radix-ui/react-visually-hidden@1.2.3(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': 3264 + dependencies: 3265 + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) 3266 + react: 18.3.1 3267 + react-dom: 18.3.1(react@18.3.1) 3268 + optionalDependencies: 3269 + '@types/react': 18.3.23 3270 + '@types/react-dom': 18.3.7(@types/react@18.3.23) 3271 + 3272 + '@radix-ui/rect@1.1.1': {} 3273 + 3274 + '@remix-run/router@1.23.0': {} 3275 + 3276 + '@rolldown/pluginutils@1.0.0-beta.11': {} 3277 + 3278 + '@rollup/rollup-android-arm-eabi@4.44.0': 3279 + optional: true 3280 + 3281 + '@rollup/rollup-android-arm64@4.44.0': 3282 + optional: true 3283 + 3284 + '@rollup/rollup-darwin-arm64@4.44.0': 3285 + optional: true 3286 + 3287 + '@rollup/rollup-darwin-x64@4.44.0': 3288 + optional: true 3289 + 3290 + '@rollup/rollup-freebsd-arm64@4.44.0': 3291 + optional: true 3292 + 3293 + '@rollup/rollup-freebsd-x64@4.44.0': 3294 + optional: true 3295 + 3296 + '@rollup/rollup-linux-arm-gnueabihf@4.44.0': 3297 + optional: true 3298 + 3299 + '@rollup/rollup-linux-arm-musleabihf@4.44.0': 3300 + optional: true 3301 + 3302 + '@rollup/rollup-linux-arm64-gnu@4.44.0': 3303 + optional: true 3304 + 3305 + '@rollup/rollup-linux-arm64-musl@4.44.0': 3306 + optional: true 3307 + 3308 + '@rollup/rollup-linux-loongarch64-gnu@4.44.0': 3309 + optional: true 3310 + 3311 + '@rollup/rollup-linux-powerpc64le-gnu@4.44.0': 3312 + optional: true 3313 + 3314 + '@rollup/rollup-linux-riscv64-gnu@4.44.0': 3315 + optional: true 3316 + 3317 + '@rollup/rollup-linux-riscv64-musl@4.44.0': 3318 + optional: true 3319 + 3320 + '@rollup/rollup-linux-s390x-gnu@4.44.0': 3321 + optional: true 3322 + 3323 + '@rollup/rollup-linux-x64-gnu@4.44.0': 3324 + optional: true 3325 + 3326 + '@rollup/rollup-linux-x64-musl@4.44.0': 3327 + optional: true 3328 + 3329 + '@rollup/rollup-win32-arm64-msvc@4.44.0': 3330 + optional: true 3331 + 3332 + '@rollup/rollup-win32-ia32-msvc@4.44.0': 3333 + optional: true 3334 + 3335 + '@rollup/rollup-win32-x64-msvc@4.44.0': 3336 + optional: true 3337 + 3338 + '@swc/core-darwin-arm64@1.12.4': 3339 + optional: true 3340 + 3341 + '@swc/core-darwin-x64@1.12.4': 3342 + optional: true 3343 + 3344 + '@swc/core-linux-arm-gnueabihf@1.12.4': 3345 + optional: true 3346 + 3347 + '@swc/core-linux-arm64-gnu@1.12.4': 3348 + optional: true 3349 + 3350 + '@swc/core-linux-arm64-musl@1.12.4': 3351 + optional: true 3352 + 3353 + '@swc/core-linux-x64-gnu@1.12.4': 3354 + optional: true 3355 + 3356 + '@swc/core-linux-x64-musl@1.12.4': 3357 + optional: true 3358 + 3359 + '@swc/core-win32-arm64-msvc@1.12.4': 3360 + optional: true 3361 + 3362 + '@swc/core-win32-ia32-msvc@1.12.4': 3363 + optional: true 3364 + 3365 + '@swc/core-win32-x64-msvc@1.12.4': 3366 + optional: true 3367 + 3368 + '@swc/core@1.12.4': 3369 + dependencies: 3370 + '@swc/counter': 0.1.3 3371 + '@swc/types': 0.1.23 3372 + optionalDependencies: 3373 + '@swc/core-darwin-arm64': 1.12.4 3374 + '@swc/core-darwin-x64': 1.12.4 3375 + '@swc/core-linux-arm-gnueabihf': 1.12.4 3376 + '@swc/core-linux-arm64-gnu': 1.12.4 3377 + '@swc/core-linux-arm64-musl': 1.12.4 3378 + '@swc/core-linux-x64-gnu': 1.12.4 3379 + '@swc/core-linux-x64-musl': 1.12.4 3380 + '@swc/core-win32-arm64-msvc': 1.12.4 3381 + '@swc/core-win32-ia32-msvc': 1.12.4 3382 + '@swc/core-win32-x64-msvc': 1.12.4 3383 + 3384 + '@swc/counter@0.1.3': {} 3385 + 3386 + '@swc/types@0.1.23': 3387 + dependencies: 3388 + '@swc/counter': 0.1.3 3389 + 3390 + '@tailwindcss/node@4.1.10': 3391 + dependencies: 3392 + '@ampproject/remapping': 2.3.0 3393 + enhanced-resolve: 5.18.1 3394 + jiti: 2.4.2 3395 + lightningcss: 1.30.1 3396 + magic-string: 0.30.17 3397 + source-map-js: 1.2.1 3398 + tailwindcss: 4.1.10 3399 + 3400 + '@tailwindcss/oxide-android-arm64@4.1.10': 3401 + optional: true 3402 + 3403 + '@tailwindcss/oxide-darwin-arm64@4.1.10': 3404 + optional: true 3405 + 3406 + '@tailwindcss/oxide-darwin-x64@4.1.10': 3407 + optional: true 3408 + 3409 + '@tailwindcss/oxide-freebsd-x64@4.1.10': 3410 + optional: true 3411 + 3412 + '@tailwindcss/oxide-linux-arm-gnueabihf@4.1.10': 3413 + optional: true 3414 + 3415 + '@tailwindcss/oxide-linux-arm64-gnu@4.1.10': 3416 + optional: true 3417 + 3418 + '@tailwindcss/oxide-linux-arm64-musl@4.1.10': 3419 + optional: true 3420 + 3421 + '@tailwindcss/oxide-linux-x64-gnu@4.1.10': 3422 + optional: true 3423 + 3424 + '@tailwindcss/oxide-linux-x64-musl@4.1.10': 3425 + optional: true 3426 + 3427 + '@tailwindcss/oxide-wasm32-wasi@4.1.10': 3428 + optional: true 3429 + 3430 + '@tailwindcss/oxide-win32-arm64-msvc@4.1.10': 3431 + optional: true 3432 + 3433 + '@tailwindcss/oxide-win32-x64-msvc@4.1.10': 3434 + optional: true 3435 + 3436 + '@tailwindcss/oxide@4.1.10': 3437 + dependencies: 3438 + detect-libc: 2.0.4 3439 + tar: 7.4.3 3440 + optionalDependencies: 3441 + '@tailwindcss/oxide-android-arm64': 4.1.10 3442 + '@tailwindcss/oxide-darwin-arm64': 4.1.10 3443 + '@tailwindcss/oxide-darwin-x64': 4.1.10 3444 + '@tailwindcss/oxide-freebsd-x64': 4.1.10 3445 + '@tailwindcss/oxide-linux-arm-gnueabihf': 4.1.10 3446 + '@tailwindcss/oxide-linux-arm64-gnu': 4.1.10 3447 + '@tailwindcss/oxide-linux-arm64-musl': 4.1.10 3448 + '@tailwindcss/oxide-linux-x64-gnu': 4.1.10 3449 + '@tailwindcss/oxide-linux-x64-musl': 4.1.10 3450 + '@tailwindcss/oxide-wasm32-wasi': 4.1.10 3451 + '@tailwindcss/oxide-win32-arm64-msvc': 4.1.10 3452 + '@tailwindcss/oxide-win32-x64-msvc': 4.1.10 3453 + 3454 + '@tailwindcss/postcss@4.1.10': 3455 + dependencies: 3456 + '@alloc/quick-lru': 5.2.0 3457 + '@tailwindcss/node': 4.1.10 3458 + '@tailwindcss/oxide': 4.1.10 3459 + postcss: 8.5.6 3460 + tailwindcss: 4.1.10 3461 + 3462 + '@tailwindcss/typography@0.5.16(tailwindcss@4.1.10)': 3463 + dependencies: 3464 + lodash.castarray: 4.4.0 3465 + lodash.isplainobject: 4.0.6 3466 + lodash.merge: 4.6.2 3467 + postcss-selector-parser: 6.0.10 3468 + tailwindcss: 4.1.10 3469 + 3470 + '@tailwindcss/vite@4.1.10(vite@5.4.19(@types/node@22.15.32)(lightningcss@1.30.1))': 3471 + dependencies: 3472 + '@tailwindcss/node': 4.1.10 3473 + '@tailwindcss/oxide': 4.1.10 3474 + tailwindcss: 4.1.10 3475 + vite: 5.4.19(@types/node@22.15.32)(lightningcss@1.30.1) 3476 + 3477 + '@types/d3-array@3.2.1': {} 3478 + 3479 + '@types/d3-color@3.1.3': {} 3480 + 3481 + '@types/d3-ease@3.0.2': {} 3482 + 3483 + '@types/d3-interpolate@3.0.4': 3484 + dependencies: 3485 + '@types/d3-color': 3.1.3 3486 + 3487 + '@types/d3-path@3.1.1': {} 3488 + 3489 + '@types/d3-scale@4.0.9': 3490 + dependencies: 3491 + '@types/d3-time': 3.0.4 3492 + 3493 + '@types/d3-shape@3.1.7': 3494 + dependencies: 3495 + '@types/d3-path': 3.1.1 3496 + 3497 + '@types/d3-time@3.0.4': {} 3498 + 3499 + '@types/d3-timer@3.0.2': {} 3500 + 3501 + '@types/estree@1.0.8': {} 3502 + 3503 + '@types/json-schema@7.0.15': {} 3504 + 3505 + '@types/node@22.15.32': 3506 + dependencies: 3507 + undici-types: 6.21.0 3508 + 3509 + '@types/prop-types@15.7.15': {} 3510 + 3511 + '@types/react-dom@18.3.7(@types/react@18.3.23)': 3512 + dependencies: 3513 + '@types/react': 18.3.23 3514 + 3515 + '@types/react@18.3.23': 3516 + dependencies: 3517 + '@types/prop-types': 15.7.15 3518 + csstype: 3.1.3 3519 + 3520 + '@typescript-eslint/eslint-plugin@8.34.1(@typescript-eslint/parser@8.34.1(eslint@9.29.0(jiti@2.4.2))(typescript@5.8.3))(eslint@9.29.0(jiti@2.4.2))(typescript@5.8.3)': 3521 + dependencies: 3522 + '@eslint-community/regexpp': 4.12.1 3523 + '@typescript-eslint/parser': 8.34.1(eslint@9.29.0(jiti@2.4.2))(typescript@5.8.3) 3524 + '@typescript-eslint/scope-manager': 8.34.1 3525 + '@typescript-eslint/type-utils': 8.34.1(eslint@9.29.0(jiti@2.4.2))(typescript@5.8.3) 3526 + '@typescript-eslint/utils': 8.34.1(eslint@9.29.0(jiti@2.4.2))(typescript@5.8.3) 3527 + '@typescript-eslint/visitor-keys': 8.34.1 3528 + eslint: 9.29.0(jiti@2.4.2) 3529 + graphemer: 1.4.0 3530 + ignore: 7.0.5 3531 + natural-compare: 1.4.0 3532 + ts-api-utils: 2.1.0(typescript@5.8.3) 3533 + typescript: 5.8.3 3534 + transitivePeerDependencies: 3535 + - supports-color 3536 + 3537 + '@typescript-eslint/parser@8.34.1(eslint@9.29.0(jiti@2.4.2))(typescript@5.8.3)': 3538 + dependencies: 3539 + '@typescript-eslint/scope-manager': 8.34.1 3540 + '@typescript-eslint/types': 8.34.1 3541 + '@typescript-eslint/typescript-estree': 8.34.1(typescript@5.8.3) 3542 + '@typescript-eslint/visitor-keys': 8.34.1 3543 + debug: 4.4.1 3544 + eslint: 9.29.0(jiti@2.4.2) 3545 + typescript: 5.8.3 3546 + transitivePeerDependencies: 3547 + - supports-color 3548 + 3549 + '@typescript-eslint/project-service@8.34.1(typescript@5.8.3)': 3550 + dependencies: 3551 + '@typescript-eslint/tsconfig-utils': 8.34.1(typescript@5.8.3) 3552 + '@typescript-eslint/types': 8.34.1 3553 + debug: 4.4.1 3554 + typescript: 5.8.3 3555 + transitivePeerDependencies: 3556 + - supports-color 3557 + 3558 + '@typescript-eslint/scope-manager@8.34.1': 3559 + dependencies: 3560 + '@typescript-eslint/types': 8.34.1 3561 + '@typescript-eslint/visitor-keys': 8.34.1 3562 + 3563 + '@typescript-eslint/tsconfig-utils@8.34.1(typescript@5.8.3)': 3564 + dependencies: 3565 + typescript: 5.8.3 3566 + 3567 + '@typescript-eslint/type-utils@8.34.1(eslint@9.29.0(jiti@2.4.2))(typescript@5.8.3)': 3568 + dependencies: 3569 + '@typescript-eslint/typescript-estree': 8.34.1(typescript@5.8.3) 3570 + '@typescript-eslint/utils': 8.34.1(eslint@9.29.0(jiti@2.4.2))(typescript@5.8.3) 3571 + debug: 4.4.1 3572 + eslint: 9.29.0(jiti@2.4.2) 3573 + ts-api-utils: 2.1.0(typescript@5.8.3) 3574 + typescript: 5.8.3 3575 + transitivePeerDependencies: 3576 + - supports-color 3577 + 3578 + '@typescript-eslint/types@8.34.1': {} 3579 + 3580 + '@typescript-eslint/typescript-estree@8.34.1(typescript@5.8.3)': 3581 + dependencies: 3582 + '@typescript-eslint/project-service': 8.34.1(typescript@5.8.3) 3583 + '@typescript-eslint/tsconfig-utils': 8.34.1(typescript@5.8.3) 3584 + '@typescript-eslint/types': 8.34.1 3585 + '@typescript-eslint/visitor-keys': 8.34.1 3586 + debug: 4.4.1 3587 + fast-glob: 3.3.3 3588 + is-glob: 4.0.3 3589 + minimatch: 9.0.5 3590 + semver: 7.7.2 3591 + ts-api-utils: 2.1.0(typescript@5.8.3) 3592 + typescript: 5.8.3 3593 + transitivePeerDependencies: 3594 + - supports-color 3595 + 3596 + '@typescript-eslint/utils@8.34.1(eslint@9.29.0(jiti@2.4.2))(typescript@5.8.3)': 3597 + dependencies: 3598 + '@eslint-community/eslint-utils': 4.7.0(eslint@9.29.0(jiti@2.4.2)) 3599 + '@typescript-eslint/scope-manager': 8.34.1 3600 + '@typescript-eslint/types': 8.34.1 3601 + '@typescript-eslint/typescript-estree': 8.34.1(typescript@5.8.3) 3602 + eslint: 9.29.0(jiti@2.4.2) 3603 + typescript: 5.8.3 3604 + transitivePeerDependencies: 3605 + - supports-color 3606 + 3607 + '@typescript-eslint/visitor-keys@8.34.1': 3608 + dependencies: 3609 + '@typescript-eslint/types': 8.34.1 3610 + eslint-visitor-keys: 4.2.1 3611 + 3612 + '@vitejs/plugin-react-swc@3.10.2(vite@5.4.19(@types/node@22.15.32)(lightningcss@1.30.1))': 3613 + dependencies: 3614 + '@rolldown/pluginutils': 1.0.0-beta.11 3615 + '@swc/core': 1.12.4 3616 + vite: 5.4.19(@types/node@22.15.32)(lightningcss@1.30.1) 3617 + transitivePeerDependencies: 3618 + - '@swc/helpers' 3619 + 3620 + acorn-jsx@5.3.2(acorn@8.15.0): 3621 + dependencies: 3622 + acorn: 8.15.0 3623 + 3624 + acorn@8.15.0: {} 3625 + 3626 + ajv@6.12.6: 3627 + dependencies: 3628 + fast-deep-equal: 3.1.3 3629 + fast-json-stable-stringify: 2.1.0 3630 + json-schema-traverse: 0.4.1 3631 + uri-js: 4.4.1 3632 + 3633 + ansi-styles@4.3.0: 3634 + dependencies: 3635 + color-convert: 2.0.1 3636 + 3637 + argparse@2.0.1: {} 3638 + 3639 + aria-hidden@1.2.6: 3640 + dependencies: 3641 + tslib: 2.8.1 3642 + 3643 + balanced-match@1.0.2: {} 3644 + 3645 + base64-arraybuffer@1.0.2: {} 3646 + 3647 + brace-expansion@1.1.12: 3648 + dependencies: 3649 + balanced-match: 1.0.2 3650 + concat-map: 0.0.1 3651 + 3652 + brace-expansion@2.0.2: 3653 + dependencies: 3654 + balanced-match: 1.0.2 3655 + 3656 + braces@3.0.3: 3657 + dependencies: 3658 + fill-range: 7.1.1 3659 + 3660 + callsites@3.1.0: {} 3661 + 3662 + chalk@4.1.2: 3663 + dependencies: 3664 + ansi-styles: 4.3.0 3665 + supports-color: 7.2.0 3666 + 3667 + chownr@3.0.0: {} 3668 + 3669 + class-variance-authority@0.7.1: 3670 + dependencies: 3671 + clsx: 2.1.1 3672 + 3673 + clsx@2.1.1: {} 3674 + 3675 + cmdk@1.1.1(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1): 3676 + dependencies: 3677 + '@radix-ui/react-compose-refs': 1.1.2(@types/react@18.3.23)(react@18.3.1) 3678 + '@radix-ui/react-dialog': 1.1.14(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) 3679 + '@radix-ui/react-id': 1.1.1(@types/react@18.3.23)(react@18.3.1) 3680 + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) 3681 + react: 18.3.1 3682 + react-dom: 18.3.1(react@18.3.1) 3683 + transitivePeerDependencies: 3684 + - '@types/react' 3685 + - '@types/react-dom' 3686 + 3687 + color-convert@2.0.1: 3688 + dependencies: 3689 + color-name: 1.1.4 3690 + 3691 + color-name@1.1.4: {} 3692 + 3693 + concat-map@0.0.1: {} 3694 + 3695 + cross-spawn@7.0.6: 3696 + dependencies: 3697 + path-key: 3.1.1 3698 + shebang-command: 2.0.0 3699 + which: 2.0.2 3700 + 3701 + css-line-break@2.1.0: 3702 + dependencies: 3703 + utrie: 1.0.2 3704 + 3705 + cssesc@3.0.0: {} 3706 + 3707 + csstype@3.1.3: {} 3708 + 3709 + d3-array@3.2.4: 3710 + dependencies: 3711 + internmap: 2.0.3 3712 + 3713 + d3-color@3.1.0: {} 3714 + 3715 + d3-ease@3.0.1: {} 3716 + 3717 + d3-format@3.1.0: {} 3718 + 3719 + d3-interpolate@3.0.1: 3720 + dependencies: 3721 + d3-color: 3.1.0 3722 + 3723 + d3-path@3.1.0: {} 3724 + 3725 + d3-scale@4.0.2: 3726 + dependencies: 3727 + d3-array: 3.2.4 3728 + d3-format: 3.1.0 3729 + d3-interpolate: 3.0.1 3730 + d3-time: 3.1.0 3731 + d3-time-format: 4.1.0 3732 + 3733 + d3-shape@3.2.0: 3734 + dependencies: 3735 + d3-path: 3.1.0 3736 + 3737 + d3-time-format@4.1.0: 3738 + dependencies: 3739 + d3-time: 3.1.0 3740 + 3741 + d3-time@3.1.0: 3742 + dependencies: 3743 + d3-array: 3.2.4 3744 + 3745 + d3-timer@3.0.1: {} 3746 + 3747 + date-fns@3.6.0: {} 3748 + 3749 + debug@4.4.1: 3750 + dependencies: 3751 + ms: 2.1.3 3752 + 3753 + decimal.js-light@2.5.1: {} 3754 + 3755 + deep-is@0.1.4: {} 3756 + 3757 + detect-libc@2.0.4: {} 3758 + 3759 + detect-node-es@1.1.0: {} 3760 + 3761 + dom-helpers@5.2.1: 3762 + dependencies: 3763 + '@babel/runtime': 7.27.6 3764 + csstype: 3.1.3 3765 + 3766 + embla-carousel-react@8.6.0(react@18.3.1): 3767 + dependencies: 3768 + embla-carousel: 8.6.0 3769 + embla-carousel-reactive-utils: 8.6.0(embla-carousel@8.6.0) 3770 + react: 18.3.1 3771 + 3772 + embla-carousel-reactive-utils@8.6.0(embla-carousel@8.6.0): 3773 + dependencies: 3774 + embla-carousel: 8.6.0 3775 + 3776 + embla-carousel@8.6.0: {} 3777 + 3778 + enhanced-resolve@5.18.1: 3779 + dependencies: 3780 + graceful-fs: 4.2.11 3781 + tapable: 2.2.2 3782 + 3783 + esbuild@0.21.5: 3784 + optionalDependencies: 3785 + '@esbuild/aix-ppc64': 0.21.5 3786 + '@esbuild/android-arm': 0.21.5 3787 + '@esbuild/android-arm64': 0.21.5 3788 + '@esbuild/android-x64': 0.21.5 3789 + '@esbuild/darwin-arm64': 0.21.5 3790 + '@esbuild/darwin-x64': 0.21.5 3791 + '@esbuild/freebsd-arm64': 0.21.5 3792 + '@esbuild/freebsd-x64': 0.21.5 3793 + '@esbuild/linux-arm': 0.21.5 3794 + '@esbuild/linux-arm64': 0.21.5 3795 + '@esbuild/linux-ia32': 0.21.5 3796 + '@esbuild/linux-loong64': 0.21.5 3797 + '@esbuild/linux-mips64el': 0.21.5 3798 + '@esbuild/linux-ppc64': 0.21.5 3799 + '@esbuild/linux-riscv64': 0.21.5 3800 + '@esbuild/linux-s390x': 0.21.5 3801 + '@esbuild/linux-x64': 0.21.5 3802 + '@esbuild/netbsd-x64': 0.21.5 3803 + '@esbuild/openbsd-x64': 0.21.5 3804 + '@esbuild/sunos-x64': 0.21.5 3805 + '@esbuild/win32-arm64': 0.21.5 3806 + '@esbuild/win32-ia32': 0.21.5 3807 + '@esbuild/win32-x64': 0.21.5 3808 + 3809 + escape-string-regexp@4.0.0: {} 3810 + 3811 + eslint-plugin-react-hooks@5.2.0(eslint@9.29.0(jiti@2.4.2)): 3812 + dependencies: 3813 + eslint: 9.29.0(jiti@2.4.2) 3814 + 3815 + eslint-plugin-react-refresh@0.4.20(eslint@9.29.0(jiti@2.4.2)): 3816 + dependencies: 3817 + eslint: 9.29.0(jiti@2.4.2) 3818 + 3819 + eslint-scope@8.4.0: 3820 + dependencies: 3821 + esrecurse: 4.3.0 3822 + estraverse: 5.3.0 3823 + 3824 + eslint-visitor-keys@3.4.3: {} 3825 + 3826 + eslint-visitor-keys@4.2.1: {} 3827 + 3828 + eslint@9.29.0(jiti@2.4.2): 3829 + dependencies: 3830 + '@eslint-community/eslint-utils': 4.7.0(eslint@9.29.0(jiti@2.4.2)) 3831 + '@eslint-community/regexpp': 4.12.1 3832 + '@eslint/config-array': 0.20.1 3833 + '@eslint/config-helpers': 0.2.3 3834 + '@eslint/core': 0.14.0 3835 + '@eslint/eslintrc': 3.3.1 3836 + '@eslint/js': 9.29.0 3837 + '@eslint/plugin-kit': 0.3.2 3838 + '@humanfs/node': 0.16.6 3839 + '@humanwhocodes/module-importer': 1.0.1 3840 + '@humanwhocodes/retry': 0.4.3 3841 + '@types/estree': 1.0.8 3842 + '@types/json-schema': 7.0.15 3843 + ajv: 6.12.6 3844 + chalk: 4.1.2 3845 + cross-spawn: 7.0.6 3846 + debug: 4.4.1 3847 + escape-string-regexp: 4.0.0 3848 + eslint-scope: 8.4.0 3849 + eslint-visitor-keys: 4.2.1 3850 + espree: 10.4.0 3851 + esquery: 1.6.0 3852 + esutils: 2.0.3 3853 + fast-deep-equal: 3.1.3 3854 + file-entry-cache: 8.0.0 3855 + find-up: 5.0.0 3856 + glob-parent: 6.0.2 3857 + ignore: 5.3.2 3858 + imurmurhash: 0.1.4 3859 + is-glob: 4.0.3 3860 + json-stable-stringify-without-jsonify: 1.0.1 3861 + lodash.merge: 4.6.2 3862 + minimatch: 3.1.2 3863 + natural-compare: 1.4.0 3864 + optionator: 0.9.4 3865 + optionalDependencies: 3866 + jiti: 2.4.2 3867 + transitivePeerDependencies: 3868 + - supports-color 3869 + 3870 + espree@10.4.0: 3871 + dependencies: 3872 + acorn: 8.15.0 3873 + acorn-jsx: 5.3.2(acorn@8.15.0) 3874 + eslint-visitor-keys: 4.2.1 3875 + 3876 + esquery@1.6.0: 3877 + dependencies: 3878 + estraverse: 5.3.0 3879 + 3880 + esrecurse@4.3.0: 3881 + dependencies: 3882 + estraverse: 5.3.0 3883 + 3884 + estraverse@5.3.0: {} 3885 + 3886 + esutils@2.0.3: {} 3887 + 3888 + eventemitter3@4.0.7: {} 3889 + 3890 + fast-deep-equal@3.1.3: {} 3891 + 3892 + fast-equals@5.2.2: {} 3893 + 3894 + fast-glob@3.3.3: 3895 + dependencies: 3896 + '@nodelib/fs.stat': 2.0.5 3897 + '@nodelib/fs.walk': 1.2.8 3898 + glob-parent: 5.1.2 3899 + merge2: 1.4.1 3900 + micromatch: 4.0.8 3901 + 3902 + fast-json-stable-stringify@2.1.0: {} 3903 + 3904 + fast-levenshtein@2.0.6: {} 3905 + 3906 + fastq@1.19.1: 3907 + dependencies: 3908 + reusify: 1.1.0 3909 + 3910 + file-entry-cache@8.0.0: 3911 + dependencies: 3912 + flat-cache: 4.0.1 3913 + 3914 + fill-range@7.1.1: 3915 + dependencies: 3916 + to-regex-range: 5.0.1 3917 + 3918 + find-up@5.0.0: 3919 + dependencies: 3920 + locate-path: 6.0.0 3921 + path-exists: 4.0.0 3922 + 3923 + flat-cache@4.0.1: 3924 + dependencies: 3925 + flatted: 3.3.3 3926 + keyv: 4.5.4 3927 + 3928 + flatted@3.3.3: {} 3929 + 3930 + fsevents@2.3.3: 3931 + optional: true 3932 + 3933 + get-nonce@1.0.1: {} 3934 + 3935 + glob-parent@5.1.2: 3936 + dependencies: 3937 + is-glob: 4.0.3 3938 + 3939 + glob-parent@6.0.2: 3940 + dependencies: 3941 + is-glob: 4.0.3 3942 + 3943 + globals@14.0.0: {} 3944 + 3945 + globals@15.15.0: {} 3946 + 3947 + graceful-fs@4.2.11: {} 3948 + 3949 + graphemer@1.4.0: {} 3950 + 3951 + has-flag@4.0.0: {} 3952 + 3953 + html2canvas@1.4.1: 3954 + dependencies: 3955 + css-line-break: 2.1.0 3956 + text-segmentation: 1.0.3 3957 + 3958 + idb-keyval@6.2.2: {} 3959 + 3960 + ignore@5.3.2: {} 3961 + 3962 + ignore@7.0.5: {} 3963 + 3964 + import-fresh@3.3.1: 3965 + dependencies: 3966 + parent-module: 1.0.1 3967 + resolve-from: 4.0.0 3968 + 3969 + imurmurhash@0.1.4: {} 3970 + 3971 + input-otp@1.4.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1): 3972 + dependencies: 3973 + react: 18.3.1 3974 + react-dom: 18.3.1(react@18.3.1) 3975 + 3976 + internmap@2.0.3: {} 3977 + 3978 + is-extglob@2.1.1: {} 3979 + 3980 + is-glob@4.0.3: 3981 + dependencies: 3982 + is-extglob: 2.1.1 3983 + 3984 + is-number@7.0.0: {} 3985 + 3986 + isexe@2.0.0: {} 3987 + 3988 + jiti@2.4.2: {} 3989 + 3990 + js-tokens@4.0.0: {} 3991 + 3992 + js-yaml@4.1.0: 3993 + dependencies: 3994 + argparse: 2.0.1 3995 + 3996 + json-buffer@3.0.1: {} 3997 + 3998 + json-schema-traverse@0.4.1: {} 3999 + 4000 + json-stable-stringify-without-jsonify@1.0.1: {} 4001 + 4002 + keyv@4.5.4: 4003 + dependencies: 4004 + json-buffer: 3.0.1 4005 + 4006 + levn@0.4.1: 4007 + dependencies: 4008 + prelude-ls: 1.2.1 4009 + type-check: 0.4.0 4010 + 4011 + lightningcss-darwin-arm64@1.30.1: 4012 + optional: true 4013 + 4014 + lightningcss-darwin-x64@1.30.1: 4015 + optional: true 4016 + 4017 + lightningcss-freebsd-x64@1.30.1: 4018 + optional: true 4019 + 4020 + lightningcss-linux-arm-gnueabihf@1.30.1: 4021 + optional: true 4022 + 4023 + lightningcss-linux-arm64-gnu@1.30.1: 4024 + optional: true 4025 + 4026 + lightningcss-linux-arm64-musl@1.30.1: 4027 + optional: true 4028 + 4029 + lightningcss-linux-x64-gnu@1.30.1: 4030 + optional: true 4031 + 4032 + lightningcss-linux-x64-musl@1.30.1: 4033 + optional: true 4034 + 4035 + lightningcss-win32-arm64-msvc@1.30.1: 4036 + optional: true 4037 + 4038 + lightningcss-win32-x64-msvc@1.30.1: 4039 + optional: true 4040 + 4041 + lightningcss@1.30.1: 4042 + dependencies: 4043 + detect-libc: 2.0.4 4044 + optionalDependencies: 4045 + lightningcss-darwin-arm64: 1.30.1 4046 + lightningcss-darwin-x64: 1.30.1 4047 + lightningcss-freebsd-x64: 1.30.1 4048 + lightningcss-linux-arm-gnueabihf: 1.30.1 4049 + lightningcss-linux-arm64-gnu: 1.30.1 4050 + lightningcss-linux-arm64-musl: 1.30.1 4051 + lightningcss-linux-x64-gnu: 1.30.1 4052 + lightningcss-linux-x64-musl: 1.30.1 4053 + lightningcss-win32-arm64-msvc: 1.30.1 4054 + lightningcss-win32-x64-msvc: 1.30.1 4055 + 4056 + locate-path@6.0.0: 4057 + dependencies: 4058 + p-locate: 5.0.0 4059 + 4060 + lodash.castarray@4.4.0: {} 4061 + 4062 + lodash.isplainobject@4.0.6: {} 4063 + 4064 + lodash.merge@4.6.2: {} 4065 + 4066 + lodash@4.17.21: {} 4067 + 4068 + loose-envify@1.4.0: 4069 + dependencies: 4070 + js-tokens: 4.0.0 4071 + 4072 + lucide-react@0.462.0(react@18.3.1): 4073 + dependencies: 4074 + react: 18.3.1 4075 + 4076 + magic-string@0.30.17: 4077 + dependencies: 4078 + '@jridgewell/sourcemap-codec': 1.5.0 4079 + 4080 + merge2@1.4.1: {} 4081 + 4082 + micromatch@4.0.8: 4083 + dependencies: 4084 + braces: 3.0.3 4085 + picomatch: 2.3.1 4086 + 4087 + minimatch@3.1.2: 4088 + dependencies: 4089 + brace-expansion: 1.1.12 4090 + 4091 + minimatch@9.0.5: 4092 + dependencies: 4093 + brace-expansion: 2.0.2 4094 + 4095 + minipass@7.1.2: {} 4096 + 4097 + minizlib@3.0.2: 4098 + dependencies: 4099 + minipass: 7.1.2 4100 + 4101 + mkdirp@3.0.1: {} 4102 + 4103 + ms@2.1.3: {} 4104 + 4105 + nanoid@3.3.11: {} 4106 + 4107 + natural-compare@1.4.0: {} 4108 + 4109 + next-themes@0.3.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1): 4110 + dependencies: 4111 + react: 18.3.1 4112 + react-dom: 18.3.1(react@18.3.1) 4113 + 4114 + object-assign@4.1.1: {} 4115 + 4116 + optionator@0.9.4: 4117 + dependencies: 4118 + deep-is: 0.1.4 4119 + fast-levenshtein: 2.0.6 4120 + levn: 0.4.1 4121 + prelude-ls: 1.2.1 4122 + type-check: 0.4.0 4123 + word-wrap: 1.2.5 4124 + 4125 + p-limit@3.1.0: 4126 + dependencies: 4127 + yocto-queue: 0.1.0 4128 + 4129 + p-locate@5.0.0: 4130 + dependencies: 4131 + p-limit: 3.1.0 4132 + 4133 + parent-module@1.0.1: 4134 + dependencies: 4135 + callsites: 3.1.0 4136 + 4137 + path-exists@4.0.0: {} 4138 + 4139 + path-key@3.1.1: {} 4140 + 4141 + picocolors@1.1.1: {} 4142 + 4143 + picomatch@2.3.1: {} 4144 + 4145 + postcss-selector-parser@6.0.10: 4146 + dependencies: 4147 + cssesc: 3.0.0 4148 + util-deprecate: 1.0.2 4149 + 4150 + postcss@8.5.6: 4151 + dependencies: 4152 + nanoid: 3.3.11 4153 + picocolors: 1.1.1 4154 + source-map-js: 1.2.1 4155 + 4156 + prelude-ls@1.2.1: {} 4157 + 4158 + prop-types@15.8.1: 4159 + dependencies: 4160 + loose-envify: 1.4.0 4161 + object-assign: 4.1.1 4162 + react-is: 16.13.1 4163 + 4164 + punycode@2.3.1: {} 4165 + 4166 + queue-microtask@1.2.3: {} 4167 + 4168 + react-day-picker@8.10.1(date-fns@3.6.0)(react@18.3.1): 4169 + dependencies: 4170 + date-fns: 3.6.0 4171 + react: 18.3.1 4172 + 4173 + react-dom@18.3.1(react@18.3.1): 4174 + dependencies: 4175 + loose-envify: 1.4.0 4176 + react: 18.3.1 4177 + scheduler: 0.23.2 4178 + 4179 + react-hook-form@7.58.1(react@18.3.1): 4180 + dependencies: 4181 + react: 18.3.1 4182 + 4183 + react-is@16.13.1: {} 4184 + 4185 + react-is@18.3.1: {} 4186 + 4187 + react-remove-scroll-bar@2.3.8(@types/react@18.3.23)(react@18.3.1): 4188 + dependencies: 4189 + react: 18.3.1 4190 + react-style-singleton: 2.2.3(@types/react@18.3.23)(react@18.3.1) 4191 + tslib: 2.8.1 4192 + optionalDependencies: 4193 + '@types/react': 18.3.23 4194 + 4195 + react-remove-scroll@2.7.1(@types/react@18.3.23)(react@18.3.1): 4196 + dependencies: 4197 + react: 18.3.1 4198 + react-remove-scroll-bar: 2.3.8(@types/react@18.3.23)(react@18.3.1) 4199 + react-style-singleton: 2.2.3(@types/react@18.3.23)(react@18.3.1) 4200 + tslib: 2.8.1 4201 + use-callback-ref: 1.3.3(@types/react@18.3.23)(react@18.3.1) 4202 + use-sidecar: 1.1.3(@types/react@18.3.23)(react@18.3.1) 4203 + optionalDependencies: 4204 + '@types/react': 18.3.23 4205 + 4206 + react-resizable-panels@2.1.9(react-dom@18.3.1(react@18.3.1))(react@18.3.1): 4207 + dependencies: 4208 + react: 18.3.1 4209 + react-dom: 18.3.1(react@18.3.1) 4210 + 4211 + react-router-dom@6.30.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1): 4212 + dependencies: 4213 + '@remix-run/router': 1.23.0 4214 + react: 18.3.1 4215 + react-dom: 18.3.1(react@18.3.1) 4216 + react-router: 6.30.1(react@18.3.1) 4217 + 4218 + react-router@6.30.1(react@18.3.1): 4219 + dependencies: 4220 + '@remix-run/router': 1.23.0 4221 + react: 18.3.1 4222 + 4223 + react-smooth@4.0.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1): 4224 + dependencies: 4225 + fast-equals: 5.2.2 4226 + prop-types: 15.8.1 4227 + react: 18.3.1 4228 + react-dom: 18.3.1(react@18.3.1) 4229 + react-transition-group: 4.4.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1) 4230 + 4231 + react-style-singleton@2.2.3(@types/react@18.3.23)(react@18.3.1): 4232 + dependencies: 4233 + get-nonce: 1.0.1 4234 + react: 18.3.1 4235 + tslib: 2.8.1 4236 + optionalDependencies: 4237 + '@types/react': 18.3.23 4238 + 4239 + react-transition-group@4.4.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1): 4240 + dependencies: 4241 + '@babel/runtime': 7.27.6 4242 + dom-helpers: 5.2.1 4243 + loose-envify: 1.4.0 4244 + prop-types: 15.8.1 4245 + react: 18.3.1 4246 + react-dom: 18.3.1(react@18.3.1) 4247 + 4248 + react@18.3.1: 4249 + dependencies: 4250 + loose-envify: 1.4.0 4251 + 4252 + recharts-scale@0.4.5: 4253 + dependencies: 4254 + decimal.js-light: 2.5.1 4255 + 4256 + recharts@2.15.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1): 4257 + dependencies: 4258 + clsx: 2.1.1 4259 + eventemitter3: 4.0.7 4260 + lodash: 4.17.21 4261 + react: 18.3.1 4262 + react-dom: 18.3.1(react@18.3.1) 4263 + react-is: 18.3.1 4264 + react-smooth: 4.0.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1) 4265 + recharts-scale: 0.4.5 4266 + tiny-invariant: 1.3.3 4267 + victory-vendor: 36.9.2 4268 + 4269 + resolve-from@4.0.0: {} 4270 + 4271 + reusify@1.1.0: {} 4272 + 4273 + rollup@4.44.0: 4274 + dependencies: 4275 + '@types/estree': 1.0.8 4276 + optionalDependencies: 4277 + '@rollup/rollup-android-arm-eabi': 4.44.0 4278 + '@rollup/rollup-android-arm64': 4.44.0 4279 + '@rollup/rollup-darwin-arm64': 4.44.0 4280 + '@rollup/rollup-darwin-x64': 4.44.0 4281 + '@rollup/rollup-freebsd-arm64': 4.44.0 4282 + '@rollup/rollup-freebsd-x64': 4.44.0 4283 + '@rollup/rollup-linux-arm-gnueabihf': 4.44.0 4284 + '@rollup/rollup-linux-arm-musleabihf': 4.44.0 4285 + '@rollup/rollup-linux-arm64-gnu': 4.44.0 4286 + '@rollup/rollup-linux-arm64-musl': 4.44.0 4287 + '@rollup/rollup-linux-loongarch64-gnu': 4.44.0 4288 + '@rollup/rollup-linux-powerpc64le-gnu': 4.44.0 4289 + '@rollup/rollup-linux-riscv64-gnu': 4.44.0 4290 + '@rollup/rollup-linux-riscv64-musl': 4.44.0 4291 + '@rollup/rollup-linux-s390x-gnu': 4.44.0 4292 + '@rollup/rollup-linux-x64-gnu': 4.44.0 4293 + '@rollup/rollup-linux-x64-musl': 4.44.0 4294 + '@rollup/rollup-win32-arm64-msvc': 4.44.0 4295 + '@rollup/rollup-win32-ia32-msvc': 4.44.0 4296 + '@rollup/rollup-win32-x64-msvc': 4.44.0 4297 + fsevents: 2.3.3 4298 + 4299 + run-parallel@1.2.0: 4300 + dependencies: 4301 + queue-microtask: 1.2.3 4302 + 4303 + scheduler@0.23.2: 4304 + dependencies: 4305 + loose-envify: 1.4.0 4306 + 4307 + semver@7.7.2: {} 4308 + 4309 + shebang-command@2.0.0: 4310 + dependencies: 4311 + shebang-regex: 3.0.0 4312 + 4313 + shebang-regex@3.0.0: {} 4314 + 4315 + sonner@1.7.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1): 4316 + dependencies: 4317 + react: 18.3.1 4318 + react-dom: 18.3.1(react@18.3.1) 4319 + 4320 + source-map-js@1.2.1: {} 4321 + 4322 + strip-json-comments@3.1.1: {} 4323 + 4324 + supports-color@7.2.0: 4325 + dependencies: 4326 + has-flag: 4.0.0 4327 + 4328 + tailwind-merge@2.6.0: {} 4329 + 4330 + tailwindcss-animate@1.0.7(tailwindcss@4.1.10): 4331 + dependencies: 4332 + tailwindcss: 4.1.10 4333 + 4334 + tailwindcss@4.1.10: {} 4335 + 4336 + tapable@2.2.2: {} 4337 + 4338 + tar@7.4.3: 4339 + dependencies: 4340 + '@isaacs/fs-minipass': 4.0.1 4341 + chownr: 3.0.0 4342 + minipass: 7.1.2 4343 + minizlib: 3.0.2 4344 + mkdirp: 3.0.1 4345 + yallist: 5.0.0 4346 + 4347 + text-segmentation@1.0.3: 4348 + dependencies: 4349 + utrie: 1.0.2 4350 + 4351 + tiny-invariant@1.3.3: {} 4352 + 4353 + to-regex-range@5.0.1: 4354 + dependencies: 4355 + is-number: 7.0.0 4356 + 4357 + ts-api-utils@2.1.0(typescript@5.8.3): 4358 + dependencies: 4359 + typescript: 5.8.3 4360 + 4361 + tslib@2.8.1: {} 4362 + 4363 + type-check@0.4.0: 4364 + dependencies: 4365 + prelude-ls: 1.2.1 4366 + 4367 + typescript-eslint@8.34.1(eslint@9.29.0(jiti@2.4.2))(typescript@5.8.3): 4368 + dependencies: 4369 + '@typescript-eslint/eslint-plugin': 8.34.1(@typescript-eslint/parser@8.34.1(eslint@9.29.0(jiti@2.4.2))(typescript@5.8.3))(eslint@9.29.0(jiti@2.4.2))(typescript@5.8.3) 4370 + '@typescript-eslint/parser': 8.34.1(eslint@9.29.0(jiti@2.4.2))(typescript@5.8.3) 4371 + '@typescript-eslint/utils': 8.34.1(eslint@9.29.0(jiti@2.4.2))(typescript@5.8.3) 4372 + eslint: 9.29.0(jiti@2.4.2) 4373 + typescript: 5.8.3 4374 + transitivePeerDependencies: 4375 + - supports-color 4376 + 4377 + typescript@5.8.3: {} 4378 + 4379 + undici-types@6.21.0: {} 4380 + 4381 + uri-js@4.4.1: 4382 + dependencies: 4383 + punycode: 2.3.1 4384 + 4385 + use-callback-ref@1.3.3(@types/react@18.3.23)(react@18.3.1): 4386 + dependencies: 4387 + react: 18.3.1 4388 + tslib: 2.8.1 4389 + optionalDependencies: 4390 + '@types/react': 18.3.23 4391 + 4392 + use-sidecar@1.1.3(@types/react@18.3.23)(react@18.3.1): 4393 + dependencies: 4394 + detect-node-es: 1.1.0 4395 + react: 18.3.1 4396 + tslib: 2.8.1 4397 + optionalDependencies: 4398 + '@types/react': 18.3.23 4399 + 4400 + use-sync-external-store@1.5.0(react@18.3.1): 4401 + dependencies: 4402 + react: 18.3.1 4403 + 4404 + util-deprecate@1.0.2: {} 4405 + 4406 + utrie@1.0.2: 4407 + dependencies: 4408 + base64-arraybuffer: 1.0.2 4409 + 4410 + vaul@0.9.9(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1): 4411 + dependencies: 4412 + '@radix-ui/react-dialog': 1.1.14(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) 4413 + react: 18.3.1 4414 + react-dom: 18.3.1(react@18.3.1) 4415 + transitivePeerDependencies: 4416 + - '@types/react' 4417 + - '@types/react-dom' 4418 + 4419 + victory-vendor@36.9.2: 4420 + dependencies: 4421 + '@types/d3-array': 3.2.1 4422 + '@types/d3-ease': 3.0.2 4423 + '@types/d3-interpolate': 3.0.4 4424 + '@types/d3-scale': 4.0.9 4425 + '@types/d3-shape': 3.1.7 4426 + '@types/d3-time': 3.0.4 4427 + '@types/d3-timer': 3.0.2 4428 + d3-array: 3.2.4 4429 + d3-ease: 3.0.1 4430 + d3-interpolate: 3.0.1 4431 + d3-scale: 4.0.2 4432 + d3-shape: 3.2.0 4433 + d3-time: 3.1.0 4434 + d3-timer: 3.0.1 4435 + 4436 + vite@5.4.19(@types/node@22.15.32)(lightningcss@1.30.1): 4437 + dependencies: 4438 + esbuild: 0.21.5 4439 + postcss: 8.5.6 4440 + rollup: 4.44.0 4441 + optionalDependencies: 4442 + '@types/node': 22.15.32 4443 + fsevents: 2.3.3 4444 + lightningcss: 1.30.1 4445 + 4446 + which@2.0.2: 4447 + dependencies: 4448 + isexe: 2.0.0 4449 + 4450 + word-wrap@1.2.5: {} 4451 + 4452 + yallist@5.0.0: {} 4453 + 4454 + yocto-queue@0.1.0: {} 4455 + 4456 + zod@3.25.67: {} 4457 + 4458 + zustand@5.0.5(@types/react@18.3.23)(react@18.3.1)(use-sync-external-store@1.5.0(react@18.3.1)): 4459 + optionalDependencies: 4460 + '@types/react': 18.3.23 4461 + react: 18.3.1 4462 + use-sync-external-store: 1.5.0(react@18.3.1)
+4
pnpm-workspace.yaml
··· 1 + onlyBuiltDependencies: 2 + - '@swc/core' 3 + - '@tailwindcss/oxide' 4 + - esbuild
public/assets/arctic_pulse.webp

This is a binary file and will not be displayed.

public/assets/cosmic_night.png

This is a binary file and will not be displayed.

public/assets/deep_horizon.webp

This is a binary file and will not be displayed.

public/assets/demo.webp

This is a binary file and will not be displayed.

public/assets/mint_breeze.webp

This is a binary file and will not be displayed.

public/assets/molten_dusk.webp

This is a binary file and will not be displayed.

public/assets/monochrome.png

This is a binary file and will not be displayed.

public/assets/neon_heat.webp

This is a binary file and will not be displayed.

public/assets/neon_midnight.webp

This is a binary file and will not be displayed.

public/assets/ocean_breeze.png

This is a binary file and will not be displayed.

public/assets/ocean_glow.webp

This is a binary file and will not be displayed.

public/assets/purple_haze.png

This is a binary file and will not be displayed.

public/assets/purple_magic.png

This is a binary file and will not be displayed.

public/assets/rainbow_dreams.png

This is a binary file and will not be displayed.

public/assets/summer_vibes.png

This is a binary file and will not be displayed.

public/assets/sunset_glow.png

This is a binary file and will not be displayed.

public/assets/twilight_ember.webp

This is a binary file and will not be displayed.

public/assets/upiQR.png

This is a binary file and will not be displayed.

public/assets/warm_embrace.png

This is a binary file and will not be displayed.

+14
public/favicon.svg
··· 1 + <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" 2 + stroke="#96ce8b" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" 3 + class="lucide lucide-hop-icon lucide-hop"> 4 + <path d="M10.82 16.12c1.69.6 3.91.79 5.18.85.55.03 1-.42.97-.97-.06-1.27-.26-3.5-.85-5.18" /> 5 + <path 6 + d="M11.5 6.5c1.64 0 5-.38 6.71-1.07.52-.2.55-.82.12-1.17A10 10 0 0 0 4.26 18.33c.35.43.96.4 1.17-.12.69-1.71 1.07-5.07 1.07-6.71 1.34.45 3.1.9 4.88.62a.88.88 0 0 0 .73-.74c.3-2.14-.15-3.5-.61-4.88" /> 7 + <path d="M15.62 16.95c.2.85.62 2.76.5 4.28a.77.77 0 0 1-.9.7 16.64 16.64 0 0 1-4.08-1.36" /> 8 + <path d="M16.13 21.05c1.65.63 3.68.84 4.87.91a.9.9 0 0 0 .96-.96 17.68 17.68 0 0 0-.9-4.87" /> 9 + <path d="M16.94 15.62c.86.2 2.77.62 4.29.5a.77.77 0 0 0 .7-.9 16.64 16.64 0 0 0-1.36-4.08" /> 10 + <path d="M17.99 5.52a20.82 20.82 0 0 1 3.15 4.5.8.8 0 0 1-.68 1.13c-2.33.2-5.3-.32-8.27-1.57" /> 11 + <path d="M4.93 4.93 3 3a.7.7 0 0 1 0-1" /> 12 + <path 13 + d="M9.58 12.18c1.24 2.98 1.77 5.95 1.57 8.28a.8.8 0 0 1-1.13.68 20.82 20.82 0 0 1-4.5-3.15" /> 14 + </svg>
public/og.webp

This is a binary file and will not be displayed.

+14
public/robots.txt
··· 1 + User-agent: Googlebot 2 + Allow: / 3 + 4 + User-agent: Bingbot 5 + Allow: / 6 + 7 + User-agent: Twitterbot 8 + Allow: / 9 + 10 + User-agent: facebookexternalhit 11 + Allow: / 12 + 13 + User-agent: * 14 + Allow: /
src/App.css

This is a binary file and will not be displayed.

+22
src/App.tsx
··· 1 + import { Toaster } from "@/components/ui/toaster"; 2 + import { Toaster as Sonner } from "@/components/ui/sonner"; 3 + import { TooltipProvider } from "@/components/ui/tooltip"; 4 + import { BrowserRouter, Routes, Route, Navigate } from "react-router-dom"; 5 + import Index from "./pages/Index"; 6 + 7 + const App = () => ( 8 + <TooltipProvider> 9 + <Toaster /> 10 + <Sonner /> 11 + <BrowserRouter> 12 + <Routes> 13 + <Route path="/" element={<Index />} /> 14 + 15 + {/* Send user to home */} 16 + <Route path="*" element={<Navigate to="/"/>} /> 17 + </Routes> 18 + </BrowserRouter> 19 + </TooltipProvider> 20 + ); 21 + 22 + export default App;
+358
src/components/editor/Backgrounds.tsx
··· 1 + import React from 'react'; 2 + import { useMobile } from '@/hooks/use-mobile'; 3 + import { Sheet, SheetContent } from '@/components/ui/sheet'; 4 + import { Button } from '@/components/ui/button'; 5 + import { ChevronUp, Image as ImageIcon, Clipboard } from 'lucide-react'; 6 + import { useMockupStore } from '@/contexts/MockupContext'; 7 + import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogTrigger } from '@/components/ui/dialog'; 8 + import { toast } from 'sonner'; 9 + import { DialogDescription } from '@radix-ui/react-dialog'; 10 + 11 + interface CustomBackground { 12 + name: string; 13 + image: string; // Base64 string 14 + } 15 + 16 + export const Backgrounds: React.FC = () => { 17 + const { 18 + backgroundImage, 19 + setBackgroundType, 20 + setGradientColors, 21 + setGradientDirection, 22 + setBackgroundImage, 23 + customBackgrounds, 24 + addCustomBackground, 25 + } = useMockupStore(); 26 + 27 + const isMobile = useMobile(); 28 + const [isOpen, setIsOpen] = React.useState(false); 29 + const [isDialogOpen, setIsDialogOpen] = React.useState(false); 30 + const [isDragOver, setIsDragOver] = React.useState(false); 31 + const fileInputRef = React.useRef<HTMLInputElement>(null); 32 + 33 + const MAX_FILE_SIZE = 10 * 1024 * 1024; // 10MB 34 + 35 + const gradientPresets = [ 36 + { 37 + name: 'Deep Horizon', 38 + colors: ['#141e30', '#243b55'], 39 + direction: 'to-tl', 40 + image: '/assets/deep_horizon.webp' 41 + }, 42 + 43 + { 44 + name: 'Ocean Glow', 45 + colors: ['#56ccf2', '#2f80ed'], 46 + direction: 'to-r', 47 + image: '/assets/ocean_glow.webp' 48 + }, 49 + { 50 + name: 'Ocean Breeze', 51 + colors: ['#ff9a9e', '#fecfef'], 52 + direction: 'to-r', 53 + image: '/assets/ocean_breeze.png' 54 + }, 55 + { 56 + name: 'Purple Haze', 57 + colors: ['#c471ed', '#f64f59'], 58 + direction: 'to-r', 59 + image: '/assets/purple_haze.png' 60 + }, 61 + { 62 + name: 'Summer Vibes', 63 + colors: ['#56ab2f', '#a8e6cf'], 64 + direction: 'to-tr', 65 + image: '/assets/summer_vibes.png' 66 + }, 67 + { 68 + name: 'Rainbow Dreams', 69 + colors: ['#ff6b6b', '#4ecdc4'], 70 + direction: 'to-br', 71 + image: '/assets/rainbow_dreams.png' 72 + }, 73 + { 74 + name: 'Neon Heat', 75 + colors: ['#ff0844', '#ffb199'], 76 + direction: 'to-br', 77 + image: '/assets/neon_heat.webp' 78 + }, 79 + { 80 + name: 'Purple Magic', 81 + colors: ['#667eea', '#764ba2'], 82 + direction: 'to-br', 83 + image: '/assets/purple_magic.png' 84 + }, 85 + { 86 + name: 'Sunset Glow', 87 + colors: ['#ff9a56', '#ff6b9d'], 88 + direction: 'to-r', 89 + image: '/assets/sunset_glow.png' 90 + }, 91 + { 92 + name: 'Warm Embrace', 93 + colors: ['#ff9472', '#f2d388'], 94 + direction: 'to-tr', 95 + image: '/assets/warm_embrace.png' 96 + }, 97 + { 98 + name: 'Cosmic Night', 99 + colors: ['#667eea', '#764ba2'], 100 + direction: 'to-br', 101 + image: '/assets/cosmic_night.png' 102 + }, 103 + { 104 + name: 'Mint Breeze', 105 + colors: ['#a8edea', '#fed6e3'], 106 + direction: 'to-tr', 107 + image: '/assets/mint_breeze.webp' 108 + }, 109 + { 110 + name: 'Neon Midnight', 111 + colors: ['#c471ed', '#f64f59'], 112 + direction: 'to-r', 113 + image: '/assets/neon_midnight.webp' 114 + }, 115 + { 116 + name: 'Monochrome', 117 + colors: ['#2c3e50', '#34495e'], 118 + direction: 'to-br', 119 + image: '/assets/monochrome.png' 120 + }, 121 + { 122 + name: 'Arctic Pulse', 123 + colors: ['#cce3df', '#3a6c7a', '#0e1a1f'], 124 + direction: 'to-r', 125 + image: '/assets/arctic_pulse.webp' 126 + }, 127 + { 128 + name: 'Molten Dusk', 129 + colors: ['#f0e7da', '#f857a6', '#2c2c2c'], 130 + direction: 'to-l', 131 + image: '/assets/molten_dusk.webp' 132 + }, 133 + { 134 + name: 'Twilight Ember', 135 + colors: ['#ffb88c', '#ea5753', '#111d2f'], 136 + direction: 'to-br', 137 + image: '/assets/twilight_ember.webp' 138 + } 139 + ]; 140 + 141 + const handleGradientSelect = (gradient: typeof gradientPresets[0] | CustomBackground) => { 142 + setBackgroundType('pattern'); 143 + if ('colors' in gradient && 'direction' in gradient) { 144 + setGradientColors(gradient.colors); 145 + setGradientDirection(gradient.direction); 146 + } else { 147 + setGradientColors(['#ffffff', '#ffffff']); 148 + setGradientDirection('to-r'); 149 + } 150 + setBackgroundImage(gradient.image); 151 + if (isMobile) setIsOpen(false); 152 + }; 153 + 154 + const handleImageUpload = (file: File) => { 155 + if (!['image/jpeg', 'image/png'].includes(file.type)) { 156 + toast.error('Invalid file format! Please use JPG, PNG.'); 157 + setIsDragOver(false); 158 + setIsDialogOpen(false); 159 + return; 160 + } 161 + 162 + if (file.size > MAX_FILE_SIZE) { 163 + toast.error('File too large! Maximum size is 10MB.'); 164 + setIsDragOver(false); 165 + setIsDialogOpen(false); 166 + return; 167 + } 168 + 169 + const reader = new FileReader(); 170 + reader.onload = (e) => { 171 + const base64Image = e.target?.result as string; 172 + if (base64Image) { 173 + const newBackground: CustomBackground = { 174 + name: `Custom ${customBackgrounds.length + 1}`, 175 + image: base64Image 176 + }; 177 + addCustomBackground(newBackground); 178 + handleGradientSelect(newBackground); 179 + toast('Background added successfully!'); 180 + setTimeout(() => setIsDialogOpen(false), 300); // Delay for toast visibility 181 + setIsDragOver(false); 182 + } 183 + }; 184 + reader.onerror = () => { 185 + toast.error('Failed to read the file. Please try again.'); 186 + setIsDragOver(false); 187 + setIsDialogOpen(false); 188 + }; 189 + reader.readAsDataURL(file); 190 + }; 191 + 192 + const handleDrop = (e: React.DragEvent<HTMLDivElement>) => { 193 + e.preventDefault(); 194 + setIsDragOver(false); 195 + const file = e.dataTransfer.files[0]; 196 + if (file) { 197 + handleImageUpload(file); 198 + } else { 199 + toast.error('No valid file dropped.'); 200 + setIsDialogOpen(false); 201 + } 202 + }; 203 + 204 + const handleFileInput = (e: React.ChangeEvent<HTMLInputElement>) => { 205 + const file = e.target.files?.[0]; 206 + if (file) { 207 + handleImageUpload(file); 208 + } else { 209 + toast.error('No file selected.'); 210 + setIsDialogOpen(false); 211 + } 212 + }; 213 + 214 + const handlePaste = (e: React.ClipboardEvent) => { 215 + const file = e.clipboardData.files[0]; 216 + if (file) { 217 + handleImageUpload(file); 218 + } else { 219 + toast.error('No valid image pasted.'); 220 + setIsDialogOpen(false); 221 + } 222 + }; 223 + 224 + const getDropZoneClasses = () => { 225 + const baseClasses = "relative transition-all duration-300 cursor-pointer border-2 border-dashed rounded-xl bg-primary/20"; 226 + const hoverClasses = "hover:bg-primary/10"; 227 + const dragOverClasses = isDragOver ? "scale-105 border-primary" : "border-gray-400"; 228 + return `${baseClasses} ${hoverClasses} ${dragOverClasses}`; 229 + }; 230 + 231 + const GradientGrid = () => ( 232 + <div className="space-y-6"> 233 + <div className="bg-background/80"> 234 + <div className="pb-4"> 235 + <h3 className="text-sidebar-foreground text-md font-medium">Background Gradients</h3> 236 + </div> 237 + <div className="pt-0"> 238 + <div className={`grid ${isMobile ? 'grid-cols-2' : 'grid-cols-1'} gap-4`}> 239 + <Dialog open={isDialogOpen} onOpenChange={setIsDialogOpen}> 240 + <DialogTrigger asChild> 241 + <button 242 + className="h-16 rounded transition-all duration-75 relative overflow-hidden cursor-pointer border-2 border-dashed border-gray-400 hover:bg-primary/10" 243 + > 244 + <span className="text-sm text-white font-medium drop-shadow-lg relative z-10 px-3 py-2 rounded"> 245 + Custom Background 246 + </span> 247 + </button> 248 + </DialogTrigger> 249 + <DialogContent className="sm:max-w-lg" onPaste={handlePaste}> 250 + <DialogHeader> 251 + <DialogTitle>Upload Custom Background</DialogTitle> 252 + <DialogDescription> 253 + custom background to personalise your creation. 254 + </DialogDescription> 255 + </DialogHeader> 256 + <div 257 + className={getDropZoneClasses()} 258 + style={{ 259 + width: '100%', 260 + height: '300px' 261 + }} 262 + onDrop={handleDrop} 263 + onDragOver={(e) => { 264 + e.preventDefault(); 265 + setIsDragOver(true); 266 + }} 267 + onDragLeave={() => setIsDragOver(false)} 268 + onClick={() => fileInputRef.current?.click()} 269 + > 270 + <div className="w-full h-full flex flex-col items-center justify-center"> 271 + <ImageIcon 272 + size={isMobile ? 40 : 48} 273 + className="text-gray-100 mb-4" 274 + /> 275 + <div className="text-center text-white px-4"> 276 + <p className={`font-semibold mb-1 ${isMobile ? 'text-base' : 'text-lg'}`}> 277 + Drop image here or click to upload 278 + </p> 279 + <p className={`text-white mb-2 ${isMobile ? 'text-xs' : 'text-sm'}`}> 280 + Supports JPG, PNG 281 + </p> 282 + 283 + </div> 284 + </div> 285 + {isDragOver && ( 286 + <div className="absolute inset-0 bg-primary/20 rounded-xl flex items-center justify-center"> 287 + <div className={`text-primary font-semibold bg-white/90 px-6 py-3 rounded-lg ${isMobile ? 'text-sm' : 'text-base'}`}> 288 + Drop image here 289 + </div> 290 + </div> 291 + )} 292 + </div> 293 + <input 294 + type="file" 295 + accept="image/jpeg,image/png" 296 + onChange={handleFileInput} 297 + className="hidden" 298 + ref={fileInputRef} 299 + /> 300 + </DialogContent> 301 + </Dialog> 302 + {[...customBackgrounds, ...gradientPresets].map((gradient, index) => ( 303 + <button 304 + key={gradient.name + index} 305 + onClick={() => handleGradientSelect(gradient)} 306 + className={`h-16 rounded transition-all duration-200 relative overflow-hidden cursor-pointer 307 + ${backgroundImage === gradient.image 308 + ? 'ring-2 ring-primary scale-105' 309 + : 'hover:scale-102' 310 + }`} 311 + style={{ 312 + backgroundImage: `url(${gradient.image})`, 313 + backgroundSize: 'cover', 314 + backgroundPosition: 'center' 315 + }} 316 + > 317 + <span className="text-sm text-white font-medium drop-shadow-lg relative z-10 bg-black/50 px-3 py-2 rounded"> 318 + {gradient.name} 319 + </span> 320 + </button> 321 + ))} 322 + </div> 323 + </div> 324 + </div> 325 + </div> 326 + ); 327 + 328 + if (isMobile) { 329 + return ( 330 + <> 331 + <Button 332 + variant="outline" 333 + className="fixed bottom-24 right-4 z-50 rounded-full bg-sidebar border-primary" 334 + onClick={() => setIsOpen(true)} 335 + > 336 + <ChevronUp className="w-6 h-6" /> 337 + Background Gradients 338 + </Button> 339 + <Sheet open={isOpen} onOpenChange={setIsOpen}> 340 + <SheetContent side="bottom" className="h-[80vh] bg-sidebar border-t border-sidebar-border [&>button:first-of-type]:hidden"> 341 + <div className="flex items-center justify-center"> 342 + <div className="h-2 w-16 bg-gradient-to-br from-gray-100 to-gray-400 inset-shadow-lg rounded-full"></div> 343 + </div> 344 + <div className="p-6 overflow-y-auto h-full"> 345 + <GradientGrid /> 346 + </div> 347 + </SheetContent> 348 + </Sheet> 349 + </> 350 + ); 351 + } 352 + 353 + return ( 354 + <div className="w-80 bg-background/80 p-6 overflow-y-auto"> 355 + <GradientGrid /> 356 + </div> 357 + ); 358 + };
+536
src/components/editor/Canvas.tsx
··· 1 + import React, { useRef, useState, useEffect } from 'react'; 2 + import { toast } from 'sonner'; 3 + import { extractDominantColor } from '@/utils/colorExtractor'; 4 + import { ImageIcon, Clipboard } from 'lucide-react'; 5 + import { useMockupStore } from '@/contexts/MockupContext'; 6 + import { Button } from '@/components/ui/button'; 7 + 8 + // Responsive configuration 9 + const getResponsiveConfig = () => { 10 + const viewportWidth = window.innerWidth; 11 + const viewportHeight = window.innerHeight; 12 + const isMobile = viewportWidth < 768; 13 + const isTablet = viewportWidth >= 768 && viewportWidth < 1024; 14 + const basePadding = isMobile ? 40 : isTablet ? 100 : 200; 15 + const scaleMultiplier = isMobile ? 0.95 : isTablet ? 0.85 : 0.8; 16 + const maxWidth = isMobile ? 1000 : isTablet ? 1100 : 1200; 17 + const maxHeight = isMobile ? 700 : isTablet ? 750 : 800; 18 + const availableWidth = viewportWidth - basePadding; 19 + const availableHeight = viewportHeight - basePadding; 20 + const maxContainerWidth = Math.min(availableWidth * scaleMultiplier, maxWidth); 21 + const maxContainerHeight = Math.min(availableHeight * scaleMultiplier, maxHeight); 22 + return { 23 + isMobile, 24 + isTablet, 25 + basePadding, 26 + scaleMultiplier, 27 + maxContainerWidth, 28 + maxContainerHeight, 29 + dropZoneWidth: isMobile ? '95%' : isTablet ? '70%' : '60%', 30 + dropZoneHeight: isMobile ? '60%' : '50%', 31 + }; 32 + }; 33 + 34 + // Smart image optimization utility - only compress if needed 35 + const optimizeImage = (file: File): Promise<string> => { 36 + return new Promise((resolve, reject) => { 37 + if (file.size < 2 * 1024 * 1024) { 38 + const reader = new FileReader(); 39 + reader.onload = (e) => resolve(e.target?.result as string); 40 + reader.onerror = reject; 41 + reader.readAsDataURL(file); 42 + return; 43 + } 44 + const canvas = document.createElement('canvas'); 45 + const ctx = canvas.getContext('2d'); 46 + const img = new Image(); 47 + img.onload = () => { 48 + const MAX_WIDTH = 2400; 49 + const MAX_HEIGHT = 1800; 50 + let { width, height } = img; 51 + if (width > MAX_WIDTH || height > MAX_HEIGHT) { 52 + const ratio = Math.min(MAX_WIDTH / width, MAX_HEIGHT / height); 53 + width = Math.round(width * ratio); 54 + height = Math.round(height * ratio); 55 + } 56 + canvas.width = width; 57 + canvas.height = height; 58 + ctx?.drawImage(img, 0, 0, width, height); 59 + let optimizedDataUrl; 60 + try { 61 + optimizedDataUrl = canvas.toDataURL('image/png'); 62 + if (optimizedDataUrl.length > 5 * 1024 * 1024) { 63 + optimizedDataUrl = canvas.toDataURL('image/jpeg', 0.92); 64 + } 65 + } catch (e) { 66 + optimizedDataUrl = canvas.toDataURL('image/jpeg', 0.92); 67 + } 68 + resolve(optimizedDataUrl); 69 + }; 70 + img.onerror = reject; 71 + img.src = URL.createObjectURL(file); 72 + }); 73 + }; 74 + 75 + // Fetch demo image as a File object 76 + const fetchDemoImage = async (): Promise<File> => { 77 + try { 78 + const response = await fetch('/assets/demo.webp'); 79 + if (!response.ok) throw new Error('Failed to fetch demo image'); 80 + const blob = await response.blob(); 81 + return new File([blob], 'demo.webp', { type: 'image/webp' }); 82 + } catch (error) { 83 + throw new Error('Error fetching demo image: ' + error); 84 + } 85 + }; 86 + 87 + export const Canvas: React.FC = () => { 88 + const { 89 + uploadedImage, 90 + backgroundType, 91 + backgroundImage, 92 + backgroundColor, 93 + gradientDirection, 94 + gradientColors, 95 + devicePosition, 96 + rotation3D, 97 + imageBorder, 98 + margin, 99 + fixedMargin, 100 + setUploadedImage, 101 + setImageBorder, 102 + } = useMockupStore(); 103 + 104 + const fileInputRef = useRef<HTMLInputElement>(null); 105 + const [isDragOver, setIsDragOver] = useState(false); 106 + const [imageDimensions, setImageDimensions] = useState<{ width: number; height: number } | null>(null); 107 + const [responsiveConfig, setResponsiveConfig] = useState(getResponsiveConfig()); 108 + const [showPasteHint, setShowPasteHint] = useState(false); 109 + 110 + // Update responsive config on window resize 111 + useEffect(() => { 112 + const handleResize = () => { 113 + setResponsiveConfig(getResponsiveConfig()); 114 + }; 115 + window.addEventListener('resize', handleResize); 116 + return () => window.removeEventListener('resize', handleResize); 117 + }, []); 118 + 119 + // Clipboard paste functionality 120 + useEffect(() => { 121 + const handlePaste = async (e: ClipboardEvent) => { 122 + const items = e.clipboardData?.items; 123 + if (!items) return; 124 + for (const item of Array.from(items)) { 125 + if (item.type.startsWith('image/')) { 126 + const file = item.getAsFile(); 127 + if (file) { 128 + if (uploadedImage) { 129 + setShowPasteHint(true); 130 + toast('Image in clipboard detected! Clear current image to paste new one.', { 131 + duration: 3000, 132 + action: { 133 + label: 'Clear & Paste', 134 + onClick: () => { 135 + setUploadedImage(null); 136 + localStorage.removeItem('demoImage'); 137 + setTimeout(() => handleImageUpload(file), 100); 138 + }, 139 + }, 140 + }); 141 + setTimeout(() => setShowPasteHint(false), 3000); 142 + } else { 143 + await handleImageUpload(file); 144 + localStorage.removeItem('demoImage'); 145 + toast('Image pasted successfully!'); 146 + } 147 + } 148 + break; 149 + } 150 + } 151 + }; 152 + document.addEventListener('paste', handlePaste); 153 + return () => document.removeEventListener('paste', handlePaste); 154 + }, [uploadedImage, setUploadedImage]); 155 + 156 + // Load demo image from local storage on mount 157 + // useEffect(() => { 158 + // const savedDemoImage = localStorage.getItem('demoImage'); 159 + // if (savedDemoImage === '/assets/demo.webp') { 160 + // handleDemoImage(); 161 + // } 162 + // }, [setUploadedImage]); 163 + 164 + const handleDrop = (e: React.DragEvent) => { 165 + e.preventDefault(); 166 + e.stopPropagation(); 167 + setIsDragOver(false); 168 + const files = Array.from(e.dataTransfer.files); 169 + const imageFile = files.find((file) => file.type.startsWith('image/')); 170 + if (imageFile) { 171 + localStorage.removeItem('demoImage'); 172 + handleImageUpload(imageFile); 173 + } 174 + }; 175 + 176 + const handleDragOver = (e: React.DragEvent) => { 177 + e.preventDefault(); 178 + e.stopPropagation(); 179 + setIsDragOver(true); 180 + }; 181 + 182 + const hexToRgb = (hex: string): { r: number; g: number; b: number } => { 183 + const r = parseInt(hex.slice(1, 3), 16); 184 + const g = parseInt(hex.slice(3, 5), 16); 185 + const b = parseInt(hex.slice(5, 7), 16); 186 + return { r, g, b }; 187 + }; 188 + 189 + const handleImageUpload = async (file: File) => { 190 + let loadingToast: string | number | undefined; 191 + if (file.size > 1024 * 1024) { 192 + loadingToast = toast('Processing image...', { duration: Infinity }); 193 + } 194 + try { 195 + const optimizedDataUrl = await optimizeImage(file); 196 + setUploadedImage(optimizedDataUrl); 197 + requestAnimationFrame(async () => { 198 + try { 199 + const dominantColor = await extractDominantColor(optimizedDataUrl); 200 + const validHex = /^#[0-9A-Fa-f]{6}$/.test(dominantColor) ? dominantColor : '#9CA389'; 201 + const { r, g, b } = hexToRgb(validHex); 202 + const borderWidth = responsiveConfig.isMobile ? 4 : 8; 203 + const initialOpacity = 0.5; 204 + setImageBorder({ 205 + enabled: true, 206 + width: borderWidth, 207 + color: `rgba(${r}, ${g}, ${b}, ${initialOpacity})`, 208 + shadow: `rgba(0, 0, 0, 0.16) 0px 3px 6px, rgba(0, 0, 0, 0.23) 0px 3px 6px`, 209 + }); 210 + if (loadingToast) { 211 + toast.dismiss(loadingToast); 212 + } 213 + toast('Image uploaded with transparent border!'); 214 + } catch (error) { 215 + const borderWidth = responsiveConfig.isMobile ? 4 : 8; 216 + const initialOpacity = 0.5; 217 + setImageBorder({ 218 + enabled: true, 219 + width: borderWidth, 220 + color: `rgba(156, 163, 137, ${initialOpacity})`, 221 + shadow: `rgba(0, 0, 0, 0.16) 0px 3px 6px, rgba(0, 0, 0, 0.23) 0px 3px 6px`, 222 + }); 223 + if (loadingToast) { 224 + toast.dismiss(loadingToast); 225 + } 226 + toast('Image uploaded with default transparent border!'); 227 + } 228 + }); 229 + } catch (error) { 230 + const reader = new FileReader(); 231 + reader.onload = async (e) => { 232 + const result = e.target?.result as string; 233 + setUploadedImage(result); 234 + try { 235 + const dominantColor = await extractDominantColor(result); 236 + const validHex = /^#[0-9A-Fa-f]{6}$/.test(dominantColor) ? dominantColor : '#9CA389'; 237 + const { r, g, b } = hexToRgb(validHex); 238 + const borderWidth = responsiveConfig.isMobile ? 4 : 8; 239 + const initialOpacity = 0.5; 240 + setImageBorder({ 241 + enabled: true, 242 + width: borderWidth, 243 + color: `rgba(${r}, ${g}, ${b}, ${initialOpacity})`, 244 + shadow: `rgba(0, 0, 0, 0.16) 0px 3px 6px, rgba(0, 0, 0, 0.23) 0px 3px 6px`, 245 + }); 246 + toast('Image uploaded with transparent border!'); 247 + } catch (error) { 248 + const borderWidth = responsiveConfig.isMobile ? 4 : 8; 249 + const initialOpacity = 0.5; 250 + setImageBorder({ 251 + enabled: true, 252 + width: borderWidth, 253 + color: `rgba(156, 163, 137, ${initialOpacity})`, 254 + shadow: `rgba(0, 0, 0, 0.16) 0px 3px 6px, rgba(0, 0, 0, 0.23) 0px 3px 6px`, 255 + }); 256 + toast('Image uploaded with default transparent border!'); 257 + } 258 + }; 259 + reader.readAsDataURL(file); 260 + if (loadingToast) { 261 + toast.dismiss(loadingToast); 262 + } 263 + console.error('Image optimization failed, using fallback:', error); 264 + } 265 + }; 266 + 267 + const handleFileSelect = (e: React.ChangeEvent<HTMLInputElement>) => { 268 + const file = e.target.files?.[0]; 269 + if (file) { 270 + localStorage.removeItem('demoImage'); 271 + handleImageUpload(file); 272 + } 273 + }; 274 + 275 + const handleDemoImage = async (e?: React.MouseEvent<HTMLButtonElement>) => { 276 + try { 277 + e?.stopPropagation(); 278 + const demoFile = await fetchDemoImage(); 279 + await handleImageUpload(demoFile); 280 + localStorage.setItem('demoImage', '/assets/demo.webp'); 281 + toast('Demo image applied successfully!'); 282 + } catch (error) { 283 + toast.error('Failed to load demo image.'); 284 + console.error(error); 285 + } 286 + }; 287 + 288 + // Calculate image dimensions with responsive scaling 289 + useEffect(() => { 290 + if (uploadedImage) { 291 + const img = new Image(); 292 + img.onload = () => { 293 + const { maxContainerWidth, maxContainerHeight } = responsiveConfig; 294 + const originalWidth = img.width; 295 + const originalHeight = img.height; 296 + const scaleX = maxContainerWidth / originalWidth; 297 + const scaleY = maxContainerHeight / originalHeight; 298 + const optimalScale = Math.min(scaleX, scaleY, 1); 299 + const scaledWidth = originalWidth * optimalScale; 300 + const scaledHeight = originalHeight * optimalScale; 301 + setImageDimensions({ 302 + width: Math.round(scaledWidth), 303 + height: Math.round(scaledHeight), 304 + }); 305 + }; 306 + img.src = uploadedImage; 307 + } else { 308 + setImageDimensions(null); 309 + } 310 + }, [uploadedImage, responsiveConfig]); 311 + 312 + // Calculate canvas style based on fixed margin setting 313 + const getCanvasStyle = () => { 314 + if (!fixedMargin || !imageDimensions || !uploadedImage) { 315 + // Default behavior - canvas takes full flex-1 space 316 + return { 317 + width: '100%', 318 + height: '100%', 319 + }; 320 + } 321 + 322 + // Fixed margin behavior - shrink canvas to image + margin 323 + const totalWidth = imageDimensions.width + margin.left + margin.right; 324 + const totalHeight = imageDimensions.height + margin.top + margin.bottom; 325 + 326 + return { 327 + width: `${totalWidth}px`, 328 + height: `${totalHeight}px`, 329 + }; 330 + }; 331 + 332 + const getBackgroundStyle = () => { 333 + const baseStyle: React.CSSProperties = {}; 334 + 335 + if (backgroundType === 'pattern' && backgroundImage) { 336 + baseStyle.backgroundImage = `url(${backgroundImage})`; 337 + baseStyle.backgroundSize = 'cover'; 338 + baseStyle.backgroundPosition = 'center'; 339 + baseStyle.backgroundRepeat = 'no-repeat'; 340 + } else if (backgroundType === 'gradient') { 341 + const direction = gradientDirection.replace('to-', ''); 342 + const degreeMap: { [key: string]: string } = { 343 + r: '90deg', 344 + br: '135deg', 345 + b: '180deg', 346 + bl: '225deg', 347 + l: '270deg', 348 + tl: '315deg', 349 + t: '0deg', 350 + tr: '45deg', 351 + }; 352 + const angle = degreeMap[direction] || '135deg'; 353 + baseStyle.background = `linear-gradient(${angle}, ${gradientColors.join(', ')})`; 354 + } else { 355 + baseStyle.backgroundColor = backgroundColor; 356 + } 357 + 358 + if (fixedMargin && imageDimensions && uploadedImage) { 359 + // When fixed margin is enabled, background takes full canvas size 360 + baseStyle.width = '100%'; 361 + baseStyle.height = '100%'; 362 + } else { 363 + // Default behavior - background fills the entire flex space 364 + baseStyle.width = '100%'; 365 + baseStyle.height = '100%'; 366 + } 367 + 368 + return baseStyle; 369 + }; 370 + 371 + const getImageContainerStyle = () => { 372 + if (!fixedMargin || !imageDimensions || !uploadedImage) { 373 + // Default behavior - center the image 374 + return { 375 + display: 'flex', 376 + alignItems: 'center', 377 + justifyContent: 'center', 378 + width: '100%', 379 + height: '100%', 380 + }; 381 + } 382 + 383 + // Fixed margin behavior - position image with exact margins 384 + return { 385 + position: 'absolute' as const, 386 + top: `${margin.top}px`, 387 + right: `${margin.right}px`, 388 + bottom: `${margin.bottom}px`, 389 + left: `${margin.left}px`, 390 + display: 'flex', 391 + alignItems: 'center', 392 + justifyContent: 'center', 393 + }; 394 + }; 395 + 396 + const getImageStyle = () => { 397 + const dimensions = imageDimensions || { width: 400, height: 300 }; 398 + const baseStyles = { 399 + width: `${dimensions.width}px`, 400 + height: `${dimensions.height}px`, 401 + transform: ` 402 + translate(${devicePosition.x}px, ${devicePosition.y}px) 403 + scale(${devicePosition.scale}) 404 + rotate(${devicePosition.rotation}deg) 405 + rotateX(${rotation3D.rotateX}deg) 406 + rotateY(${rotation3D.rotateY}deg) 407 + rotateZ(${rotation3D.rotateZ}deg) 408 + skew(${rotation3D.skew}deg) 409 + `, 410 + transformOrigin: 'center center', 411 + transformStyle: 'preserve-3d' as const, 412 + }; 413 + 414 + if (imageBorder.enabled) { 415 + return { 416 + ...baseStyles, 417 + border: `${imageBorder.width}px solid ${imageBorder.color}`, 418 + borderRadius: `${imageBorder.radius}px`, 419 + boxShadow: imageBorder.shadow, 420 + }; 421 + } 422 + return baseStyles; 423 + }; 424 + 425 + const getDropZoneClasses = () => { 426 + const baseClasses = 'relative transition-all duration-300 cursor-pointer border-2 border-dashed rounded-xl bg-gray-900/50 mx-auto top-[15%] md:top-[25%]'; 427 + const hoverClasses = 'hover:bg-gray-900/70'; 428 + const dragOverClasses = isDragOver ? 'scale-105 border-primary' : 'border-gray-400'; 429 + return `${baseClasses} ${hoverClasses} ${dragOverClasses}`; 430 + }; 431 + 432 + const getDropZoneOverlayText = () => { 433 + const primaryText = uploadedImage ? 'Drop to replace image' : 'Drop image here'; 434 + const iconSize = responsiveConfig.isMobile ? 32 : 48; 435 + const textSize = responsiveConfig.isMobile ? 'text-base' : 'text-xl'; 436 + return { primaryText, iconSize, textSize }; 437 + }; 438 + 439 + return ( 440 + <div 441 + className="flex-1 flex items-center justify-center relative transition-all duration-300" 442 + style={fixedMargin && imageDimensions && uploadedImage ? { backgroundColor: 'black' } : {}} 443 + > 444 + <div 445 + className="transition-all duration-300 relative overflow-hidden" 446 + style={getCanvasStyle()} 447 + data-mockup-canvas 448 + onDrop={uploadedImage ? handleDrop : undefined} 449 + onDragOver={uploadedImage ? handleDragOver : undefined} 450 + > 451 + <div 452 + className="transition-all duration-300 relative" 453 + style={getBackgroundStyle()} 454 + > 455 + {uploadedImage ? ( 456 + <div style={getImageContainerStyle()}> 457 + <div className="relative"> 458 + <img 459 + src={uploadedImage} 460 + alt="Uploaded mockup" 461 + className="relative size-full transition-all duration-300 will-transform select-none" 462 + style={getImageStyle()} 463 + crossOrigin="anonymous" 464 + onDragOver={(e) => e.stopPropagation()} 465 + /> 466 + {isDragOver && ( 467 + <div className="absolute inset-0 bg-primary/30 rounded-xl flex items-center justify-center z-10"> 468 + <div 469 + className={`text-primary font-semibold bg-white/90 px-6 py-3 rounded-lg ${getDropZoneOverlayText().textSize}`} 470 + > 471 + {getDropZoneOverlayText().primaryText} 472 + </div> 473 + </div> 474 + )} 475 + {showPasteHint && ( 476 + <div className="absolute inset-0 bg-blue-500/20 rounded-xl flex items-center justify-center z-20 animate-pulse"> 477 + <div className="text-blue-600 font-semibold bg-white/95 px-6 py-3 rounded-lg text-center shadow-lg"> 478 + <Clipboard className="inline-block mr-2 h-5 w-5" /> 479 + Image ready to paste! 480 + </div> 481 + </div> 482 + )} 483 + </div> 484 + </div> 485 + ) : ( 486 + <div 487 + className={getDropZoneClasses()} 488 + style={{ 489 + width: responsiveConfig.dropZoneWidth, 490 + height: responsiveConfig.dropZoneHeight, 491 + }} 492 + onDrop={handleDrop} 493 + onDragOver={handleDragOver} 494 + onClick={() => fileInputRef.current?.click()} 495 + > 496 + <div className="absolute inset-0 flex flex-col items-center justify-center "> 497 + <ImageIcon size={getDropZoneOverlayText().iconSize} className="text-gray-100 mb-4" /> 498 + <div className="text-center text-white px-4"> 499 + <p className={`font-semibold mb-1 ${responsiveConfig.isMobile ? 'text-base' : 'text-lg'}`}> 500 + Drop image here or click to upload 501 + </p> 502 + <p className={`text-white/70 mb-2 ${responsiveConfig.isMobile ? 'text-xs' : 'text-sm'}`}> 503 + Supports JPG, PNG 504 + </p> 505 + <div className="flex items-center justify-center gap-2 text-white font-semibold"> 506 + <Clipboard className="h-4 w-4" strokeWidth={2.5} /> 507 + <span className={`${responsiveConfig.isMobile ? 'text-xs' : 'text-sm'} text-white/70`}> 508 + Or paste image (Ctrl+V) 509 + </span> 510 + </div> 511 + </div> 512 + <Button 513 + variant="outline" 514 + className="mt-6 z-50 rounded-full bg-gray-900/50 hover:bg-gray-900 border-2 border-primary" 515 + onClick={handleDemoImage} 516 + > 517 + Use Demo Image 518 + </Button> 519 + </div> 520 + {isDragOver && ( 521 + <div className="absolute inset-0 bg-primary/20 rounded-xl flex items-center justify-center"> 522 + <div 523 + className={`text-primary font-semibold bg-white/90 px-6 py-3 rounded-lg ${getDropZoneOverlayText().textSize}`} 524 + > 525 + Drop image here 526 + </div> 527 + </div> 528 + )} 529 + </div> 530 + )} 531 + </div> 532 + <input ref={fileInputRef} type="file" accept="image/*" onChange={handleFileSelect} className="hidden" /> 533 + </div> 534 + </div> 535 + ); 536 + };
+21
src/components/editor/Editor.tsx
··· 1 + 2 + import React from 'react'; 3 + import { Canvas } from './Canvas'; 4 + import { Backgrounds } from './Backgrounds'; 5 + import { Navbar } from './Navbar'; 6 + import { FloatingBar } from './FloatingBar'; 7 + 8 + export const Editor: React.FC = () => { 9 + return ( 10 + <div className="h-screen flex flex-col bg-gray-900"> 11 + <Navbar /> 12 + 13 + <div className="flex-1 flex overflow-hidden"> 14 + <Canvas /> 15 + <Backgrounds /> 16 + </div> 17 + 18 + <FloatingBar /> 19 + </div> 20 + ); 21 + };
+228
src/components/editor/FloatingBar.tsx
··· 1 + import React, { useState, useRef } from 'react'; 2 + import { Button } from '@/components/ui/button'; 3 + import { 4 + Tooltip, 5 + TooltipContent, 6 + TooltipTrigger, 7 + } from '@/components/ui/tooltip'; 8 + import { Separator } from '@/components/ui/separator'; 9 + import { 10 + Move, 11 + Undo, 12 + SquareDashed, 13 + Rotate3D 14 + } from 'lucide-react'; 15 + import { RotationSkewPanel } from './RotationSkewPanel'; 16 + import { PositionScalePanel } from './PositionScalePanel'; 17 + import { ImageBorderPanel } from './ImageBorderPanel'; 18 + import { useMobile } from '@/hooks/use-mobile'; 19 + import { Sheet, SheetContent } from '@/components/ui/sheet'; 20 + import { useMockupStore } from '@/contexts/MockupContext'; 21 + 22 + export const FloatingBar: React.FC = () => { 23 + const { 24 + uploadedImage, 25 + setUploadedImage, 26 + updateDevicePosition, 27 + set3DRotation, 28 + setImageBorder, 29 + setMargin, 30 + setFixedMargin 31 + } = useMockupStore(); 32 + 33 + const [activePanel, setActivePanel] = useState<string | null>(null); 34 + const [navPosition, setNavPosition] = useState({ x: 0, y: 0 }); 35 + const isMobile = useMobile(); 36 + const hasImageRef = useRef(false); 37 + 38 + // Only update hasImageRef when uploadedImage actually changes between null and non-null 39 + const hasImage = !!uploadedImage; 40 + if (hasImageRef.current !== hasImage) { 41 + hasImageRef.current = hasImage; 42 + } 43 + 44 + const handleReset = () => { 45 + setMargin({ top: 0, right: 0, bottom: 0, left: 0 }); 46 + setFixedMargin(false); 47 + setUploadedImage(null); 48 + updateDevicePosition({ x: 0, y: 0, scale: 1, rotation: 0 }); 49 + set3DRotation({ rotateX: 0, rotateY: 0, rotateZ: 0, skew: 0 }); 50 + setImageBorder({ width: 8, color: '#FF6B6B', radius: 22, enabled: false }); 51 + setActivePanel(null); 52 + }; 53 + 54 + const togglePanel = (panelName: string) => { 55 + setActivePanel(prev => prev === panelName ? null : panelName); 56 + }; 57 + 58 + // Separate the position calculation logic to only run when necessary 59 + React.useEffect(() => { 60 + if (!isMobile && hasImageRef.current) { 61 + const updatePosition = () => { 62 + const canvasElement = document.querySelector('[data-mockup-canvas]') as HTMLElement; 63 + if (canvasElement) { 64 + const rect = canvasElement.getBoundingClientRect(); 65 + const navHeight = 60; 66 + const navWidth = 280; 67 + const padding = 20; 68 + 69 + let centerX = rect.left + rect.width / 2; 70 + let bottomY = rect.bottom - 80; 71 + 72 + const maxX = window.innerWidth - navWidth / 2 - padding; 73 + const minX = navWidth / 2 + padding; 74 + centerX = Math.max(minX, Math.min(maxX, centerX)); 75 + 76 + const maxY = window.innerHeight - navHeight - padding; 77 + const minY = padding; 78 + bottomY = Math.max(minY, Math.min(maxY, bottomY)); 79 + 80 + setNavPosition({ 81 + x: centerX, 82 + y: bottomY 83 + }); 84 + } 85 + }; 86 + 87 + updatePosition(); 88 + window.addEventListener('resize', updatePosition); 89 + window.addEventListener('scroll', updatePosition); 90 + 91 + return () => { 92 + window.removeEventListener('resize', updatePosition); 93 + window.removeEventListener('scroll', updatePosition); 94 + }; 95 + } 96 + }, [isMobile, hasImageRef.current]); // Remove uploadedImage dependency 97 + 98 + // Render panel components only once and keep them stable 99 + const panelComponents = React.useMemo(() => ({ 100 + rotation: <RotationSkewPanel onClose={() => setActivePanel(null)} />, 101 + position: <PositionScalePanel onClose={() => setActivePanel(null)} />, 102 + border: <ImageBorderPanel onClose={() => setActivePanel(null)} /> 103 + }), []); // Empty dependency array - components are stable 104 + 105 + const renderPanel = () => { 106 + if (!activePanel) return null; 107 + 108 + const PanelContent = panelComponents[activePanel as keyof typeof panelComponents]; 109 + 110 + if (isMobile) { 111 + return ( 112 + <Sheet open={!!activePanel} onOpenChange={() => setActivePanel(null)}> 113 + <SheetContent side="bottom" className="bg-sidebar border-t border-sidebar-border"> 114 + {PanelContent} 115 + </SheetContent> 116 + </Sheet> 117 + ); 118 + } 119 + 120 + return PanelContent; 121 + }; 122 + 123 + const NavButtons = () => ( 124 + <> 125 + <Tooltip> 126 + <TooltipTrigger asChild> 127 + <Button 128 + onClick={handleReset} 129 + variant="outline" 130 + className={`text-white hover:text-primary hover:bg-primary/20 rounded-full`} 131 + disabled={!hasImage} 132 + > 133 + <Undo className="w-10 h-10" /> 134 + Reset 135 + </Button> 136 + </TooltipTrigger> 137 + <TooltipContent><p>Reset Transformations</p></TooltipContent> 138 + </Tooltip> 139 + 140 + {!isMobile && <Separator orientation="vertical" className="h-6 bg-primary/40 mx-1" />} 141 + 142 + <Tooltip> 143 + <TooltipTrigger asChild> 144 + <Button 145 + onClick={() => togglePanel('rotation')} 146 + variant={activePanel === 'rotation' ? 'default' : 'ghost'} 147 + className={`rounded-full 148 + ${activePanel === 'rotation' 149 + ? 'bg-primary hover:bg-primary/80 text-black' 150 + : 'text-white hover:text-primary hover:bg-primary/20' 151 + }`} 152 + disabled={!hasImage} 153 + > 154 + <Rotate3D className="w-10 h-10" /> 155 + {!isMobile && 'Rotate & Transform'} 156 + </Button> 157 + </TooltipTrigger> 158 + <TooltipContent><p>3D Rotation</p></TooltipContent> 159 + </Tooltip> 160 + 161 + <Tooltip> 162 + <TooltipTrigger asChild> 163 + <Button 164 + onClick={() => togglePanel('position')} 165 + variant={activePanel === 'position' ? 'default' : 'ghost'} 166 + className={`rounded-full py-3 167 + ${activePanel === 'position' 168 + ? 'bg-primary hover:bg-primary/80 text-black' 169 + : 'text-white hover:text-primary hover:bg-primary/20' 170 + }`} 171 + disabled={!hasImage} 172 + > 173 + <Move className="w-10 h-10" /> 174 + {!isMobile && 'Position & Scale'} 175 + </Button> 176 + </TooltipTrigger> 177 + <TooltipContent><p>Position & Scale</p></TooltipContent> 178 + </Tooltip> 179 + 180 + <Tooltip> 181 + <TooltipTrigger asChild> 182 + <Button 183 + onClick={() => togglePanel('border')} 184 + variant={activePanel === 'border' ? 'default' : 'ghost'} 185 + className={`rounded-full 186 + ${activePanel === 'border' 187 + ? 'bg-primary hover:bg-primary/80 text-black' 188 + : 'text-white hover:text-primary hover:bg-primary/20' 189 + }`} 190 + disabled={!hasImage} 191 + > 192 + <SquareDashed className="w-10 h-10" /> 193 + {!isMobile && 'Border'} 194 + </Button> 195 + </TooltipTrigger> 196 + <TooltipContent><p>Image Border</p></TooltipContent> 197 + </Tooltip> 198 + </> 199 + ); 200 + 201 + if (isMobile) { 202 + return ( 203 + <> 204 + {renderPanel()} 205 + <div className="fixed bottom-0 left-0 right-0 h-20 bg-sidebar border-t border-sidebar-border flex items-center justify-between px-4"> 206 + <NavButtons /> 207 + </div> 208 + </> 209 + ); 210 + } 211 + 212 + return ( 213 + <> 214 + {renderPanel()} 215 + <div 216 + className="fixed z-30 -translate-x-1/2" 217 + style={{ 218 + left: `${navPosition.x}px`, 219 + top: `${navPosition.y}px` 220 + }} 221 + > 222 + <div className="flex items-center gap-1 bg-sidebar/80 backdrop-blur-lg border-2 border-primary/60 rounded-full shadow-2xl p-1.5"> 223 + <NavButtons /> 224 + </div> 225 + </div> 226 + </> 227 + ); 228 + };
+301
src/components/editor/ImageBorderPanel.tsx
··· 1 + import React, { useState, useRef } from 'react'; 2 + import { Card, CardContent } from '@/components/ui/card'; 3 + import { Button } from '@/components/ui/button'; 4 + import { Slider } from '@/components/ui/slider'; 5 + import { Switch } from '@/components/ui/switch'; 6 + import { GripVertical, Undo, RectangleHorizontal, Palette, CornerUpLeft, Wand2 } from 'lucide-react'; 7 + import { useMockupStore } from '@/contexts/MockupContext'; 8 + import { extractDominantColor } from '@/utils/colorExtractor'; 9 + 10 + interface ImageBorderPanelProps { 11 + onClose: () => void; 12 + } 13 + 14 + export const ImageBorderPanel: React.FC<ImageBorderPanelProps> = ({ onClose }) => { 15 + const { 16 + imageBorder, 17 + setImageBorder, 18 + uploadedImage, 19 + } = useMockupStore(); 20 + 21 + // Parse RGBA to initialize isTransparent and opacity 22 + const parseRgba = (color: string): { r: number; g: number; b: number; a: number } => { 23 + const matches = color.match(/rgba\((\d+),\s*(\d+),\s*(\d+),\s*([\d.]+)\)/); 24 + if (matches) { 25 + return { 26 + r: parseInt(matches[1]), 27 + g: parseInt(matches[2]), 28 + b: parseInt(matches[3]), 29 + a: parseFloat(matches[4]), 30 + }; 31 + } 32 + // Fallback to default color if parsing fails 33 + return { r: 156, g: 163, b: 137, a: 1 }; // #9CA389 34 + }; 35 + 36 + const initialRgba = parseRgba(imageBorder.color); 37 + const [isDragging, setIsDragging] = useState(false); 38 + const [windowPosition, setWindowPosition] = useState({ x: 300, y: 100 }); 39 + const windowRef = useRef<HTMLDivElement>(null); 40 + const dragStartRef = useRef({ x: 0, y: 0 }); 41 + const [isTransparent, setIsTransparent] = useState(initialRgba.a < 1); 42 + const [isLoadingColor, setIsLoadingColor] = useState(false); 43 + const [opacity, setOpacity] = useState(Math.max(0.3, initialRgba.a)); 44 + 45 + const handleWindowDragStart = (e: React.MouseEvent) => { 46 + e.preventDefault(); 47 + setIsDragging(true); 48 + dragStartRef.current = { 49 + x: e.clientX - windowPosition.x, 50 + y: e.clientY - windowPosition.y 51 + }; 52 + }; 53 + 54 + const handleWindowDrag = (e: MouseEvent) => { 55 + if (!isDragging) return; 56 + setWindowPosition({ 57 + x: e.clientX - dragStartRef.current.x, 58 + y: e.clientY - dragStartRef.current.y 59 + }); 60 + }; 61 + 62 + const handleWindowDragEnd = () => { 63 + setIsDragging(false); 64 + }; 65 + 66 + React.useEffect(() => { 67 + if (isDragging) { 68 + window.addEventListener('mousemove', handleWindowDrag); 69 + window.addEventListener('mouseup', handleWindowDragEnd); 70 + return () => { 71 + window.removeEventListener('mousemove', handleWindowDrag); 72 + window.removeEventListener('mouseup', handleWindowDragEnd); 73 + }; 74 + } 75 + }, [isDragging]); 76 + 77 + const resetBorder = () => { 78 + setImageBorder({ 79 + width: 8, 80 + color: 'rgba(156, 163, 137, 1)', // #9CA389 81 + radius: 22, 82 + enabled: false, 83 + shadow: 'rgba(0, 0, 0, 0.16) 0px 3px 6px, rgba(0, 0, 0, 0.23) 0px 3px 6px', 84 + }); 85 + setIsTransparent(false); 86 + setOpacity(1); 87 + }; 88 + 89 + const handleMagicWandClick = async () => { 90 + if (!uploadedImage) return; 91 + setIsLoadingColor(true); 92 + try { 93 + const dominantColor = await extractDominantColor(uploadedImage); 94 + const matches = dominantColor.match(/^#[0-9A-Fa-f]{6}$/) ? dominantColor : '#9CA389'; 95 + const { r, g, b } = hexToRgb(matches); 96 + setImageBorder({ 97 + color: `rgba(${r}, ${g}, ${b}, ${isTransparent ? opacity : 1})` 98 + }); 99 + } catch (error) { 100 + console.error('Error extracting dominant color:', error); 101 + setImageBorder({ 102 + color: `rgba(156, 163, 137, ${isTransparent ? opacity : 1})` // #9CA389 103 + }); 104 + } finally { 105 + setIsLoadingColor(false); 106 + } 107 + }; 108 + 109 + const colorOptions = [ 110 + 'magic-wand', 111 + 'rgba(255, 255, 255, 1)', // #FFFFFF 112 + 'rgba(156, 163, 137, 1)', // #9CA389 113 + 'rgba(0, 0, 0, 1)', // #000000 114 + 'rgba(255, 107, 107, 1)', // #FF6B6B 115 + 'rgba(254, 202, 87, 1)', // #FECA57 116 + 'rgba(78, 205, 196, 1)', // #4ECDC4 117 + 'rgba(69, 183, 209, 1)', // #45B7D1 118 + ]; 119 + 120 + const hexToRgb = (hex: string): { r: number; g: number; b: number } => { 121 + const r = parseInt(hex.slice(1, 3), 16); 122 + const g = parseInt(hex.slice(3, 5), 16); 123 + const b = parseInt(hex.slice(5, 7), 16); 124 + return { r, g, b }; 125 + }; 126 + 127 + const convertToRgba = (r: number, g: number, b: number, opacity: number): string => { 128 + return `rgba(${r}, ${g}, ${b}, ${opacity})`; 129 + }; 130 + 131 + const handleColorSelect = (color: string) => { 132 + if (color === 'magic-wand') return; 133 + const { r, g, b } = parseRgba(color); 134 + setImageBorder({ 135 + color: convertToRgba(r, g, b, isTransparent ? opacity : 1) 136 + }); 137 + }; 138 + 139 + const handleTransparencyToggle = (checked: boolean) => { 140 + setIsTransparent(checked); 141 + const newOpacity = checked ? 0.5 : 1; 142 + setOpacity(newOpacity); 143 + const { r, g, b } = parseRgba(imageBorder.color); 144 + setImageBorder({ 145 + color: convertToRgba(r, g, b, newOpacity) 146 + }); 147 + }; 148 + 149 + const handleOpacityChange = (value: number[]) => { 150 + const newOpacity = Math.max(0.3, value[0]); 151 + setOpacity(newOpacity); 152 + if (isTransparent) { 153 + const { r, g, b } = parseRgba(imageBorder.color); 154 + setImageBorder({ 155 + color: convertToRgba(r, g, b, newOpacity) 156 + }); 157 + } 158 + }; 159 + 160 + return ( 161 + <div 162 + ref={windowRef} 163 + className="md:fixed z-40 select-none" 164 + style={{ 165 + left: `${windowPosition.x}px`, 166 + top: `${windowPosition.y}px` 167 + }} 168 + > 169 + <Card className="bg-sidebar max-md:border-none border-sidebar-border rounded-2xl overflow-hidden min-w-80"> 170 + <div 171 + className="flex items-center justify-between p-4 bg-sidebar md:cursor-move border-b border-sidebar-border" 172 + onMouseDown={handleWindowDragStart} 173 + > 174 + <div className="flex items-center gap-3"> 175 + <GripVertical className="w-4 h-4 text-primary/70" /> 176 + <span className="text-white text-lg font-semibold">Image Border</span> 177 + </div> 178 + <Button onClick={resetBorder} variant="ghost" size="sm" className="text-white hover:text-primary hover:bg-primary/20 p-3 rounded-full"> 179 + <Undo className="w-4 h-4" /> 180 + </Button> 181 + </div> 182 + 183 + <CardContent className="p-6 bg-sidebar"> 184 + <div className="space-y-6"> 185 + {/* Enable Border Section */} 186 + <div className="flex items-center justify-between"> 187 + <span className="text-white text-sm">Enable Border</span> 188 + <Switch 189 + checked={imageBorder.enabled} 190 + onCheckedChange={(checked) => setImageBorder({ enabled: checked })} 191 + /> 192 + </div> 193 + 194 + {imageBorder.enabled && ( 195 + <> 196 + {/* Transparent Border Section */} 197 + <div className="space-y-3"> 198 + <div className="flex items-center justify-between"> 199 + <span className="text-white text-sm">Transparent Border</span> 200 + <Switch 201 + checked={isTransparent} 202 + onCheckedChange={handleTransparencyToggle} 203 + /> 204 + </div> 205 + {isTransparent && ( 206 + <div className="space-y-3"> 207 + <div className="flex items-center justify-between"> 208 + <span className="text-white text-sm">Opacity</span> 209 + <span className="text-primary text-sm">{Math.round(opacity * 100)}%</span> 210 + </div> 211 + <Slider 212 + value={[opacity]} 213 + onValueChange={handleOpacityChange} 214 + min={0.3} 215 + max={1} 216 + step={0.01} 217 + /> 218 + </div> 219 + )} 220 + </div> 221 + 222 + {/* Color Section */} 223 + <div className="space-y-3"> 224 + <div className="flex items-center gap-2"> 225 + <Palette className="w-4 h-4 text-primary" /> 226 + <span className="text-white text-sm">Color</span> 227 + </div> 228 + <div className="grid grid-cols-4 gap-2"> 229 + {colorOptions.map((color) => ( 230 + color === 'magic-wand' ? ( 231 + <button 232 + key={color} 233 + onClick={handleMagicWandClick} 234 + disabled={isLoadingColor || !uploadedImage} 235 + className={`w-full h-10 rounded-lg border-2 transition-all flex items-center justify-center ${ 236 + isLoadingColor || !uploadedImage 237 + ? 'opacity-50 cursor-not-allowed' 238 + : 'border-primary/30 hover:border-primary/70' 239 + }`} 240 + > 241 + <Wand2 className="w-5 h-5 text-primary" /> 242 + </button> 243 + ) : ( 244 + <button 245 + key={color} 246 + onClick={() => handleColorSelect(color)} 247 + className={`w-full h-10 rounded-lg border-2 transition-all ${ 248 + imageBorder.color === color || imageBorder.color === convertToRgba(parseRgba(color).r, parseRgba(color).g, parseRgba(color).b, opacity) 249 + ? 'border-primary scale-110' 250 + : 'border-primary/30 hover:border-primary/70' 251 + }`} 252 + style={{ backgroundColor: isTransparent ? convertToRgba(parseRgba(color).r, parseRgba(color).g, parseRgba(color).b, opacity) : color }} 253 + /> 254 + ) 255 + ))} 256 + </div> 257 + </div> 258 + 259 + {/* Width Section */} 260 + <div className="space-y-3"> 261 + <div className="flex items-center justify-between"> 262 + <div className="flex items-center gap-2"> 263 + <RectangleHorizontal className="w-4 h-4 text-primary" /> 264 + <span className="text-white text-sm">Width</span> 265 + </div> 266 + <span className="text-primary text-sm">{imageBorder.width}px</span> 267 + </div> 268 + <Slider 269 + value={[imageBorder.width]} 270 + onValueChange={(value) => setImageBorder({ width: value[0] })} 271 + min={0} 272 + max={20} 273 + step={1} 274 + /> 275 + </div> 276 + 277 + {/* Radius (Corners) Section */} 278 + <div className="space-y-3"> 279 + <div className="flex items-center justify-between"> 280 + <div className="flex items-center gap-2"> 281 + <CornerUpLeft className="w-4 h-4 text-primary" /> 282 + <span className="text-white text-sm">Radius</span> 283 + </div> 284 + <span className="text-primary text-sm">{imageBorder.radius}px</span> 285 + </div> 286 + <Slider 287 + value={[imageBorder.radius]} 288 + onValueChange={(value) => setImageBorder({ radius: value[0] })} 289 + min={0} 290 + max={50} 291 + step={1} 292 + /> 293 + </div> 294 + </> 295 + )} 296 + </div> 297 + </CardContent> 298 + </Card> 299 + </div> 300 + ); 301 + };
+437
src/components/editor/Navbar.tsx
··· 1 + import { useState } from 'react'; 2 + import { Button } from '@/components/ui/button'; 3 + import { Slider } from '@/components/ui/slider'; 4 + import { Card, CardContent, CardHeader } from '@/components/ui/card'; 5 + import { Dialog, DialogContent, DialogTrigger, DialogHeader, DialogTitle } from '@/components/ui/dialog'; 6 + import { Badge } from '@/components/ui/badge'; 7 + import { ToggleGroup, ToggleGroupItem } from '@/components/ui/toggle-group'; 8 + import { 9 + Download, 10 + ChevronDown, 11 + Coffee, 12 + ExternalLink, 13 + Loader2, 14 + QrCode, 15 + Heart, 16 + Twitter, 17 + X, 18 + CheckCircle 19 + } from 'lucide-react'; 20 + import { toast } from 'sonner'; 21 + import { useMockupStore } from '@/contexts/MockupContext'; 22 + import html2canvas from 'html2canvas'; 23 + 24 + export function Navbar() { 25 + const [showExportOptions, setShowExportOptions] = useState(false); 26 + const [exportFormat, setExportFormat] = useState('PNG'); 27 + const [quality, setQuality] = useState([2]); 28 + const [isExporting, setIsExporting] = useState(false); 29 + const [isDialogOpen, setIsDialogOpen] = useState(false); 30 + 31 + const { uploadedImage, imageBorder, fixedMargin, margin } = useMockupStore(); 32 + 33 + const isMobile = typeof window !== 'undefined' ? window.innerWidth < 768 : false; 34 + 35 + const getQualityLabel = (value) => { 36 + switch (value) { 37 + case 1: return 'Standard (1x)'; 38 + case 2: return 'High (2x)'; 39 + case 3: return 'Ultra (3x)'; 40 + default: return `${value}x`; 41 + } 42 + }; 43 + 44 + const exportImage = async (format: string, qualityMultiplier: number) => { 45 + if (!uploadedImage) return; 46 + 47 + try { 48 + const mockupElement = document.querySelector('[data-mockup-canvas]') as HTMLDivElement; 49 + if (!mockupElement) throw new Error('Mockup canvas not found'); 50 + 51 + const imgElement = mockupElement.querySelector('img') as HTMLImageElement; 52 + if (!imgElement) throw new Error('Image element not found'); 53 + 54 + // For fixed margin mode: capture the entire canvas (image + background margins) 55 + const canvas = await html2canvas(mockupElement, { 56 + scale: qualityMultiplier * 2, 57 + useCORS: true, 58 + allowTaint: true, 59 + backgroundColor: null, 60 + // Capture the entire mockup element without any cropping 61 + }); 62 + 63 + const mimeType = `image/${format.toLowerCase()}`; 64 + const imageQuality = format === 'JPEG' ? 1 : undefined; 65 + 66 + canvas.toBlob( 67 + (blob) => { 68 + if (blob) { 69 + const url = URL.createObjectURL(blob); 70 + const a = document.createElement('a'); 71 + a.href = url; 72 + a.download = `mockup-${Date.now()}.${format.toLowerCase()}`; 73 + document.body.appendChild(a); 74 + a.click(); 75 + document.body.removeChild(a); 76 + URL.revokeObjectURL(url); 77 + } else { 78 + throw new Error('Failed to create blob'); 79 + } 80 + }, 81 + mimeType, 82 + imageQuality 83 + ); 84 + 85 + 86 + } catch (error) { 87 + console.error('Export error:', error); 88 + throw error; 89 + } 90 + }; 91 + 92 + const handleSingleExport = async () => { 93 + if (!uploadedImage) { 94 + toast.error('Please upload an image first'); 95 + return; 96 + } 97 + 98 + setIsExporting(true); 99 + try { 100 + await exportImage(exportFormat, quality[0]); 101 + toast.success(`Successfully exported as ${exportFormat}!`, { 102 + icon: <CheckCircle className="w-4 h-4" /> 103 + }); 104 + } catch (error) { 105 + toast.error('Failed to export image. Please try again.'); 106 + } finally { 107 + setIsExporting(false); 108 + } 109 + }; 110 + 111 + const handleAllFormatsExport = async () => { 112 + if (!uploadedImage) { 113 + toast.error('Please upload an image first'); 114 + return; 115 + } 116 + 117 + setIsExporting(true); 118 + const formats = ['PNG', 'JPEG', 'WebP']; 119 + const qualityMultiplier = quality[0]; 120 + 121 + try { 122 + await Promise.all( 123 + formats.map(format => exportImage(format, qualityMultiplier)) 124 + ); 125 + 126 + toast.success(`Successfully exported all formats (${formats.join(', ')})!`, { 127 + icon: <CheckCircle className="w-4 h-4" /> 128 + }); 129 + } catch (error) { 130 + console.error('Export all formats error:', error); 131 + toast.error('Failed to export some formats. Please try again.'); 132 + } finally { 133 + setIsExporting(false); 134 + } 135 + }; 136 + 137 + const ExportOptionsContent = () => ( 138 + <div className={` 139 + ${isMobile 140 + ? 'flex flex-col gap-2' 141 + : 'grid grid-cols-2 gap-6' 142 + } 143 + `}> 144 + <div className={`space-y-6 ${!isMobile ? 'order-2' : 'order-1'}`}> 145 + <div> 146 + <h3 className="text-xl font-semibold text-foreground mb-4 flex items-center gap-2"> 147 + <Download className="w-5 h-5 text-primary" /> 148 + Export Settings 149 + </h3> 150 + <div className="space-y-4"> 151 + <div className="flex items-center justify-between"> 152 + <label className="text-sm font-medium">Format</label> 153 + <Badge variant="outline" className="font-mono"> 154 + {exportFormat} 155 + </Badge> 156 + </div> 157 + <ToggleGroup 158 + type="single" 159 + value={exportFormat} 160 + onValueChange={(value) => value && setExportFormat(value)} 161 + className="flex gap-1 bg-sidebar/80 rounded-full ring-2 ring-secondary p-1" 162 + > 163 + {['PNG', 'JPEG', 'WebP'].map((format) => ( 164 + <ToggleGroupItem 165 + key={format} 166 + value={format} 167 + className={`flex-1 text-sm rounded-full cursor-pointer hover:bg-primary/20 hover:text-primary data-[state=on]:bg-primary data-[state=on]:text-black`} 168 + > 169 + {format} 170 + </ToggleGroupItem> 171 + ))} 172 + </ToggleGroup> 173 + </div> 174 + <div className="space-y-2 mt-4"> 175 + <div className="flex items-center justify-between"> 176 + <label className="text-sm font-medium">Quality</label> 177 + <Badge variant="outline" className="font-mono"> 178 + {getQualityLabel(quality[0])} 179 + </Badge> 180 + </div> 181 + <div className="space-y-3"> 182 + <Slider 183 + value={quality} 184 + onValueChange={setQuality} 185 + min={1} 186 + max={3} 187 + step={1} 188 + className="w-full" 189 + /> 190 + <div className="flex justify-between text-xs text-muted-foreground"> 191 + <span>Standard</span> 192 + <span>High</span> 193 + <span>Ultra</span> 194 + </div> 195 + </div> 196 + </div> 197 + </div> 198 + <div className="space-y-3 grid grid-cols-2 gap-3"> 199 + <Button 200 + onClick={handleSingleExport} 201 + className="w-full h-12 font-medium shadow-md" 202 + disabled={isExporting || !uploadedImage} 203 + > 204 + {isExporting ? ( 205 + <> 206 + <Loader2 className="w-5 h-5 mr-2 animate-spin" /> 207 + Exporting... 208 + </> 209 + ) : ( 210 + <> 211 + <Download className="w-5 h-5 mr-2" /> 212 + Export as {exportFormat} 213 + </> 214 + )} 215 + </Button> 216 + <Button 217 + onClick={handleAllFormatsExport} 218 + variant="outline" 219 + className="w-full h-12 font-medium" 220 + disabled={isExporting || !uploadedImage} 221 + > 222 + {isExporting ? ( 223 + <> 224 + <Loader2 className="w-5 h-5 mr-2 animate-spin" /> 225 + Exporting All... 226 + </> 227 + ) : ( 228 + <> 229 + <Download className="w-5 h-5 mr-2" /> 230 + Export All Formats 231 + </> 232 + )} 233 + </Button> 234 + </div> 235 + </div> 236 + 237 + <Card className={`border-primary/20 bg-gradient-to-br from-primary/5 to-primary/10 ${!isMobile ? 'order-1' : 'order-2'} group`}> 238 + <CardHeader className="pb-4"> 239 + <div className="flex items-center gap-2"> 240 + <Heart className="w-5 h-5 text-primary fill-primary/20 group-hover:fill-primary/50 transition-colors group-hover:animate-pulse group-hover:scale-110" /> 241 + </div> 242 + </CardHeader> 243 + <CardContent className="space-y-4"> 244 + <div> 245 + <p className="text-sm text-foreground leading-relaxed"> 246 + Hi, I'm 247 + <a 248 + href="https://jaydip.me" 249 + target="_blank" 250 + rel="noopener noreferrer" 251 + className="inline-flex items-center gap-1 mx-1 text-primary underline hover:no-underline" 252 + > 253 + Jaydip 254 + <ExternalLink className="w-3 h-3" /> 255 + </a> 256 + </p> 257 + <p className='text-sm text-muted-foreground leading-relaxed'> 258 + moocup is a simple offline tool. 259 + </p> 260 + <p className='text-sm text-muted-foreground leading-relaxed'> 261 + focus on your craft, we'll take care of rest. 262 + </p> 263 + <p className='text-sm text-muted-foreground leading-relaxed'> 264 + you can show your support by sponsoring my work! 265 + </p> 266 + </div> 267 + <div className="space-y-3"> 268 + <Button 269 + asChild 270 + className="w-full h-12 hover:primary/10 shadow-md" 271 + > 272 + <a 273 + href="https://ko-fi.com/jaydipsanghani" 274 + target="_blank" 275 + rel="noopener noreferrer" 276 + className="flex items-center" 277 + > 278 + <Coffee className="w-5 h-5" /> 279 + <span className="font-medium">Buy me a coffee</span> 280 + </a> 281 + </Button> 282 + <Dialog> 283 + <DialogTrigger asChild> 284 + <Button 285 + variant="outline" 286 + className="w-full h-12 border-primary/30 hover:bg-primary/5 hover:border-primary/50 inline-flex items-center" 287 + > 288 + <QrCode className="w-5 h-5 -ml-10" /> 289 + <span className="font-medium">UPI (India)</span> 290 + </Button> 291 + </DialogTrigger> 292 + <DialogContent className="max-w-xs"> 293 + <DialogHeader> 294 + <DialogTitle className="text-center">Thanks so much!</DialogTitle> 295 + </DialogHeader> 296 + <div className="flex flex-col items-center gap-4 py-4"> 297 + <div className="w-48 h-48 bg-muted rounded-xl flex items-center justify-center border-2 border-dashed border-muted-foreground/20"> 298 + <QrCode className="w-16 h-16 text-muted-foreground/50" /> 299 + </div> 300 + <p className="text-sm text-muted-foreground text-center"> 301 + Scan with any UPI app 302 + </p> 303 + </div> 304 + </DialogContent> 305 + </Dialog> 306 + </div> 307 + <div className="space-y-2 pt-2 border-t border-primary/10"> 308 + <h4 className="text-sm font-medium text-muted-foreground">Say hi! 309 + <span className='ml-2 text-sm text-muted-foreground leading-relaxed'> 310 + I'm always up for quick chat :) 311 + </span> 312 + </h4> 313 + <div className="flex gap-2"> 314 + <Button variant="outline" size="sm" asChild className="flex-1 border-primary/30 hover:border-primary/50"> 315 + <a 316 + href="https://bsky.app/profile/jellydeck.bsky.social" 317 + target="_blank" 318 + rel="noopener noreferrer" 319 + className="flex items-center gap-2" 320 + > 321 + <svg 322 + xmlns="http://www.w3.org/2000/svg" 323 + viewBox="0 0 24 24" 324 + className='w-4 h-4' 325 + > 326 + <path fill="none" stroke="currentColor" strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M12 10Q2-2 2 6t5 8q-5 3-1 6t6-3q2 6 6 3t-1-6q5 0 5-8t-10 4" /> 327 + </svg> 328 + <span>Bluesky</span> 329 + </a> 330 + </Button> 331 + <Button variant="outline" size="sm" asChild className="flex-1 border-primary/30 hover:border-primary/50"> 332 + <a 333 + href="https://twitter.com/JellyDeck" 334 + target="_blank" 335 + rel="noopener noreferrer" 336 + className="flex items-center gap-2" 337 + > 338 + <Twitter className="w-4 h-4" /> 339 + <span>Twitter</span> 340 + </a> 341 + </Button> 342 + </div> 343 + </div> 344 + </CardContent> 345 + </Card> 346 + </div> 347 + ); 348 + 349 + return ( 350 + <div className="sticky top-0 z-50 bg-background/80 backdrop-blur-lg border-b"> 351 + <div className="flex items-center justify-between px-4 h-16 lg:mx-40"> 352 + <div className="flex items-center gap-4"> 353 + <div className="flex items-center gap-2"> 354 + <h1 className="text-xl font-bold text-primary">Moo</h1> 355 + <a 356 + href="https://ko-fi.com/jaydipsanghani" 357 + target="_blank" 358 + rel="noopener noreferrer" 359 + className="flex items-center" 360 + > 361 + <Coffee className="w-6 h-6 text-primary hover:text-primary/80 hover:rotate-12 transition-transform cursor-pointer" /> 362 + </a> 363 + </div> 364 + {!isMobile && ( 365 + <div className="flex items-center gap-2 text-sm text-muted-foreground"> 366 + <span>crafted by</span> 367 + <Button variant="link" className="h-auto p-0 text-primary" asChild> 368 + <a 369 + href="https://jaydip.me" 370 + target="_blank" 371 + rel="noopener noreferrer" 372 + className="flex items-center gap-1" 373 + > 374 + Jaydip 375 + <ExternalLink className="w-3 h-3" /> 376 + </a> 377 + </Button> 378 + </div> 379 + )} 380 + </div> 381 + <div className="flex items-center gap-2"> 382 + {isMobile ? ( 383 + <Dialog open={isDialogOpen} onOpenChange={setIsDialogOpen}> 384 + <DialogTrigger asChild> 385 + <Button 386 + size="icon" 387 + disabled={!uploadedImage} 388 + className="h-10 w-10" 389 + > 390 + <Download className="w-5 h-5" /> 391 + </Button> 392 + </DialogTrigger> 393 + <DialogContent className="w-[95vw] max-w-md h-[90vh] max-h-[90vh] flex flex-col p-0 [&>button:first-of-type]:hidden"> 394 + <div className="flex items-center justify-between p-4 border-b shrink-0"> 395 + <DialogTitle className="text-lg font-semibold">Export & Support</DialogTitle> 396 + <Button 397 + variant="ghost" 398 + size="icon" 399 + onClick={() => setIsDialogOpen(false)} 400 + className="h-8 w-8" 401 + > 402 + <X className="w-4 h-4" /> 403 + </Button> 404 + </div> 405 + <div className="flex-1 overflow-y-auto"> 406 + <div className="px-4 pb-4"> 407 + <ExportOptionsContent /> 408 + </div> 409 + </div> 410 + </DialogContent> 411 + </Dialog> 412 + ) : ( 413 + <div className="relative"> 414 + <Button 415 + variant="outline" 416 + onClick={() => setShowExportOptions(!showExportOptions)} 417 + disabled={!uploadedImage} 418 + className="gap-2" 419 + > 420 + <Download className="w-4 h-4" /> 421 + Export 422 + <ChevronDown className={`w-4 h-4 transition-transform ${showExportOptions ? 'rotate-180' : ''}`} /> 423 + </Button> 424 + {showExportOptions && ( 425 + <Card className="absolute right-0 top-full mt-2 w-[900px] shadow-lg z-50 bg-background border"> 426 + <CardContent className="p-6"> 427 + <ExportOptionsContent /> 428 + </CardContent> 429 + </Card> 430 + )} 431 + </div> 432 + )} 433 + </div> 434 + </div> 435 + </div> 436 + ); 437 + }
+304
src/components/editor/PositionScalePanel.tsx
··· 1 + import { useEffect, useRef, useState } from 'react'; 2 + import { Card, CardContent } from '@/components/ui/card'; 3 + import { Button } from '@/components/ui/button'; 4 + import { Slider } from '@/components/ui/slider'; 5 + import { Switch } from '@/components/ui/switch'; 6 + import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'; 7 + import { ToggleGroup, ToggleGroupItem } from '@/components/ui/toggle-group'; 8 + import { GripVertical, Undo, Move, Maximize2 } from 'lucide-react'; 9 + import { useMockupStore } from '@/contexts/MockupContext'; 10 + 11 + interface PositionScalePanelProps { 12 + onClose: () => void; 13 + } 14 + 15 + export const PositionScalePanel: React.FC<PositionScalePanelProps> = ({ onClose }) => { 16 + const { 17 + devicePosition, 18 + updateDevicePosition, 19 + fixedMargin, 20 + setFixedMargin, 21 + margin, 22 + setMargin, 23 + } = useMockupStore(); 24 + 25 + const [isDragging, setIsDragging] = useState(false); 26 + const [windowPosition, setWindowPosition] = useState({ x: 200, y: 100 }); 27 + const [scale, setScale] = useState(() => devicePosition.scale); 28 + const [activeTab, setActiveTab] = useState('position'); 29 + const [marginPreset, setMarginPreset] = useState('medium'); 30 + 31 + const windowRef = useRef<HTMLDivElement>(null); 32 + const dragStartRef = useRef({ x: 0, y: 0 }); 33 + const gridRef = useRef<HTMLDivElement>(null); 34 + 35 + const handleWindowDragStart = (e: React.MouseEvent) => { 36 + e.preventDefault(); 37 + setIsDragging(true); 38 + dragStartRef.current = { 39 + x: e.clientX - windowPosition.x, 40 + y: e.clientY - windowPosition.y, 41 + }; 42 + }; 43 + 44 + const handleWindowDrag = (e: MouseEvent) => { 45 + if (!isDragging) return; 46 + setWindowPosition({ 47 + x: e.clientX - dragStartRef.current.x, 48 + y: e.clientY - dragStartRef.current.y, 49 + }); 50 + }; 51 + 52 + const handleWindowDragEnd = () => { 53 + setIsDragging(false); 54 + }; 55 + 56 + useEffect(() => { 57 + if (isDragging) { 58 + window.addEventListener('mousemove', handleWindowDrag); 59 + window.addEventListener('mouseup', handleWindowDragEnd); 60 + return () => { 61 + window.removeEventListener('mousemove', handleWindowDrag); 62 + window.removeEventListener('mouseup', handleWindowDragEnd); 63 + }; 64 + } 65 + }, [isDragging]); 66 + 67 + const handleBallClick = (e: React.MouseEvent, ballIndex: number) => { 68 + e.preventDefault(); 69 + if (!gridRef.current) return; 70 + 71 + const rect = gridRef.current.getBoundingClientRect(); 72 + const ballElement = e.currentTarget as HTMLElement; 73 + const ballRect = ballElement.getBoundingClientRect(); 74 + 75 + const ballCenterX = ballRect.left + ballRect.width / 2 - rect.left; 76 + const ballCenterY = ballRect.top + ballRect.height / 2 - rect.top; 77 + 78 + const ballRadius = 25; 79 + const gridWidth = rect.width; 80 + const gridHeight = rect.height; 81 + 82 + const deviceX = ((ballCenterX - ballRadius) / (gridWidth - ballRadius * 2)) * 400 - 200; 83 + const deviceY = ((ballCenterY - ballRadius) / (gridHeight - ballRadius * 2)) * 400 - 200; 84 + 85 + updateDevicePosition({ x: deviceX, y: deviceY }); 86 + }; 87 + 88 + const resetPosition = () => { 89 + setScale(1); 90 + updateDevicePosition({ x: 0, y: 0, scale: 1 }); 91 + setFixedMargin(false); 92 + setMargin({ top: 35, right: 35, bottom: 35, left: 35 }); 93 + setMarginPreset('medium'); 94 + }; 95 + 96 + const handleMarginPresetChange = (preset: string) => { 97 + if (!preset) return; 98 + setMarginPreset(preset); 99 + let marginValue; 100 + switch (preset) { 101 + case 'small': 102 + marginValue = 20; 103 + break; 104 + case 'medium': 105 + marginValue = 35; 106 + break; 107 + case 'large': 108 + marginValue = 50; 109 + break; 110 + default: 111 + marginValue = 35; 112 + } 113 + setMargin({ top: marginValue, right: marginValue, bottom: marginValue, left: marginValue }); 114 + }; 115 + 116 + const positionBalls = [ 117 + { style: { left: '25px', top: '25px' } }, 118 + { style: { right: '25px', top: '25px' } }, 119 + { style: { left: '25px', bottom: '25px' } }, 120 + { style: { right: '25px', bottom: '25px' } }, 121 + { style: { left: '50%', top: '25px', transform: 'translateX(-50%)' } }, 122 + { style: { left: '50%', bottom: '25px', transform: 'translateX(-50%)' } }, 123 + { style: { left: '25px', top: '50%', transform: 'translateY(-50%)' } }, 124 + { style: { right: '25px', top: '50%', transform: 'translateY(-50%)' } }, 125 + { style: { left: '50%', top: '50%', transform: 'translate(-50%, -50%)' } }, 126 + ]; 127 + 128 + return ( 129 + <div 130 + ref={windowRef} 131 + className="md:fixed z-40 select-none" 132 + style={{ 133 + left: `${windowPosition.x}px`, 134 + top: `${windowPosition.y}px`, 135 + }} 136 + > 137 + <Card className="bg-sidebar max-md:border-none border-sidebar-border rounded-2xl overflow-hidden min-w-80"> 138 + <div className="flex items-center justify-between p-4 bg-sidebar md:cursor-move border-b border-sidebar-border"> 139 + <div className="flex items-center gap-3" onMouseDown={handleWindowDragStart}> 140 + <GripVertical className="w-4 h-4 text-primary/70" /> 141 + <span className="text-white text-lg font-semibold">Position / Scale</span> 142 + </div> 143 + <Button 144 + onClick={resetPosition} 145 + variant="ghost" 146 + size="sm" 147 + className="text-white hover:text-primary hover:bg-primary/20 p-3 rounded-full" 148 + > 149 + <Undo className="w-4 h-4" /> 150 + </Button> 151 + </div> 152 + 153 + <CardContent className="px-6 pt-4 pb-6 bg-sidebar"> 154 + <Tabs value={activeTab} onValueChange={setActiveTab} className="w-full"> 155 + <TabsList className="grid w-full grid-cols-2 bg-sidebar/80 rounded-full ring-2 ring-secondary"> 156 + <TabsTrigger value="position" className="flex items-center gap-2 text-white hover:bg-primary/20 hover:text-primary rounded-r-xl rounded-l-3xl data-[state=active]:bg-primary data-[state=active]:text-black"> 157 + <Move className="w-4 h-4" /> 158 + Position 159 + </TabsTrigger> 160 + <TabsTrigger value="margins" className="flex items-center gap-2 text-white hover:bg-primary/20 hover:text-primary rounded-l-xl rounded-r-3xl data-[state=active]:bg-primary data-[state=active]:text-black"> 161 + <Maximize2 className="w-4 h-4" /> 162 + Margins 163 + </TabsTrigger> 164 + </TabsList> 165 + 166 + <TabsContent value="position" className="mt-4"> 167 + <div 168 + ref={gridRef} 169 + className="relative w-full h-60 bg-primary/10 rounded-xl border border-primary/30 mb-6 overflow-hidden" 170 + > 171 + {positionBalls.map((ball, index) => ( 172 + <div 173 + key={index} 174 + className="absolute size-12 rounded-full cursor-pointer transition-all duration-200 bg-primary/40 hover:bg-primary/60 hover:scale-105 focus-visible:bg-primary focus-visible:scale-110 focus-visible:shadow-lg focus-visible:shadow-primary/50 focus-visible:ring-2 focus-visible:ring-white outline-none" 175 + style={ball.style} 176 + onClick={(e) => handleBallClick(e, index)} 177 + /> 178 + ))} 179 + </div> 180 + 181 + <div className="space-y-3"> 182 + <div className="flex items-center justify-between"> 183 + <span className="text-white text-sm">Scale</span> 184 + <span className="text-primary text-sm">{scale.toFixed(1)}x</span> 185 + </div> 186 + <Slider 187 + value={[scale]} 188 + onValueChange={(value) => { 189 + setScale(value[0]); 190 + updateDevicePosition({ scale: value[0] }); 191 + }} 192 + min={0.1} 193 + max={3} 194 + step={0.1} 195 + /> 196 + </div> 197 + </TabsContent> 198 + 199 + <TabsContent value="margins" className="mt-4"> 200 + <div className="space-y-4"> 201 + <div className="flex items-center justify-between"> 202 + <span className="text-white text-sm">Fixed Margin</span> 203 + <Switch 204 + checked={fixedMargin} 205 + onCheckedChange={(checked) => setFixedMargin(checked)} 206 + className="data-[state=checked]:bg-primary" 207 + /> 208 + </div> 209 + 210 + {fixedMargin && ( 211 + <div className="space-y-4"> 212 + <ToggleGroup 213 + type="single" 214 + value={marginPreset} 215 + onValueChange={handleMarginPresetChange} 216 + className="flex gap-1 bg-sidebar/80 rounded-full ring-2 ring-secondary p-1" 217 + > 218 + <ToggleGroupItem 219 + value="small" 220 + className={`flex-1 text-sm rounded-full hover:bg-primary/20 hover:text-primary data-[state=on]:bg-primary data-[state=on]:text-black cursor-pointer`} 221 + > 222 + Small 223 + </ToggleGroupItem> 224 + <ToggleGroupItem 225 + value="medium" 226 + className={`flex-1 text-sm rounded-full hover:bg-primary/20 hover:text-primary data-[state=on]:bg-primary data-[state=on]:text-black cursor-pointer`} 227 + > 228 + Medium 229 + </ToggleGroupItem> 230 + <ToggleGroupItem 231 + value="large" 232 + className={`flex-1 text-sm rounded-full hover:bg-primary/20 hover:text-primary data-[state=on]:bg-primary data-[state=on]:text-black cursor-pointer`} 233 + > 234 + Large 235 + </ToggleGroupItem> 236 + </ToggleGroup> 237 + <div className="space-y-3"> 238 + <div className="flex items-center justify-between"> 239 + <span className="text-white text-sm">Top Margin</span> 240 + <span className="text-primary text-sm">{Math.round(margin.top)}</span> 241 + </div> 242 + <Slider 243 + value={[margin.top]} 244 + onValueChange={(value) => { 245 + setMargin({ ...margin, top: value[0] }); 246 + setMarginPreset(''); 247 + }} 248 + min={0} 249 + max={100} 250 + step={1} 251 + /> 252 + <div className="flex items-center justify-between"> 253 + <span className="text-white text-sm">Right Margin</span> 254 + <span className="text-primary text-sm">{Math.round(margin.right)}</span> 255 + </div> 256 + <Slider 257 + value={[margin.right]} 258 + onValueChange={(value) => { 259 + setMargin({ ...margin, right: value[0] }); 260 + setMarginPreset(''); 261 + }} 262 + min={0} 263 + max={100} 264 + step={1} 265 + /> 266 + <div className="flex items-center justify-between"> 267 + <span className="text-white text-sm">Bottom Margin</span> 268 + <span className="text-primary text-sm">{Math.round(margin.bottom)}</span> 269 + </div> 270 + <Slider 271 + value={[margin.bottom]} 272 + onValueChange={(value) => { 273 + setMargin({ ...margin, bottom: value[0] }); 274 + setMarginPreset(''); 275 + }} 276 + min={0} 277 + max={100} 278 + step={1} 279 + /> 280 + <div className="flex items-center justify-between"> 281 + <span className="text-white text-sm">Left Margin</span> 282 + <span className="text-primary text-sm">{Math.round(margin.left)}</span> 283 + </div> 284 + <Slider 285 + value={[margin.left]} 286 + onValueChange={(value) => { 287 + setMargin({ ...margin, left: value[0] }); 288 + setMarginPreset(''); 289 + }} 290 + min={0} 291 + max={100} 292 + step={1} 293 + /> 294 + </div> 295 + </div> 296 + )} 297 + </div> 298 + </TabsContent> 299 + </Tabs> 300 + </CardContent> 301 + </Card> 302 + </div> 303 + ); 304 + };
+190
src/components/editor/RotationSkewPanel.tsx
··· 1 + import React, { useState, useRef, useEffect } from 'react'; 2 + import { Card, CardContent } from '@/components/ui/card'; 3 + import { Button } from '@/components/ui/button'; 4 + import { Slider } from '@/components/ui/slider'; 5 + import { GripVertical, Undo, FlipHorizontal, FlipVertical, RotateCw, Move3D, ArrowUp } from 'lucide-react'; 6 + import { useMockupStore } from '@/contexts/MockupContext'; 7 + 8 + 9 + interface RotationSkewPanelProps { 10 + onClose: () => void; 11 + } 12 + 13 + export const RotationSkewPanel: React.FC<RotationSkewPanelProps> = ({ onClose }) => { 14 + // 2. Select only the state and actions this component needs from the store. 15 + // This is more performant than pulling the entire state. 16 + const rotation3D = useMockupStore((state) => state.rotation3D); 17 + const set3DRotation = useMockupStore((state) => state.set3DRotation); 18 + 19 + // Local component state remains the same 20 + const [isDragging, setIsDragging] = useState(false); 21 + const [windowPosition, setWindowPosition] = useState({ x: 100, y: 100 }); 22 + const windowRef = useRef<HTMLDivElement>(null); 23 + const dragStartRef = useRef({ x: 0, y: 0 }); 24 + 25 + // Window dragging logic remains unchanged 26 + const handleWindowDragStart = (e: React.MouseEvent) => { 27 + e.preventDefault(); 28 + setIsDragging(true); 29 + dragStartRef.current = { 30 + x: e.clientX - windowPosition.x, 31 + y: e.clientY - windowPosition.y 32 + }; 33 + }; 34 + 35 + const handleWindowDrag = (e: MouseEvent) => { 36 + if (!isDragging) return; 37 + setWindowPosition({ 38 + x: e.clientX - dragStartRef.current.x, 39 + y: e.clientY - dragStartRef.current.y 40 + }); 41 + }; 42 + 43 + const handleWindowDragEnd = () => { 44 + setIsDragging(false); 45 + }; 46 + 47 + useEffect(() => { 48 + if (isDragging) { 49 + window.addEventListener('mousemove', handleWindowDrag); 50 + window.addEventListener('mouseup', handleWindowDragEnd); 51 + return () => { 52 + window.removeEventListener('mousemove', handleWindowDrag); 53 + window.removeEventListener('mouseup', handleWindowDragEnd); 54 + }; 55 + } 56 + }, [isDragging]); 57 + 58 + // 3. Update action calls to use the new Zustand actions directly. 59 + const handleRotationChange = (type: 'rotateX' | 'rotateY' | 'rotateZ' | 'skew', value: number[]) => { 60 + // OLD: dispatch({ type: 'SET_3D_ROTATION', payload: { [type]: value[0] } }); 61 + set3DRotation({ [type]: value[0] }); 62 + }; 63 + 64 + const resetRotation = () => { 65 + // OLD: dispatch({ type: 'SET_3D_ROTATION', payload: { rotateX: 0, rotateY: 0, rotateZ: 0, skew: 0 } }); 66 + set3DRotation({ rotateX: 0, rotateY: 0, rotateZ: 0, skew: 0 }); 67 + }; 68 + 69 + const presets = [ 70 + { rotateX: 20, rotateY: 0, rotateZ: 0, skew: 0 }, 71 + { rotateX: 30, rotateY: -15, rotateZ: 25, skew: 0 }, 72 + { rotateX: 0, rotateY: 0, rotateZ: 15, skew: 0 }, 73 + { rotateX: -20, rotateY: 0, rotateZ: 0, skew: 0 }, 74 + { rotateX: 0, rotateY: -20, rotateZ: 0, skew: 0 }, 75 + { rotateX: 0, rotateY: 0, rotateZ: -15, skew: 0 }, 76 + ]; 77 + 78 + return ( 79 + <div 80 + ref={windowRef} 81 + className="md:fixed z-40 select-none" 82 + style={{ 83 + left: `${windowPosition.x}px`, 84 + top: `${windowPosition.y}px`, 85 + }} 86 + > 87 + <Card className="bg-sidebar max-md:border-none border-sidebar-border rounded-2xl overflow-hidden min-w-80"> 88 + <div 89 + className="flex items-center justify-between p-4 bg-sidebar md:cursor-move border-b border-sidebar-border" 90 + onMouseDown={handleWindowDragStart} 91 + > 92 + <div className="flex items-center gap-3"> 93 + <GripVertical className="w-4 h-4 text-primary/70" /> 94 + <div className="flex items-center gap-4"> 95 + <span className="text-white text-lg font-semibold">Rotation & Skew</span> 96 + </div> 97 + </div> 98 + <Button onClick={resetRotation} variant="ghost" size="sm" className="text-white hover:text-primary hover:bg-primary/20 p-3 rounded-full"> 99 + <Undo className="w-4 h-4" /> 100 + </Button> 101 + </div> 102 + 103 + <CardContent className="p-6 bg-sidebar"> 104 + <div className="space-y-6"> 105 + <div className="space-y-4"> 106 + {/* 4. Update state access to use the selected state directly (no more 'state.' prefix) */} 107 + <div className="flex items-center gap-4"> 108 + <div className="[&>svg]:text-gray-400 text-sm w-6"><FlipVertical className='size-6' /></div> 109 + <div className="flex-1"> 110 + <Slider 111 + value={[rotation3D.rotateX]} 112 + onValueChange={(value) => handleRotationChange('rotateX', value)} 113 + min={-45} 114 + max={45} 115 + step={1} 116 + /> 117 + </div> 118 + <span className="text-white text-sm w-8 text-right">{rotation3D.rotateX}</span> 119 + </div> 120 + 121 + <div className="flex items-center gap-4"> 122 + <div className="[&>svg]:text-gray-400 text-sm w-6"><FlipHorizontal className='size-6' /></div> 123 + <div className="flex-1"> 124 + <Slider 125 + value={[rotation3D.rotateY]} 126 + onValueChange={(value) => handleRotationChange('rotateY', value)} 127 + min={-45} 128 + max={45} 129 + step={1} 130 + /> 131 + </div> 132 + <span className="text-white text-sm w-8 text-right">{rotation3D.rotateY}</span> 133 + </div> 134 + 135 + <div className="flex items-center gap-4"> 136 + <div className="[&>svg]:text-gray-400 text-sm w-6"><RotateCw className='size-6' /></div> 137 + <div className="flex-1"> 138 + <Slider 139 + value={[rotation3D.rotateZ]} 140 + onValueChange={(value) => handleRotationChange('rotateZ', value)} 141 + min={-180} 142 + max={180} 143 + step={1} 144 + /> 145 + </div> 146 + <span className="text-white text-sm w-8 text-right">{rotation3D.rotateZ}</span> 147 + </div> 148 + 149 + <div className="flex items-center gap-4"> 150 + <div className="[&>svg]:text-gray-400 text-sm w-6"><Move3D className='size-6' /></div> 151 + <div className="flex-1"> 152 + <Slider 153 + value={[rotation3D.skew]} 154 + onValueChange={(value) => handleRotationChange('skew', value)} 155 + min={0} 156 + max={45} 157 + step={1} 158 + /> 159 + </div> 160 + <span className="text-white text-sm w-8 text-right">{rotation3D.skew}</span> 161 + </div> 162 + </div> 163 + 164 + <div className="grid grid-cols-3 gap-2"> 165 + {presets.map((preset, index) => ( 166 + <Button 167 + key={index} 168 + // 5. Update the onClick to call the Zustand action. 169 + // OLD: onClick={() => dispatch({ type: 'SET_3D_ROTATION', payload: preset })} 170 + onClick={() => set3DRotation(preset)} 171 + variant="ghost" 172 + className="h-16 bg-primary/10 hover:bg-primary/20 border border-primary/30 flex flex-col items-center justify-center rounded-lg perspective-near" 173 + > 174 + <div 175 + className="relative w-10 h-8 bg-primary rounded-sm mb-1 flex items-center justify-center [&>svg]:stroke-gray-600" 176 + style={{ 177 + transform: `rotateX(${preset.rotateX}deg) rotateY(${preset.rotateY}deg) rotateZ(${preset.rotateZ}deg)` 178 + }} 179 + > 180 + <ArrowUp /> 181 + </div> 182 + </Button> 183 + ))} 184 + </div> 185 + </div> 186 + </CardContent> 187 + </Card> 188 + </div> 189 + ); 190 + };
+56
src/components/ui/accordion.tsx
··· 1 + import * as React from "react" 2 + import * as AccordionPrimitive from "@radix-ui/react-accordion" 3 + import { ChevronDown } from "lucide-react" 4 + 5 + import { cn } from "@/lib/utils" 6 + 7 + const Accordion = AccordionPrimitive.Root 8 + 9 + const AccordionItem = React.forwardRef< 10 + React.ElementRef<typeof AccordionPrimitive.Item>, 11 + React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Item> 12 + >(({ className, ...props }, ref) => ( 13 + <AccordionPrimitive.Item 14 + ref={ref} 15 + className={cn("border-b", className)} 16 + {...props} 17 + /> 18 + )) 19 + AccordionItem.displayName = "AccordionItem" 20 + 21 + const AccordionTrigger = React.forwardRef< 22 + React.ElementRef<typeof AccordionPrimitive.Trigger>, 23 + React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Trigger> 24 + >(({ className, children, ...props }, ref) => ( 25 + <AccordionPrimitive.Header className="flex"> 26 + <AccordionPrimitive.Trigger 27 + ref={ref} 28 + className={cn( 29 + "flex flex-1 items-center justify-between py-4 font-medium transition-all hover:underline [&[data-state=open]>svg]:rotate-180", 30 + className 31 + )} 32 + {...props} 33 + > 34 + {children} 35 + <ChevronDown className="h-4 w-4 shrink-0 transition-transform duration-200" /> 36 + </AccordionPrimitive.Trigger> 37 + </AccordionPrimitive.Header> 38 + )) 39 + AccordionTrigger.displayName = AccordionPrimitive.Trigger.displayName 40 + 41 + const AccordionContent = React.forwardRef< 42 + React.ElementRef<typeof AccordionPrimitive.Content>, 43 + React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Content> 44 + >(({ className, children, ...props }, ref) => ( 45 + <AccordionPrimitive.Content 46 + ref={ref} 47 + className="overflow-hidden text-sm transition-all data-[state=closed]:animate-accordion-up data-[state=open]:animate-accordion-down" 48 + {...props} 49 + > 50 + <div className={cn("pb-4 pt-0", className)}>{children}</div> 51 + </AccordionPrimitive.Content> 52 + )) 53 + 54 + AccordionContent.displayName = AccordionPrimitive.Content.displayName 55 + 56 + export { Accordion, AccordionItem, AccordionTrigger, AccordionContent }
+139
src/components/ui/alert-dialog.tsx
··· 1 + import * as React from "react" 2 + import * as AlertDialogPrimitive from "@radix-ui/react-alert-dialog" 3 + 4 + import { cn } from "@/lib/utils" 5 + import { buttonVariants } from "@/components/ui/button" 6 + 7 + const AlertDialog = AlertDialogPrimitive.Root 8 + 9 + const AlertDialogTrigger = AlertDialogPrimitive.Trigger 10 + 11 + const AlertDialogPortal = AlertDialogPrimitive.Portal 12 + 13 + const AlertDialogOverlay = React.forwardRef< 14 + React.ElementRef<typeof AlertDialogPrimitive.Overlay>, 15 + React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Overlay> 16 + >(({ className, ...props }, ref) => ( 17 + <AlertDialogPrimitive.Overlay 18 + className={cn( 19 + "fixed inset-0 z-50 bg-black/80 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0", 20 + className 21 + )} 22 + {...props} 23 + ref={ref} 24 + /> 25 + )) 26 + AlertDialogOverlay.displayName = AlertDialogPrimitive.Overlay.displayName 27 + 28 + const AlertDialogContent = React.forwardRef< 29 + React.ElementRef<typeof AlertDialogPrimitive.Content>, 30 + React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Content> 31 + >(({ className, ...props }, ref) => ( 32 + <AlertDialogPortal> 33 + <AlertDialogOverlay /> 34 + <AlertDialogPrimitive.Content 35 + ref={ref} 36 + className={cn( 37 + "fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg", 38 + className 39 + )} 40 + {...props} 41 + /> 42 + </AlertDialogPortal> 43 + )) 44 + AlertDialogContent.displayName = AlertDialogPrimitive.Content.displayName 45 + 46 + const AlertDialogHeader = ({ 47 + className, 48 + ...props 49 + }: React.HTMLAttributes<HTMLDivElement>) => ( 50 + <div 51 + className={cn( 52 + "flex flex-col space-y-2 text-center sm:text-left", 53 + className 54 + )} 55 + {...props} 56 + /> 57 + ) 58 + AlertDialogHeader.displayName = "AlertDialogHeader" 59 + 60 + const AlertDialogFooter = ({ 61 + className, 62 + ...props 63 + }: React.HTMLAttributes<HTMLDivElement>) => ( 64 + <div 65 + className={cn( 66 + "flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2", 67 + className 68 + )} 69 + {...props} 70 + /> 71 + ) 72 + AlertDialogFooter.displayName = "AlertDialogFooter" 73 + 74 + const AlertDialogTitle = React.forwardRef< 75 + React.ElementRef<typeof AlertDialogPrimitive.Title>, 76 + React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Title> 77 + >(({ className, ...props }, ref) => ( 78 + <AlertDialogPrimitive.Title 79 + ref={ref} 80 + className={cn("text-lg font-semibold", className)} 81 + {...props} 82 + /> 83 + )) 84 + AlertDialogTitle.displayName = AlertDialogPrimitive.Title.displayName 85 + 86 + const AlertDialogDescription = React.forwardRef< 87 + React.ElementRef<typeof AlertDialogPrimitive.Description>, 88 + React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Description> 89 + >(({ className, ...props }, ref) => ( 90 + <AlertDialogPrimitive.Description 91 + ref={ref} 92 + className={cn("text-sm text-muted-foreground", className)} 93 + {...props} 94 + /> 95 + )) 96 + AlertDialogDescription.displayName = 97 + AlertDialogPrimitive.Description.displayName 98 + 99 + const AlertDialogAction = React.forwardRef< 100 + React.ElementRef<typeof AlertDialogPrimitive.Action>, 101 + React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Action> 102 + >(({ className, ...props }, ref) => ( 103 + <AlertDialogPrimitive.Action 104 + ref={ref} 105 + className={cn(buttonVariants(), className)} 106 + {...props} 107 + /> 108 + )) 109 + AlertDialogAction.displayName = AlertDialogPrimitive.Action.displayName 110 + 111 + const AlertDialogCancel = React.forwardRef< 112 + React.ElementRef<typeof AlertDialogPrimitive.Cancel>, 113 + React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Cancel> 114 + >(({ className, ...props }, ref) => ( 115 + <AlertDialogPrimitive.Cancel 116 + ref={ref} 117 + className={cn( 118 + buttonVariants({ variant: "outline-solid" }), 119 + "mt-2 sm:mt-0", 120 + className 121 + )} 122 + {...props} 123 + /> 124 + )) 125 + AlertDialogCancel.displayName = AlertDialogPrimitive.Cancel.displayName 126 + 127 + export { 128 + AlertDialog, 129 + AlertDialogPortal, 130 + AlertDialogOverlay, 131 + AlertDialogTrigger, 132 + AlertDialogContent, 133 + AlertDialogHeader, 134 + AlertDialogFooter, 135 + AlertDialogTitle, 136 + AlertDialogDescription, 137 + AlertDialogAction, 138 + AlertDialogCancel, 139 + }
+59
src/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<HTMLDivElement> & VariantProps<typeof alertVariants> 25 + >(({ className, variant, ...props }, ref) => ( 26 + <div 27 + ref={ref} 28 + role="alert" 29 + className={cn(alertVariants({ variant }), className)} 30 + {...props} 31 + /> 32 + )) 33 + Alert.displayName = "Alert" 34 + 35 + const AlertTitle = React.forwardRef< 36 + HTMLParagraphElement, 37 + React.HTMLAttributes<HTMLHeadingElement> 38 + >(({ className, ...props }, ref) => ( 39 + <h5 40 + ref={ref} 41 + className={cn("mb-1 font-medium leading-none tracking-tight", className)} 42 + {...props} 43 + /> 44 + )) 45 + AlertTitle.displayName = "AlertTitle" 46 + 47 + const AlertDescription = React.forwardRef< 48 + HTMLParagraphElement, 49 + React.HTMLAttributes<HTMLParagraphElement> 50 + >(({ className, ...props }, ref) => ( 51 + <div 52 + ref={ref} 53 + className={cn("text-sm [&_p]:leading-relaxed", className)} 54 + {...props} 55 + /> 56 + )) 57 + AlertDescription.displayName = "AlertDescription" 58 + 59 + export { Alert, AlertTitle, AlertDescription }
+5
src/components/ui/aspect-ratio.tsx
··· 1 + import * as AspectRatioPrimitive from "@radix-ui/react-aspect-ratio" 2 + 3 + const AspectRatio = AspectRatioPrimitive.Root 4 + 5 + export { AspectRatio }
+48
src/components/ui/avatar.tsx
··· 1 + import * as React from "react" 2 + import * as AvatarPrimitive from "@radix-ui/react-avatar" 3 + 4 + import { cn } from "@/lib/utils" 5 + 6 + const Avatar = React.forwardRef< 7 + React.ElementRef<typeof AvatarPrimitive.Root>, 8 + React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Root> 9 + >(({ className, ...props }, ref) => ( 10 + <AvatarPrimitive.Root 11 + ref={ref} 12 + className={cn( 13 + "relative flex h-10 w-10 shrink-0 overflow-hidden rounded-full", 14 + className 15 + )} 16 + {...props} 17 + /> 18 + )) 19 + Avatar.displayName = AvatarPrimitive.Root.displayName 20 + 21 + const AvatarImage = React.forwardRef< 22 + React.ElementRef<typeof AvatarPrimitive.Image>, 23 + React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Image> 24 + >(({ className, ...props }, ref) => ( 25 + <AvatarPrimitive.Image 26 + ref={ref} 27 + className={cn("aspect-square h-full w-full", className)} 28 + {...props} 29 + /> 30 + )) 31 + AvatarImage.displayName = AvatarPrimitive.Image.displayName 32 + 33 + const AvatarFallback = React.forwardRef< 34 + React.ElementRef<typeof AvatarPrimitive.Fallback>, 35 + React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Fallback> 36 + >(({ className, ...props }, ref) => ( 37 + <AvatarPrimitive.Fallback 38 + ref={ref} 39 + className={cn( 40 + "flex h-full w-full items-center justify-center rounded-full bg-muted", 41 + className 42 + )} 43 + {...props} 44 + /> 45 + )) 46 + AvatarFallback.displayName = AvatarPrimitive.Fallback.displayName 47 + 48 + export { Avatar, AvatarImage, AvatarFallback }
+36
src/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-hidden 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<HTMLDivElement>, 28 + VariantProps<typeof badgeVariants> {} 29 + 30 + function Badge({ className, variant, ...props }: BadgeProps) { 31 + return ( 32 + <div className={cn(badgeVariants({ variant }), className)} {...props} /> 33 + ) 34 + } 35 + 36 + export { Badge, badgeVariants }
+115
src/components/ui/breadcrumb.tsx
··· 1 + import * as React from "react" 2 + import { Slot } from "@radix-ui/react-slot" 3 + import { ChevronRight, MoreHorizontal } from "lucide-react" 4 + 5 + import { cn } from "@/lib/utils" 6 + 7 + const Breadcrumb = React.forwardRef< 8 + HTMLElement, 9 + React.ComponentPropsWithoutRef<"nav"> & { 10 + separator?: React.ReactNode 11 + } 12 + >(({ ...props }, ref) => <nav ref={ref} aria-label="breadcrumb" {...props} />) 13 + Breadcrumb.displayName = "Breadcrumb" 14 + 15 + const BreadcrumbList = React.forwardRef< 16 + HTMLOListElement, 17 + React.ComponentPropsWithoutRef<"ol"> 18 + >(({ className, ...props }, ref) => ( 19 + <ol 20 + ref={ref} 21 + className={cn( 22 + "flex flex-wrap items-center gap-1.5 break-words text-sm text-muted-foreground sm:gap-2.5", 23 + className 24 + )} 25 + {...props} 26 + /> 27 + )) 28 + BreadcrumbList.displayName = "BreadcrumbList" 29 + 30 + const BreadcrumbItem = React.forwardRef< 31 + HTMLLIElement, 32 + React.ComponentPropsWithoutRef<"li"> 33 + >(({ className, ...props }, ref) => ( 34 + <li 35 + ref={ref} 36 + className={cn("inline-flex items-center gap-1.5", className)} 37 + {...props} 38 + /> 39 + )) 40 + BreadcrumbItem.displayName = "BreadcrumbItem" 41 + 42 + const BreadcrumbLink = React.forwardRef< 43 + HTMLAnchorElement, 44 + React.ComponentPropsWithoutRef<"a"> & { 45 + asChild?: boolean 46 + } 47 + >(({ asChild, className, ...props }, ref) => { 48 + const Comp = asChild ? Slot : "a" 49 + 50 + return ( 51 + <Comp 52 + ref={ref} 53 + className={cn("transition-colors hover:text-foreground", className)} 54 + {...props} 55 + /> 56 + ) 57 + }) 58 + BreadcrumbLink.displayName = "BreadcrumbLink" 59 + 60 + const BreadcrumbPage = React.forwardRef< 61 + HTMLSpanElement, 62 + React.ComponentPropsWithoutRef<"span"> 63 + >(({ className, ...props }, ref) => ( 64 + <span 65 + ref={ref} 66 + role="link" 67 + aria-disabled="true" 68 + aria-current="page" 69 + className={cn("font-normal text-foreground", className)} 70 + {...props} 71 + /> 72 + )) 73 + BreadcrumbPage.displayName = "BreadcrumbPage" 74 + 75 + const BreadcrumbSeparator = ({ 76 + children, 77 + className, 78 + ...props 79 + }: React.ComponentProps<"li">) => ( 80 + <li 81 + role="presentation" 82 + aria-hidden="true" 83 + className={cn("[&>svg]:size-3.5", className)} 84 + {...props} 85 + > 86 + {children ?? <ChevronRight />} 87 + </li> 88 + ) 89 + BreadcrumbSeparator.displayName = "BreadcrumbSeparator" 90 + 91 + const BreadcrumbEllipsis = ({ 92 + className, 93 + ...props 94 + }: React.ComponentProps<"span">) => ( 95 + <span 96 + role="presentation" 97 + aria-hidden="true" 98 + className={cn("flex h-9 w-9 items-center justify-center", className)} 99 + {...props} 100 + > 101 + <MoreHorizontal className="h-4 w-4" /> 102 + <span className="sr-only">More</span> 103 + </span> 104 + ) 105 + BreadcrumbEllipsis.displayName = "BreadcrumbElipssis" 106 + 107 + export { 108 + Breadcrumb, 109 + BreadcrumbList, 110 + BreadcrumbItem, 111 + BreadcrumbLink, 112 + BreadcrumbPage, 113 + BreadcrumbSeparator, 114 + BreadcrumbEllipsis, 115 + }
+56
src/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 cursor-pointer rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-hidden 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-5 [&_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<HTMLButtonElement>, 38 + VariantProps<typeof buttonVariants> { 39 + asChild?: boolean 40 + } 41 + 42 + const Button = React.forwardRef<HTMLButtonElement, ButtonProps>( 43 + ({ className, variant, size, asChild = false, ...props }, ref) => { 44 + const Comp = asChild ? Slot : "button" 45 + return ( 46 + <Comp 47 + className={cn(buttonVariants({ variant, size, className }))} 48 + ref={ref} 49 + {...props} 50 + /> 51 + ) 52 + } 53 + ) 54 + Button.displayName = "Button" 55 + 56 + export { Button, buttonVariants }
+64
src/components/ui/calendar.tsx
··· 1 + import * as React from "react"; 2 + import { ChevronLeft, ChevronRight } from "lucide-react"; 3 + import { DayPicker } from "react-day-picker"; 4 + 5 + import { cn } from "@/lib/utils"; 6 + import { buttonVariants } from "@/components/ui/button"; 7 + 8 + export type CalendarProps = React.ComponentProps<typeof DayPicker>; 9 + 10 + function Calendar({ 11 + className, 12 + classNames, 13 + showOutsideDays = true, 14 + ...props 15 + }: CalendarProps) { 16 + return ( 17 + <DayPicker 18 + showOutsideDays={showOutsideDays} 19 + className={cn("p-3", className)} 20 + classNames={{ 21 + months: "flex flex-col sm:flex-row space-y-4 sm:space-x-4 sm:space-y-0", 22 + month: "space-y-4", 23 + caption: "flex justify-center pt-1 relative items-center", 24 + caption_label: "text-sm font-medium", 25 + nav: "space-x-1 flex items-center", 26 + nav_button: cn( 27 + buttonVariants({ variant: "outline-solid" }), 28 + "h-7 w-7 bg-transparent p-0 opacity-50 hover:opacity-100" 29 + ), 30 + nav_button_previous: "absolute left-1", 31 + nav_button_next: "absolute right-1", 32 + table: "w-full border-collapse space-y-1", 33 + head_row: "flex", 34 + head_cell: 35 + "text-muted-foreground rounded-md w-9 font-normal text-[0.8rem]", 36 + row: "flex w-full mt-2", 37 + cell: "h-9 w-9 text-center text-sm p-0 relative [&:has([aria-selected].day-range-end)]:rounded-r-md [&:has([aria-selected].day-outside)]:bg-accent/50 [&:has([aria-selected])]:bg-accent first:[&:has([aria-selected])]:rounded-l-md last:[&:has([aria-selected])]:rounded-r-md focus-within:relative focus-within:z-20", 38 + day: cn( 39 + buttonVariants({ variant: "ghost" }), 40 + "h-9 w-9 p-0 font-normal aria-selected:opacity-100" 41 + ), 42 + day_range_end: "day-range-end", 43 + day_selected: 44 + "bg-primary text-primary-foreground hover:bg-primary hover:text-primary-foreground focus:bg-primary focus:text-primary-foreground", 45 + day_today: "bg-accent text-accent-foreground", 46 + day_outside: 47 + "day-outside text-muted-foreground opacity-50 aria-selected:bg-accent/50 aria-selected:text-muted-foreground aria-selected:opacity-30", 48 + day_disabled: "text-muted-foreground opacity-50", 49 + day_range_middle: 50 + "aria-selected:bg-accent aria-selected:text-accent-foreground", 51 + day_hidden: "invisible", 52 + ...classNames, 53 + }} 54 + components={{ 55 + IconLeft: ({ ..._props }) => <ChevronLeft className="h-4 w-4" />, 56 + IconRight: ({ ..._props }) => <ChevronRight className="h-4 w-4" />, 57 + }} 58 + {...props} 59 + /> 60 + ); 61 + } 62 + Calendar.displayName = "Calendar"; 63 + 64 + export { Calendar };
+79
src/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<HTMLDivElement> 8 + >(({ className, ...props }, ref) => ( 9 + <div 10 + ref={ref} 11 + className={cn( 12 + "rounded-lg border bg-card text-card-foreground shadow-xs", 13 + className 14 + )} 15 + {...props} 16 + /> 17 + )) 18 + Card.displayName = "Card" 19 + 20 + const CardHeader = React.forwardRef< 21 + HTMLDivElement, 22 + React.HTMLAttributes<HTMLDivElement> 23 + >(({ className, ...props }, ref) => ( 24 + <div 25 + ref={ref} 26 + className={cn("flex flex-col space-y-1.5 p-6", className)} 27 + {...props} 28 + /> 29 + )) 30 + CardHeader.displayName = "CardHeader" 31 + 32 + const CardTitle = React.forwardRef< 33 + HTMLParagraphElement, 34 + React.HTMLAttributes<HTMLHeadingElement> 35 + >(({ className, ...props }, ref) => ( 36 + <h3 37 + ref={ref} 38 + className={cn( 39 + "text-2xl font-semibold leading-none tracking-tight", 40 + className 41 + )} 42 + {...props} 43 + /> 44 + )) 45 + CardTitle.displayName = "CardTitle" 46 + 47 + const CardDescription = React.forwardRef< 48 + HTMLParagraphElement, 49 + React.HTMLAttributes<HTMLParagraphElement> 50 + >(({ className, ...props }, ref) => ( 51 + <p 52 + ref={ref} 53 + className={cn("text-sm text-muted-foreground", className)} 54 + {...props} 55 + /> 56 + )) 57 + CardDescription.displayName = "CardDescription" 58 + 59 + const CardContent = React.forwardRef< 60 + HTMLDivElement, 61 + React.HTMLAttributes<HTMLDivElement> 62 + >(({ className, ...props }, ref) => ( 63 + <div ref={ref} className={cn("p-6 pt-0", className)} {...props} /> 64 + )) 65 + CardContent.displayName = "CardContent" 66 + 67 + const CardFooter = React.forwardRef< 68 + HTMLDivElement, 69 + React.HTMLAttributes<HTMLDivElement> 70 + >(({ className, ...props }, ref) => ( 71 + <div 72 + ref={ref} 73 + className={cn("flex items-center p-6 pt-0", className)} 74 + {...props} 75 + /> 76 + )) 77 + CardFooter.displayName = "CardFooter" 78 + 79 + export { Card, CardHeader, CardFooter, CardTitle, CardDescription, CardContent }
+260
src/components/ui/carousel.tsx
··· 1 + import * as React from "react" 2 + import useEmblaCarousel, { 3 + type UseEmblaCarouselType, 4 + } from "embla-carousel-react" 5 + import { ArrowLeft, ArrowRight } from "lucide-react" 6 + 7 + import { cn } from "@/lib/utils" 8 + import { Button } from "@/components/ui/button" 9 + 10 + type CarouselApi = UseEmblaCarouselType[1] 11 + type UseCarouselParameters = Parameters<typeof useEmblaCarousel> 12 + type CarouselOptions = UseCarouselParameters[0] 13 + type CarouselPlugin = UseCarouselParameters[1] 14 + 15 + type CarouselProps = { 16 + opts?: CarouselOptions 17 + plugins?: CarouselPlugin 18 + orientation?: "horizontal" | "vertical" 19 + setApi?: (api: CarouselApi) => void 20 + } 21 + 22 + type CarouselContextProps = { 23 + carouselRef: ReturnType<typeof useEmblaCarousel>[0] 24 + api: ReturnType<typeof useEmblaCarousel>[1] 25 + scrollPrev: () => void 26 + scrollNext: () => void 27 + canScrollPrev: boolean 28 + canScrollNext: boolean 29 + } & CarouselProps 30 + 31 + const CarouselContext = React.createContext<CarouselContextProps | null>(null) 32 + 33 + function useCarousel() { 34 + const context = React.useContext(CarouselContext) 35 + 36 + if (!context) { 37 + throw new Error("useCarousel must be used within a <Carousel />") 38 + } 39 + 40 + return context 41 + } 42 + 43 + const Carousel = React.forwardRef< 44 + HTMLDivElement, 45 + React.HTMLAttributes<HTMLDivElement> & CarouselProps 46 + >( 47 + ( 48 + { 49 + orientation = "horizontal", 50 + opts, 51 + setApi, 52 + plugins, 53 + className, 54 + children, 55 + ...props 56 + }, 57 + ref 58 + ) => { 59 + const [carouselRef, api] = useEmblaCarousel( 60 + { 61 + ...opts, 62 + axis: orientation === "horizontal" ? "x" : "y", 63 + }, 64 + plugins 65 + ) 66 + const [canScrollPrev, setCanScrollPrev] = React.useState(false) 67 + const [canScrollNext, setCanScrollNext] = React.useState(false) 68 + 69 + const onSelect = React.useCallback((api: CarouselApi) => { 70 + if (!api) { 71 + return 72 + } 73 + 74 + setCanScrollPrev(api.canScrollPrev()) 75 + setCanScrollNext(api.canScrollNext()) 76 + }, []) 77 + 78 + const scrollPrev = React.useCallback(() => { 79 + api?.scrollPrev() 80 + }, [api]) 81 + 82 + const scrollNext = React.useCallback(() => { 83 + api?.scrollNext() 84 + }, [api]) 85 + 86 + const handleKeyDown = React.useCallback( 87 + (event: React.KeyboardEvent<HTMLDivElement>) => { 88 + if (event.key === "ArrowLeft") { 89 + event.preventDefault() 90 + scrollPrev() 91 + } else if (event.key === "ArrowRight") { 92 + event.preventDefault() 93 + scrollNext() 94 + } 95 + }, 96 + [scrollPrev, scrollNext] 97 + ) 98 + 99 + React.useEffect(() => { 100 + if (!api || !setApi) { 101 + return 102 + } 103 + 104 + setApi(api) 105 + }, [api, setApi]) 106 + 107 + React.useEffect(() => { 108 + if (!api) { 109 + return 110 + } 111 + 112 + onSelect(api) 113 + api.on("reInit", onSelect) 114 + api.on("select", onSelect) 115 + 116 + return () => { 117 + api?.off("select", onSelect) 118 + } 119 + }, [api, onSelect]) 120 + 121 + return ( 122 + <CarouselContext.Provider 123 + value={{ 124 + carouselRef, 125 + api: api, 126 + opts, 127 + orientation: 128 + orientation || (opts?.axis === "y" ? "vertical" : "horizontal"), 129 + scrollPrev, 130 + scrollNext, 131 + canScrollPrev, 132 + canScrollNext, 133 + }} 134 + > 135 + <div 136 + ref={ref} 137 + onKeyDownCapture={handleKeyDown} 138 + className={cn("relative", className)} 139 + role="region" 140 + aria-roledescription="carousel" 141 + {...props} 142 + > 143 + {children} 144 + </div> 145 + </CarouselContext.Provider> 146 + ) 147 + } 148 + ) 149 + Carousel.displayName = "Carousel" 150 + 151 + const CarouselContent = React.forwardRef< 152 + HTMLDivElement, 153 + React.HTMLAttributes<HTMLDivElement> 154 + >(({ className, ...props }, ref) => { 155 + const { carouselRef, orientation } = useCarousel() 156 + 157 + return ( 158 + <div ref={carouselRef} className="overflow-hidden"> 159 + <div 160 + ref={ref} 161 + className={cn( 162 + "flex", 163 + orientation === "horizontal" ? "-ml-4" : "-mt-4 flex-col", 164 + className 165 + )} 166 + {...props} 167 + /> 168 + </div> 169 + ) 170 + }) 171 + CarouselContent.displayName = "CarouselContent" 172 + 173 + const CarouselItem = React.forwardRef< 174 + HTMLDivElement, 175 + React.HTMLAttributes<HTMLDivElement> 176 + >(({ className, ...props }, ref) => { 177 + const { orientation } = useCarousel() 178 + 179 + return ( 180 + <div 181 + ref={ref} 182 + role="group" 183 + aria-roledescription="slide" 184 + className={cn( 185 + "min-w-0 shrink-0 grow-0 basis-full", 186 + orientation === "horizontal" ? "pl-4" : "pt-4", 187 + className 188 + )} 189 + {...props} 190 + /> 191 + ) 192 + }) 193 + CarouselItem.displayName = "CarouselItem" 194 + 195 + const CarouselPrevious = React.forwardRef< 196 + HTMLButtonElement, 197 + React.ComponentProps<typeof Button> 198 + >(({ className, variant = "outline-solid", size = "icon", ...props }, ref) => { 199 + const { orientation, scrollPrev, canScrollPrev } = useCarousel() 200 + 201 + return ( 202 + <Button 203 + ref={ref} 204 + variant={variant} 205 + size={size} 206 + className={cn( 207 + "absolute h-8 w-8 rounded-full", 208 + orientation === "horizontal" 209 + ? "-left-12 top-1/2 -translate-y-1/2" 210 + : "-top-12 left-1/2 -translate-x-1/2 rotate-90", 211 + className 212 + )} 213 + disabled={!canScrollPrev} 214 + onClick={scrollPrev} 215 + {...props} 216 + > 217 + <ArrowLeft className="h-4 w-4" /> 218 + <span className="sr-only">Previous slide</span> 219 + </Button> 220 + ) 221 + }) 222 + CarouselPrevious.displayName = "CarouselPrevious" 223 + 224 + const CarouselNext = React.forwardRef< 225 + HTMLButtonElement, 226 + React.ComponentProps<typeof Button> 227 + >(({ className, variant = "outline-solid", size = "icon", ...props }, ref) => { 228 + const { orientation, scrollNext, canScrollNext } = useCarousel() 229 + 230 + return ( 231 + <Button 232 + ref={ref} 233 + variant={variant} 234 + size={size} 235 + className={cn( 236 + "absolute h-8 w-8 rounded-full", 237 + orientation === "horizontal" 238 + ? "-right-12 top-1/2 -translate-y-1/2" 239 + : "-bottom-12 left-1/2 -translate-x-1/2 rotate-90", 240 + className 241 + )} 242 + disabled={!canScrollNext} 243 + onClick={scrollNext} 244 + {...props} 245 + > 246 + <ArrowRight className="h-4 w-4" /> 247 + <span className="sr-only">Next slide</span> 248 + </Button> 249 + ) 250 + }) 251 + CarouselNext.displayName = "CarouselNext" 252 + 253 + export { 254 + type CarouselApi, 255 + Carousel, 256 + CarouselContent, 257 + CarouselItem, 258 + CarouselPrevious, 259 + CarouselNext, 260 + }
+363
src/components/ui/chart.tsx
··· 1 + import * as React from "react" 2 + import * as RechartsPrimitive from "recharts" 3 + 4 + import { cn } from "@/lib/utils" 5 + 6 + // Format: { THEME_NAME: CSS_SELECTOR } 7 + const THEMES = { light: "", dark: ".dark" } as const 8 + 9 + export type ChartConfig = { 10 + [k in string]: { 11 + label?: React.ReactNode 12 + icon?: React.ComponentType 13 + } & ( 14 + | { color?: string; theme?: never } 15 + | { color?: never; theme: Record<keyof typeof THEMES, string> } 16 + ) 17 + } 18 + 19 + type ChartContextProps = { 20 + config: ChartConfig 21 + } 22 + 23 + const ChartContext = React.createContext<ChartContextProps | null>(null) 24 + 25 + function useChart() { 26 + const context = React.useContext(ChartContext) 27 + 28 + if (!context) { 29 + throw new Error("useChart must be used within a <ChartContainer />") 30 + } 31 + 32 + return context 33 + } 34 + 35 + const ChartContainer = React.forwardRef< 36 + HTMLDivElement, 37 + React.ComponentProps<"div"> & { 38 + config: ChartConfig 39 + children: React.ComponentProps< 40 + typeof RechartsPrimitive.ResponsiveContainer 41 + >["children"] 42 + } 43 + >(({ id, className, children, config, ...props }, ref) => { 44 + const uniqueId = React.useId() 45 + const chartId = `chart-${id || uniqueId.replace(/:/g, "")}` 46 + 47 + return ( 48 + <ChartContext.Provider value={{ config }}> 49 + <div 50 + data-chart={chartId} 51 + ref={ref} 52 + className={cn( 53 + "flex aspect-video justify-center text-xs [&_.recharts-cartesian-axis-tick_text]:fill-muted-foreground [&_.recharts-cartesian-grid_line[stroke='#ccc']]:stroke-border/50 [&_.recharts-curve.recharts-tooltip-cursor]:stroke-border [&_.recharts-dot[stroke='#fff']]:stroke-transparent [&_.recharts-layer]:outline-hidden [&_.recharts-polar-grid_[stroke='#ccc']]:stroke-border [&_.recharts-radial-bar-background-sector]:fill-muted [&_.recharts-rectangle.recharts-tooltip-cursor]:fill-muted [&_.recharts-reference-line_[stroke='#ccc']]:stroke-border [&_.recharts-sector[stroke='#fff']]:stroke-transparent [&_.recharts-sector]:outline-hidden [&_.recharts-surface]:outline-hidden", 54 + className 55 + )} 56 + {...props} 57 + > 58 + <ChartStyle id={chartId} config={config} /> 59 + <RechartsPrimitive.ResponsiveContainer> 60 + {children} 61 + </RechartsPrimitive.ResponsiveContainer> 62 + </div> 63 + </ChartContext.Provider> 64 + ) 65 + }) 66 + ChartContainer.displayName = "Chart" 67 + 68 + const ChartStyle = ({ id, config }: { id: string; config: ChartConfig }) => { 69 + const colorConfig = Object.entries(config).filter( 70 + ([_, config]) => config.theme || config.color 71 + ) 72 + 73 + if (!colorConfig.length) { 74 + return null 75 + } 76 + 77 + return ( 78 + <style 79 + dangerouslySetInnerHTML={{ 80 + __html: Object.entries(THEMES) 81 + .map( 82 + ([theme, prefix]) => ` 83 + ${prefix} [data-chart=${id}] { 84 + ${colorConfig 85 + .map(([key, itemConfig]) => { 86 + const color = 87 + itemConfig.theme?.[theme as keyof typeof itemConfig.theme] || 88 + itemConfig.color 89 + return color ? ` --color-${key}: ${color};` : null 90 + }) 91 + .join("\n")} 92 + } 93 + ` 94 + ) 95 + .join("\n"), 96 + }} 97 + /> 98 + ) 99 + } 100 + 101 + const ChartTooltip = RechartsPrimitive.Tooltip 102 + 103 + const ChartTooltipContent = React.forwardRef< 104 + HTMLDivElement, 105 + React.ComponentProps<typeof RechartsPrimitive.Tooltip> & 106 + React.ComponentProps<"div"> & { 107 + hideLabel?: boolean 108 + hideIndicator?: boolean 109 + indicator?: "line" | "dot" | "dashed" 110 + nameKey?: string 111 + labelKey?: string 112 + } 113 + >( 114 + ( 115 + { 116 + active, 117 + payload, 118 + className, 119 + indicator = "dot", 120 + hideLabel = false, 121 + hideIndicator = false, 122 + label, 123 + labelFormatter, 124 + labelClassName, 125 + formatter, 126 + color, 127 + nameKey, 128 + labelKey, 129 + }, 130 + ref 131 + ) => { 132 + const { config } = useChart() 133 + 134 + const tooltipLabel = React.useMemo(() => { 135 + if (hideLabel || !payload?.length) { 136 + return null 137 + } 138 + 139 + const [item] = payload 140 + const key = `${labelKey || item.dataKey || item.name || "value"}` 141 + const itemConfig = getPayloadConfigFromPayload(config, item, key) 142 + const value = 143 + !labelKey && typeof label === "string" 144 + ? config[label as keyof typeof config]?.label || label 145 + : itemConfig?.label 146 + 147 + if (labelFormatter) { 148 + return ( 149 + <div className={cn("font-medium", labelClassName)}> 150 + {labelFormatter(value, payload)} 151 + </div> 152 + ) 153 + } 154 + 155 + if (!value) { 156 + return null 157 + } 158 + 159 + return <div className={cn("font-medium", labelClassName)}>{value}</div> 160 + }, [ 161 + label, 162 + labelFormatter, 163 + payload, 164 + hideLabel, 165 + labelClassName, 166 + config, 167 + labelKey, 168 + ]) 169 + 170 + if (!active || !payload?.length) { 171 + return null 172 + } 173 + 174 + const nestLabel = payload.length === 1 && indicator !== "dot" 175 + 176 + return ( 177 + <div 178 + ref={ref} 179 + className={cn( 180 + "grid min-w-32 items-start gap-1.5 rounded-lg border border-border/50 bg-background px-2.5 py-1.5 text-xs shadow-xl", 181 + className 182 + )} 183 + > 184 + {!nestLabel ? tooltipLabel : null} 185 + <div className="grid gap-1.5"> 186 + {payload.map((item, index) => { 187 + const key = `${nameKey || item.name || item.dataKey || "value"}` 188 + const itemConfig = getPayloadConfigFromPayload(config, item, key) 189 + const indicatorColor = color || item.payload.fill || item.color 190 + 191 + return ( 192 + <div 193 + key={item.dataKey} 194 + className={cn( 195 + "flex w-full flex-wrap items-stretch gap-2 [&>svg]:h-2.5 [&>svg]:w-2.5 [&>svg]:text-muted-foreground", 196 + indicator === "dot" && "items-center" 197 + )} 198 + > 199 + {formatter && item?.value !== undefined && item.name ? ( 200 + formatter(item.value, item.name, item, index, item.payload) 201 + ) : ( 202 + <> 203 + {itemConfig?.icon ? ( 204 + <itemConfig.icon /> 205 + ) : ( 206 + !hideIndicator && ( 207 + <div 208 + className={cn( 209 + "shrink-0 rounded-[2px] border-border bg-(--color-bg)", 210 + { 211 + "h-2.5 w-2.5": indicator === "dot", 212 + "w-1": indicator === "line", 213 + "w-0 border-[1.5px] border-dashed bg-transparent": 214 + indicator === "dashed", 215 + "my-0.5": nestLabel && indicator === "dashed", 216 + } 217 + )} 218 + style={ 219 + { 220 + "--color-bg": indicatorColor, 221 + "--color-border": indicatorColor, 222 + } as React.CSSProperties 223 + } 224 + /> 225 + ) 226 + )} 227 + <div 228 + className={cn( 229 + "flex flex-1 justify-between leading-none", 230 + nestLabel ? "items-end" : "items-center" 231 + )} 232 + > 233 + <div className="grid gap-1.5"> 234 + {nestLabel ? tooltipLabel : null} 235 + <span className="text-muted-foreground"> 236 + {itemConfig?.label || item.name} 237 + </span> 238 + </div> 239 + {item.value && ( 240 + <span className="font-mono font-medium tabular-nums text-foreground"> 241 + {item.value.toLocaleString()} 242 + </span> 243 + )} 244 + </div> 245 + </> 246 + )} 247 + </div> 248 + ) 249 + })} 250 + </div> 251 + </div> 252 + ) 253 + } 254 + ) 255 + ChartTooltipContent.displayName = "ChartTooltip" 256 + 257 + const ChartLegend = RechartsPrimitive.Legend 258 + 259 + const ChartLegendContent = React.forwardRef< 260 + HTMLDivElement, 261 + React.ComponentProps<"div"> & 262 + Pick<RechartsPrimitive.LegendProps, "payload" | "verticalAlign"> & { 263 + hideIcon?: boolean 264 + nameKey?: string 265 + } 266 + >( 267 + ( 268 + { className, hideIcon = false, payload, verticalAlign = "bottom", nameKey }, 269 + ref 270 + ) => { 271 + const { config } = useChart() 272 + 273 + if (!payload?.length) { 274 + return null 275 + } 276 + 277 + return ( 278 + <div 279 + ref={ref} 280 + className={cn( 281 + "flex items-center justify-center gap-4", 282 + verticalAlign === "top" ? "pb-3" : "pt-3", 283 + className 284 + )} 285 + > 286 + {payload.map((item) => { 287 + const key = `${nameKey || item.dataKey || "value"}` 288 + const itemConfig = getPayloadConfigFromPayload(config, item, key) 289 + 290 + return ( 291 + <div 292 + key={item.value} 293 + className={cn( 294 + "flex items-center gap-1.5 [&>svg]:h-3 [&>svg]:w-3 [&>svg]:text-muted-foreground" 295 + )} 296 + > 297 + {itemConfig?.icon && !hideIcon ? ( 298 + <itemConfig.icon /> 299 + ) : ( 300 + <div 301 + className="h-2 w-2 shrink-0 rounded-[2px]" 302 + style={{ 303 + backgroundColor: item.color, 304 + }} 305 + /> 306 + )} 307 + {itemConfig?.label} 308 + </div> 309 + ) 310 + })} 311 + </div> 312 + ) 313 + } 314 + ) 315 + ChartLegendContent.displayName = "ChartLegend" 316 + 317 + // Helper to extract item config from a payload. 318 + function getPayloadConfigFromPayload( 319 + config: ChartConfig, 320 + payload: unknown, 321 + key: string 322 + ) { 323 + if (typeof payload !== "object" || payload === null) { 324 + return undefined 325 + } 326 + 327 + const payloadPayload = 328 + "payload" in payload && 329 + typeof payload.payload === "object" && 330 + payload.payload !== null 331 + ? payload.payload 332 + : undefined 333 + 334 + let configLabelKey: string = key 335 + 336 + if ( 337 + key in payload && 338 + typeof payload[key as keyof typeof payload] === "string" 339 + ) { 340 + configLabelKey = payload[key as keyof typeof payload] as string 341 + } else if ( 342 + payloadPayload && 343 + key in payloadPayload && 344 + typeof payloadPayload[key as keyof typeof payloadPayload] === "string" 345 + ) { 346 + configLabelKey = payloadPayload[ 347 + key as keyof typeof payloadPayload 348 + ] as string 349 + } 350 + 351 + return configLabelKey in config 352 + ? config[configLabelKey] 353 + : config[key as keyof typeof config] 354 + } 355 + 356 + export { 357 + ChartContainer, 358 + ChartTooltip, 359 + ChartTooltipContent, 360 + ChartLegend, 361 + ChartLegendContent, 362 + ChartStyle, 363 + }
+28
src/components/ui/checkbox.tsx
··· 1 + import * as React from "react" 2 + import * as CheckboxPrimitive from "@radix-ui/react-checkbox" 3 + import { Check } from "lucide-react" 4 + 5 + import { cn } from "@/lib/utils" 6 + 7 + const Checkbox = React.forwardRef< 8 + React.ElementRef<typeof CheckboxPrimitive.Root>, 9 + React.ComponentPropsWithoutRef<typeof CheckboxPrimitive.Root> 10 + >(({ className, ...props }, ref) => ( 11 + <CheckboxPrimitive.Root 12 + ref={ref} 13 + className={cn( 14 + "peer h-4 w-4 shrink-0 rounded-sm border border-primary ring-offset-background focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground", 15 + className 16 + )} 17 + {...props} 18 + > 19 + <CheckboxPrimitive.Indicator 20 + className={cn("flex items-center justify-center text-current")} 21 + > 22 + <Check className="h-4 w-4" /> 23 + </CheckboxPrimitive.Indicator> 24 + </CheckboxPrimitive.Root> 25 + )) 26 + Checkbox.displayName = CheckboxPrimitive.Root.displayName 27 + 28 + export { Checkbox }
+9
src/components/ui/collapsible.tsx
··· 1 + import * as CollapsiblePrimitive from "@radix-ui/react-collapsible" 2 + 3 + const Collapsible = CollapsiblePrimitive.Root 4 + 5 + const CollapsibleTrigger = CollapsiblePrimitive.CollapsibleTrigger 6 + 7 + const CollapsibleContent = CollapsiblePrimitive.CollapsibleContent 8 + 9 + export { Collapsible, CollapsibleTrigger, CollapsibleContent }
+153
src/components/ui/command.tsx
··· 1 + import * as React from "react" 2 + import { type DialogProps } from "@radix-ui/react-dialog" 3 + import { Command as CommandPrimitive } from "cmdk" 4 + import { Search } from "lucide-react" 5 + 6 + import { cn } from "@/lib/utils" 7 + import { Dialog, DialogContent } from "@/components/ui/dialog" 8 + 9 + const Command = React.forwardRef< 10 + React.ElementRef<typeof CommandPrimitive>, 11 + React.ComponentPropsWithoutRef<typeof CommandPrimitive> 12 + >(({ className, ...props }, ref) => ( 13 + <CommandPrimitive 14 + ref={ref} 15 + className={cn( 16 + "flex h-full w-full flex-col overflow-hidden rounded-md bg-popover text-popover-foreground", 17 + className 18 + )} 19 + {...props} 20 + /> 21 + )) 22 + Command.displayName = CommandPrimitive.displayName 23 + 24 + interface CommandDialogProps extends DialogProps {} 25 + 26 + const CommandDialog = ({ children, ...props }: CommandDialogProps) => { 27 + return ( 28 + <Dialog {...props}> 29 + <DialogContent className="overflow-hidden p-0 shadow-lg"> 30 + <Command className="[&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:font-medium [&_[cmdk-group-heading]]:text-muted-foreground [&_[cmdk-group]:not([hidden])_~[cmdk-group]]:pt-0 [&_[cmdk-group]]:px-2 [&_[cmdk-input-wrapper]_svg]:h-5 [&_[cmdk-input-wrapper]_svg]:w-5 [&_[cmdk-input]]:h-12 [&_[cmdk-item]]:px-2 [&_[cmdk-item]]:py-3 [&_[cmdk-item]_svg]:h-5 [&_[cmdk-item]_svg]:w-5"> 31 + {children} 32 + </Command> 33 + </DialogContent> 34 + </Dialog> 35 + ) 36 + } 37 + 38 + const CommandInput = React.forwardRef< 39 + React.ElementRef<typeof CommandPrimitive.Input>, 40 + React.ComponentPropsWithoutRef<typeof CommandPrimitive.Input> 41 + >(({ className, ...props }, ref) => ( 42 + <div className="flex items-center border-b px-3" cmdk-input-wrapper=""> 43 + <Search className="mr-2 h-4 w-4 shrink-0 opacity-50" /> 44 + <CommandPrimitive.Input 45 + ref={ref} 46 + className={cn( 47 + "flex h-11 w-full rounded-md bg-transparent py-3 text-sm outline-hidden placeholder:text-muted-foreground disabled:cursor-not-allowed disabled:opacity-50", 48 + className 49 + )} 50 + {...props} 51 + /> 52 + </div> 53 + )) 54 + 55 + CommandInput.displayName = CommandPrimitive.Input.displayName 56 + 57 + const CommandList = React.forwardRef< 58 + React.ElementRef<typeof CommandPrimitive.List>, 59 + React.ComponentPropsWithoutRef<typeof CommandPrimitive.List> 60 + >(({ className, ...props }, ref) => ( 61 + <CommandPrimitive.List 62 + ref={ref} 63 + className={cn("max-h-[300px] overflow-y-auto overflow-x-hidden", className)} 64 + {...props} 65 + /> 66 + )) 67 + 68 + CommandList.displayName = CommandPrimitive.List.displayName 69 + 70 + const CommandEmpty = React.forwardRef< 71 + React.ElementRef<typeof CommandPrimitive.Empty>, 72 + React.ComponentPropsWithoutRef<typeof CommandPrimitive.Empty> 73 + >((props, ref) => ( 74 + <CommandPrimitive.Empty 75 + ref={ref} 76 + className="py-6 text-center text-sm" 77 + {...props} 78 + /> 79 + )) 80 + 81 + CommandEmpty.displayName = CommandPrimitive.Empty.displayName 82 + 83 + const CommandGroup = React.forwardRef< 84 + React.ElementRef<typeof CommandPrimitive.Group>, 85 + React.ComponentPropsWithoutRef<typeof CommandPrimitive.Group> 86 + >(({ className, ...props }, ref) => ( 87 + <CommandPrimitive.Group 88 + ref={ref} 89 + className={cn( 90 + "overflow-hidden p-1 text-foreground [&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:py-1.5 [&_[cmdk-group-heading]]:text-xs [&_[cmdk-group-heading]]:font-medium [&_[cmdk-group-heading]]:text-muted-foreground", 91 + className 92 + )} 93 + {...props} 94 + /> 95 + )) 96 + 97 + CommandGroup.displayName = CommandPrimitive.Group.displayName 98 + 99 + const CommandSeparator = React.forwardRef< 100 + React.ElementRef<typeof CommandPrimitive.Separator>, 101 + React.ComponentPropsWithoutRef<typeof CommandPrimitive.Separator> 102 + >(({ className, ...props }, ref) => ( 103 + <CommandPrimitive.Separator 104 + ref={ref} 105 + className={cn("-mx-1 h-px bg-border", className)} 106 + {...props} 107 + /> 108 + )) 109 + CommandSeparator.displayName = CommandPrimitive.Separator.displayName 110 + 111 + const CommandItem = React.forwardRef< 112 + React.ElementRef<typeof CommandPrimitive.Item>, 113 + React.ComponentPropsWithoutRef<typeof CommandPrimitive.Item> 114 + >(({ className, ...props }, ref) => ( 115 + <CommandPrimitive.Item 116 + ref={ref} 117 + className={cn( 118 + "relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-hidden data-[disabled=true]:pointer-events-none data-[selected='true']:bg-accent data-[selected=true]:text-accent-foreground data-[disabled=true]:opacity-50", 119 + className 120 + )} 121 + {...props} 122 + /> 123 + )) 124 + 125 + CommandItem.displayName = CommandPrimitive.Item.displayName 126 + 127 + const CommandShortcut = ({ 128 + className, 129 + ...props 130 + }: React.HTMLAttributes<HTMLSpanElement>) => { 131 + return ( 132 + <span 133 + className={cn( 134 + "ml-auto text-xs tracking-widest text-muted-foreground", 135 + className 136 + )} 137 + {...props} 138 + /> 139 + ) 140 + } 141 + CommandShortcut.displayName = "CommandShortcut" 142 + 143 + export { 144 + Command, 145 + CommandDialog, 146 + CommandInput, 147 + CommandList, 148 + CommandEmpty, 149 + CommandGroup, 150 + CommandItem, 151 + CommandShortcut, 152 + CommandSeparator, 153 + }
+198
src/components/ui/context-menu.tsx
··· 1 + import * as React from "react" 2 + import * as ContextMenuPrimitive from "@radix-ui/react-context-menu" 3 + import { Check, ChevronRight, Circle } from "lucide-react" 4 + 5 + import { cn } from "@/lib/utils" 6 + 7 + const ContextMenu = ContextMenuPrimitive.Root 8 + 9 + const ContextMenuTrigger = ContextMenuPrimitive.Trigger 10 + 11 + const ContextMenuGroup = ContextMenuPrimitive.Group 12 + 13 + const ContextMenuPortal = ContextMenuPrimitive.Portal 14 + 15 + const ContextMenuSub = ContextMenuPrimitive.Sub 16 + 17 + const ContextMenuRadioGroup = ContextMenuPrimitive.RadioGroup 18 + 19 + const ContextMenuSubTrigger = React.forwardRef< 20 + React.ElementRef<typeof ContextMenuPrimitive.SubTrigger>, 21 + React.ComponentPropsWithoutRef<typeof ContextMenuPrimitive.SubTrigger> & { 22 + inset?: boolean 23 + } 24 + >(({ className, inset, children, ...props }, ref) => ( 25 + <ContextMenuPrimitive.SubTrigger 26 + ref={ref} 27 + className={cn( 28 + "flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-hidden focus:bg-accent focus:text-accent-foreground data-[state=open]:bg-accent data-[state=open]:text-accent-foreground", 29 + inset && "pl-8", 30 + className 31 + )} 32 + {...props} 33 + > 34 + {children} 35 + <ChevronRight className="ml-auto h-4 w-4" /> 36 + </ContextMenuPrimitive.SubTrigger> 37 + )) 38 + ContextMenuSubTrigger.displayName = ContextMenuPrimitive.SubTrigger.displayName 39 + 40 + const ContextMenuSubContent = React.forwardRef< 41 + React.ElementRef<typeof ContextMenuPrimitive.SubContent>, 42 + React.ComponentPropsWithoutRef<typeof ContextMenuPrimitive.SubContent> 43 + >(({ className, ...props }, ref) => ( 44 + <ContextMenuPrimitive.SubContent 45 + ref={ref} 46 + className={cn( 47 + "z-50 min-w-32 overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2", 48 + className 49 + )} 50 + {...props} 51 + /> 52 + )) 53 + ContextMenuSubContent.displayName = ContextMenuPrimitive.SubContent.displayName 54 + 55 + const ContextMenuContent = React.forwardRef< 56 + React.ElementRef<typeof ContextMenuPrimitive.Content>, 57 + React.ComponentPropsWithoutRef<typeof ContextMenuPrimitive.Content> 58 + >(({ className, ...props }, ref) => ( 59 + <ContextMenuPrimitive.Portal> 60 + <ContextMenuPrimitive.Content 61 + ref={ref} 62 + className={cn( 63 + "z-50 min-w-32 overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md animate-in fade-in-80 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2", 64 + className 65 + )} 66 + {...props} 67 + /> 68 + </ContextMenuPrimitive.Portal> 69 + )) 70 + ContextMenuContent.displayName = ContextMenuPrimitive.Content.displayName 71 + 72 + const ContextMenuItem = React.forwardRef< 73 + React.ElementRef<typeof ContextMenuPrimitive.Item>, 74 + React.ComponentPropsWithoutRef<typeof ContextMenuPrimitive.Item> & { 75 + inset?: boolean 76 + } 77 + >(({ className, inset, ...props }, ref) => ( 78 + <ContextMenuPrimitive.Item 79 + ref={ref} 80 + className={cn( 81 + "relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-hidden focus:bg-accent focus:text-accent-foreground data-disabled:pointer-events-none data-disabled:opacity-50", 82 + inset && "pl-8", 83 + className 84 + )} 85 + {...props} 86 + /> 87 + )) 88 + ContextMenuItem.displayName = ContextMenuPrimitive.Item.displayName 89 + 90 + const ContextMenuCheckboxItem = React.forwardRef< 91 + React.ElementRef<typeof ContextMenuPrimitive.CheckboxItem>, 92 + React.ComponentPropsWithoutRef<typeof ContextMenuPrimitive.CheckboxItem> 93 + >(({ className, children, checked, ...props }, ref) => ( 94 + <ContextMenuPrimitive.CheckboxItem 95 + ref={ref} 96 + className={cn( 97 + "relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-hidden focus:bg-accent focus:text-accent-foreground data-disabled:pointer-events-none data-disabled:opacity-50", 98 + className 99 + )} 100 + checked={checked} 101 + {...props} 102 + > 103 + <span className="absolute left-2 flex h-3.5 w-3.5 items-center justify-center"> 104 + <ContextMenuPrimitive.ItemIndicator> 105 + <Check className="h-4 w-4" /> 106 + </ContextMenuPrimitive.ItemIndicator> 107 + </span> 108 + {children} 109 + </ContextMenuPrimitive.CheckboxItem> 110 + )) 111 + ContextMenuCheckboxItem.displayName = 112 + ContextMenuPrimitive.CheckboxItem.displayName 113 + 114 + const ContextMenuRadioItem = React.forwardRef< 115 + React.ElementRef<typeof ContextMenuPrimitive.RadioItem>, 116 + React.ComponentPropsWithoutRef<typeof ContextMenuPrimitive.RadioItem> 117 + >(({ className, children, ...props }, ref) => ( 118 + <ContextMenuPrimitive.RadioItem 119 + ref={ref} 120 + className={cn( 121 + "relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-hidden focus:bg-accent focus:text-accent-foreground data-disabled:pointer-events-none data-disabled:opacity-50", 122 + className 123 + )} 124 + {...props} 125 + > 126 + <span className="absolute left-2 flex h-3.5 w-3.5 items-center justify-center"> 127 + <ContextMenuPrimitive.ItemIndicator> 128 + <Circle className="h-2 w-2 fill-current" /> 129 + </ContextMenuPrimitive.ItemIndicator> 130 + </span> 131 + {children} 132 + </ContextMenuPrimitive.RadioItem> 133 + )) 134 + ContextMenuRadioItem.displayName = ContextMenuPrimitive.RadioItem.displayName 135 + 136 + const ContextMenuLabel = React.forwardRef< 137 + React.ElementRef<typeof ContextMenuPrimitive.Label>, 138 + React.ComponentPropsWithoutRef<typeof ContextMenuPrimitive.Label> & { 139 + inset?: boolean 140 + } 141 + >(({ className, inset, ...props }, ref) => ( 142 + <ContextMenuPrimitive.Label 143 + ref={ref} 144 + className={cn( 145 + "px-2 py-1.5 text-sm font-semibold text-foreground", 146 + inset && "pl-8", 147 + className 148 + )} 149 + {...props} 150 + /> 151 + )) 152 + ContextMenuLabel.displayName = ContextMenuPrimitive.Label.displayName 153 + 154 + const ContextMenuSeparator = React.forwardRef< 155 + React.ElementRef<typeof ContextMenuPrimitive.Separator>, 156 + React.ComponentPropsWithoutRef<typeof ContextMenuPrimitive.Separator> 157 + >(({ className, ...props }, ref) => ( 158 + <ContextMenuPrimitive.Separator 159 + ref={ref} 160 + className={cn("-mx-1 my-1 h-px bg-border", className)} 161 + {...props} 162 + /> 163 + )) 164 + ContextMenuSeparator.displayName = ContextMenuPrimitive.Separator.displayName 165 + 166 + const ContextMenuShortcut = ({ 167 + className, 168 + ...props 169 + }: React.HTMLAttributes<HTMLSpanElement>) => { 170 + return ( 171 + <span 172 + className={cn( 173 + "ml-auto text-xs tracking-widest text-muted-foreground", 174 + className 175 + )} 176 + {...props} 177 + /> 178 + ) 179 + } 180 + ContextMenuShortcut.displayName = "ContextMenuShortcut" 181 + 182 + export { 183 + ContextMenu, 184 + ContextMenuTrigger, 185 + ContextMenuContent, 186 + ContextMenuItem, 187 + ContextMenuCheckboxItem, 188 + ContextMenuRadioItem, 189 + ContextMenuLabel, 190 + ContextMenuSeparator, 191 + ContextMenuShortcut, 192 + ContextMenuGroup, 193 + ContextMenuPortal, 194 + ContextMenuSub, 195 + ContextMenuSubContent, 196 + ContextMenuSubTrigger, 197 + ContextMenuRadioGroup, 198 + }
+120
src/components/ui/dialog.tsx
··· 1 + import * as React from "react" 2 + import * as DialogPrimitive from "@radix-ui/react-dialog" 3 + import { X } from "lucide-react" 4 + 5 + import { cn } from "@/lib/utils" 6 + 7 + const Dialog = DialogPrimitive.Root 8 + 9 + const DialogTrigger = DialogPrimitive.Trigger 10 + 11 + const DialogPortal = DialogPrimitive.Portal 12 + 13 + const DialogClose = DialogPrimitive.Close 14 + 15 + const DialogOverlay = React.forwardRef< 16 + React.ElementRef<typeof DialogPrimitive.Overlay>, 17 + React.ComponentPropsWithoutRef<typeof DialogPrimitive.Overlay> 18 + >(({ className, ...props }, ref) => ( 19 + <DialogPrimitive.Overlay 20 + ref={ref} 21 + className={cn( 22 + "fixed inset-0 z-50 bg-black/80 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0", 23 + className 24 + )} 25 + {...props} 26 + /> 27 + )) 28 + DialogOverlay.displayName = DialogPrimitive.Overlay.displayName 29 + 30 + const DialogContent = React.forwardRef< 31 + React.ElementRef<typeof DialogPrimitive.Content>, 32 + React.ComponentPropsWithoutRef<typeof DialogPrimitive.Content> 33 + >(({ className, children, ...props }, ref) => ( 34 + <DialogPortal> 35 + <DialogOverlay /> 36 + <DialogPrimitive.Content 37 + ref={ref} 38 + className={cn( 39 + "fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 sm:rounded-lg", 40 + className 41 + )} 42 + {...props} 43 + > 44 + {children} 45 + <DialogPrimitive.Close className="absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-hidden focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-accent data-[state=open]:text-muted-foreground cursor-pointer"> 46 + <X className="h-4 w-4" /> 47 + <span className="sr-only">Close</span> 48 + </DialogPrimitive.Close> 49 + </DialogPrimitive.Content> 50 + </DialogPortal> 51 + )) 52 + DialogContent.displayName = DialogPrimitive.Content.displayName 53 + 54 + const DialogHeader = ({ 55 + className, 56 + ...props 57 + }: React.HTMLAttributes<HTMLDivElement>) => ( 58 + <div 59 + className={cn( 60 + "flex flex-col space-y-1.5 text-center sm:text-left", 61 + className 62 + )} 63 + {...props} 64 + /> 65 + ) 66 + DialogHeader.displayName = "DialogHeader" 67 + 68 + const DialogFooter = ({ 69 + className, 70 + ...props 71 + }: React.HTMLAttributes<HTMLDivElement>) => ( 72 + <div 73 + className={cn( 74 + "flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2", 75 + className 76 + )} 77 + {...props} 78 + /> 79 + ) 80 + DialogFooter.displayName = "DialogFooter" 81 + 82 + const DialogTitle = React.forwardRef< 83 + React.ElementRef<typeof DialogPrimitive.Title>, 84 + React.ComponentPropsWithoutRef<typeof DialogPrimitive.Title> 85 + >(({ className, ...props }, ref) => ( 86 + <DialogPrimitive.Title 87 + ref={ref} 88 + className={cn( 89 + "text-lg font-semibold leading-none tracking-tight", 90 + className 91 + )} 92 + {...props} 93 + /> 94 + )) 95 + DialogTitle.displayName = DialogPrimitive.Title.displayName 96 + 97 + const DialogDescription = React.forwardRef< 98 + React.ElementRef<typeof DialogPrimitive.Description>, 99 + React.ComponentPropsWithoutRef<typeof DialogPrimitive.Description> 100 + >(({ className, ...props }, ref) => ( 101 + <DialogPrimitive.Description 102 + ref={ref} 103 + className={cn("text-sm text-muted-foreground", className)} 104 + {...props} 105 + /> 106 + )) 107 + DialogDescription.displayName = DialogPrimitive.Description.displayName 108 + 109 + export { 110 + Dialog, 111 + DialogPortal, 112 + DialogOverlay, 113 + DialogClose, 114 + DialogTrigger, 115 + DialogContent, 116 + DialogHeader, 117 + DialogFooter, 118 + DialogTitle, 119 + DialogDescription, 120 + }
+116
src/components/ui/drawer.tsx
··· 1 + import * as React from "react" 2 + import { Drawer as DrawerPrimitive } from "vaul" 3 + 4 + import { cn } from "@/lib/utils" 5 + 6 + const Drawer = ({ 7 + shouldScaleBackground = true, 8 + ...props 9 + }: React.ComponentProps<typeof DrawerPrimitive.Root>) => ( 10 + <DrawerPrimitive.Root 11 + shouldScaleBackground={shouldScaleBackground} 12 + {...props} 13 + /> 14 + ) 15 + Drawer.displayName = "Drawer" 16 + 17 + const DrawerTrigger = DrawerPrimitive.Trigger 18 + 19 + const DrawerPortal = DrawerPrimitive.Portal 20 + 21 + const DrawerClose = DrawerPrimitive.Close 22 + 23 + const DrawerOverlay = React.forwardRef< 24 + React.ElementRef<typeof DrawerPrimitive.Overlay>, 25 + React.ComponentPropsWithoutRef<typeof DrawerPrimitive.Overlay> 26 + >(({ className, ...props }, ref) => ( 27 + <DrawerPrimitive.Overlay 28 + ref={ref} 29 + className={cn("fixed inset-0 z-50 bg-black/80", className)} 30 + {...props} 31 + /> 32 + )) 33 + DrawerOverlay.displayName = DrawerPrimitive.Overlay.displayName 34 + 35 + const DrawerContent = React.forwardRef< 36 + React.ElementRef<typeof DrawerPrimitive.Content>, 37 + React.ComponentPropsWithoutRef<typeof DrawerPrimitive.Content> 38 + >(({ className, children, ...props }, ref) => ( 39 + <DrawerPortal> 40 + <DrawerOverlay /> 41 + <DrawerPrimitive.Content 42 + ref={ref} 43 + className={cn( 44 + "fixed inset-x-0 bottom-0 z-50 mt-24 flex h-auto flex-col rounded-t-[10px] border bg-background", 45 + className 46 + )} 47 + {...props} 48 + > 49 + <div className="mx-auto mt-4 h-2 w-[100px] rounded-full bg-muted" /> 50 + {children} 51 + </DrawerPrimitive.Content> 52 + </DrawerPortal> 53 + )) 54 + DrawerContent.displayName = "DrawerContent" 55 + 56 + const DrawerHeader = ({ 57 + className, 58 + ...props 59 + }: React.HTMLAttributes<HTMLDivElement>) => ( 60 + <div 61 + className={cn("grid gap-1.5 p-4 text-center sm:text-left", className)} 62 + {...props} 63 + /> 64 + ) 65 + DrawerHeader.displayName = "DrawerHeader" 66 + 67 + const DrawerFooter = ({ 68 + className, 69 + ...props 70 + }: React.HTMLAttributes<HTMLDivElement>) => ( 71 + <div 72 + className={cn("mt-auto flex flex-col gap-2 p-4", className)} 73 + {...props} 74 + /> 75 + ) 76 + DrawerFooter.displayName = "DrawerFooter" 77 + 78 + const DrawerTitle = React.forwardRef< 79 + React.ElementRef<typeof DrawerPrimitive.Title>, 80 + React.ComponentPropsWithoutRef<typeof DrawerPrimitive.Title> 81 + >(({ className, ...props }, ref) => ( 82 + <DrawerPrimitive.Title 83 + ref={ref} 84 + className={cn( 85 + "text-lg font-semibold leading-none tracking-tight", 86 + className 87 + )} 88 + {...props} 89 + /> 90 + )) 91 + DrawerTitle.displayName = DrawerPrimitive.Title.displayName 92 + 93 + const DrawerDescription = React.forwardRef< 94 + React.ElementRef<typeof DrawerPrimitive.Description>, 95 + React.ComponentPropsWithoutRef<typeof DrawerPrimitive.Description> 96 + >(({ className, ...props }, ref) => ( 97 + <DrawerPrimitive.Description 98 + ref={ref} 99 + className={cn("text-sm text-muted-foreground", className)} 100 + {...props} 101 + /> 102 + )) 103 + DrawerDescription.displayName = DrawerPrimitive.Description.displayName 104 + 105 + export { 106 + Drawer, 107 + DrawerPortal, 108 + DrawerOverlay, 109 + DrawerTrigger, 110 + DrawerClose, 111 + DrawerContent, 112 + DrawerHeader, 113 + DrawerFooter, 114 + DrawerTitle, 115 + DrawerDescription, 116 + }
+198
src/components/ui/dropdown-menu.tsx
··· 1 + import * as React from "react" 2 + import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu" 3 + import { Check, ChevronRight, Circle } from "lucide-react" 4 + 5 + import { cn } from "@/lib/utils" 6 + 7 + const DropdownMenu = DropdownMenuPrimitive.Root 8 + 9 + const DropdownMenuTrigger = DropdownMenuPrimitive.Trigger 10 + 11 + const DropdownMenuGroup = DropdownMenuPrimitive.Group 12 + 13 + const DropdownMenuPortal = DropdownMenuPrimitive.Portal 14 + 15 + const DropdownMenuSub = DropdownMenuPrimitive.Sub 16 + 17 + const DropdownMenuRadioGroup = DropdownMenuPrimitive.RadioGroup 18 + 19 + const DropdownMenuSubTrigger = React.forwardRef< 20 + React.ElementRef<typeof DropdownMenuPrimitive.SubTrigger>, 21 + React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.SubTrigger> & { 22 + inset?: boolean 23 + } 24 + >(({ className, inset, children, ...props }, ref) => ( 25 + <DropdownMenuPrimitive.SubTrigger 26 + ref={ref} 27 + className={cn( 28 + "flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-hidden focus:bg-accent data-[state=open]:bg-accent", 29 + inset && "pl-8", 30 + className 31 + )} 32 + {...props} 33 + > 34 + {children} 35 + <ChevronRight className="ml-auto h-4 w-4" /> 36 + </DropdownMenuPrimitive.SubTrigger> 37 + )) 38 + DropdownMenuSubTrigger.displayName = 39 + DropdownMenuPrimitive.SubTrigger.displayName 40 + 41 + const DropdownMenuSubContent = React.forwardRef< 42 + React.ElementRef<typeof DropdownMenuPrimitive.SubContent>, 43 + React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.SubContent> 44 + >(({ className, ...props }, ref) => ( 45 + <DropdownMenuPrimitive.SubContent 46 + ref={ref} 47 + className={cn( 48 + "z-50 min-w-32 overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-lg data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2", 49 + className 50 + )} 51 + {...props} 52 + /> 53 + )) 54 + DropdownMenuSubContent.displayName = 55 + DropdownMenuPrimitive.SubContent.displayName 56 + 57 + const DropdownMenuContent = React.forwardRef< 58 + React.ElementRef<typeof DropdownMenuPrimitive.Content>, 59 + React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Content> 60 + >(({ className, sideOffset = 4, ...props }, ref) => ( 61 + <DropdownMenuPrimitive.Portal> 62 + <DropdownMenuPrimitive.Content 63 + ref={ref} 64 + sideOffset={sideOffset} 65 + className={cn( 66 + "z-50 min-w-32 overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2", 67 + className 68 + )} 69 + {...props} 70 + /> 71 + </DropdownMenuPrimitive.Portal> 72 + )) 73 + DropdownMenuContent.displayName = DropdownMenuPrimitive.Content.displayName 74 + 75 + const DropdownMenuItem = React.forwardRef< 76 + React.ElementRef<typeof DropdownMenuPrimitive.Item>, 77 + React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Item> & { 78 + inset?: boolean 79 + } 80 + >(({ className, inset, ...props }, ref) => ( 81 + <DropdownMenuPrimitive.Item 82 + ref={ref} 83 + className={cn( 84 + "relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-hidden transition-colors focus:bg-accent focus:text-accent-foreground data-disabled:pointer-events-none data-disabled:opacity-50", 85 + inset && "pl-8", 86 + className 87 + )} 88 + {...props} 89 + /> 90 + )) 91 + DropdownMenuItem.displayName = DropdownMenuPrimitive.Item.displayName 92 + 93 + const DropdownMenuCheckboxItem = React.forwardRef< 94 + React.ElementRef<typeof DropdownMenuPrimitive.CheckboxItem>, 95 + React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.CheckboxItem> 96 + >(({ className, children, checked, ...props }, ref) => ( 97 + <DropdownMenuPrimitive.CheckboxItem 98 + ref={ref} 99 + className={cn( 100 + "relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-hidden transition-colors focus:bg-accent focus:text-accent-foreground data-disabled:pointer-events-none data-disabled:opacity-50", 101 + className 102 + )} 103 + checked={checked} 104 + {...props} 105 + > 106 + <span className="absolute left-2 flex h-3.5 w-3.5 items-center justify-center"> 107 + <DropdownMenuPrimitive.ItemIndicator> 108 + <Check className="h-4 w-4" /> 109 + </DropdownMenuPrimitive.ItemIndicator> 110 + </span> 111 + {children} 112 + </DropdownMenuPrimitive.CheckboxItem> 113 + )) 114 + DropdownMenuCheckboxItem.displayName = 115 + DropdownMenuPrimitive.CheckboxItem.displayName 116 + 117 + const DropdownMenuRadioItem = React.forwardRef< 118 + React.ElementRef<typeof DropdownMenuPrimitive.RadioItem>, 119 + React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.RadioItem> 120 + >(({ className, children, ...props }, ref) => ( 121 + <DropdownMenuPrimitive.RadioItem 122 + ref={ref} 123 + className={cn( 124 + "relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-hidden transition-colors focus:bg-accent focus:text-accent-foreground data-disabled:pointer-events-none data-disabled:opacity-50", 125 + className 126 + )} 127 + {...props} 128 + > 129 + <span className="absolute left-2 flex h-3.5 w-3.5 items-center justify-center"> 130 + <DropdownMenuPrimitive.ItemIndicator> 131 + <Circle className="h-2 w-2 fill-current" /> 132 + </DropdownMenuPrimitive.ItemIndicator> 133 + </span> 134 + {children} 135 + </DropdownMenuPrimitive.RadioItem> 136 + )) 137 + DropdownMenuRadioItem.displayName = DropdownMenuPrimitive.RadioItem.displayName 138 + 139 + const DropdownMenuLabel = React.forwardRef< 140 + React.ElementRef<typeof DropdownMenuPrimitive.Label>, 141 + React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Label> & { 142 + inset?: boolean 143 + } 144 + >(({ className, inset, ...props }, ref) => ( 145 + <DropdownMenuPrimitive.Label 146 + ref={ref} 147 + className={cn( 148 + "px-2 py-1.5 text-sm font-semibold", 149 + inset && "pl-8", 150 + className 151 + )} 152 + {...props} 153 + /> 154 + )) 155 + DropdownMenuLabel.displayName = DropdownMenuPrimitive.Label.displayName 156 + 157 + const DropdownMenuSeparator = React.forwardRef< 158 + React.ElementRef<typeof DropdownMenuPrimitive.Separator>, 159 + React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Separator> 160 + >(({ className, ...props }, ref) => ( 161 + <DropdownMenuPrimitive.Separator 162 + ref={ref} 163 + className={cn("-mx-1 my-1 h-px bg-muted", className)} 164 + {...props} 165 + /> 166 + )) 167 + DropdownMenuSeparator.displayName = DropdownMenuPrimitive.Separator.displayName 168 + 169 + const DropdownMenuShortcut = ({ 170 + className, 171 + ...props 172 + }: React.HTMLAttributes<HTMLSpanElement>) => { 173 + return ( 174 + <span 175 + className={cn("ml-auto text-xs tracking-widest opacity-60", className)} 176 + {...props} 177 + /> 178 + ) 179 + } 180 + DropdownMenuShortcut.displayName = "DropdownMenuShortcut" 181 + 182 + export { 183 + DropdownMenu, 184 + DropdownMenuTrigger, 185 + DropdownMenuContent, 186 + DropdownMenuItem, 187 + DropdownMenuCheckboxItem, 188 + DropdownMenuRadioItem, 189 + DropdownMenuLabel, 190 + DropdownMenuSeparator, 191 + DropdownMenuShortcut, 192 + DropdownMenuGroup, 193 + DropdownMenuPortal, 194 + DropdownMenuSub, 195 + DropdownMenuSubContent, 196 + DropdownMenuSubTrigger, 197 + DropdownMenuRadioGroup, 198 + }
+176
src/components/ui/form.tsx
··· 1 + import * as React from "react" 2 + import * as LabelPrimitive from "@radix-ui/react-label" 3 + import { Slot } from "@radix-ui/react-slot" 4 + import { 5 + Controller, 6 + ControllerProps, 7 + FieldPath, 8 + FieldValues, 9 + FormProvider, 10 + useFormContext, 11 + } from "react-hook-form" 12 + 13 + import { cn } from "@/lib/utils" 14 + import { Label } from "@/components/ui/label" 15 + 16 + const Form = FormProvider 17 + 18 + type FormFieldContextValue< 19 + TFieldValues extends FieldValues = FieldValues, 20 + TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues> 21 + > = { 22 + name: TName 23 + } 24 + 25 + const FormFieldContext = React.createContext<FormFieldContextValue>( 26 + {} as FormFieldContextValue 27 + ) 28 + 29 + const FormField = < 30 + TFieldValues extends FieldValues = FieldValues, 31 + TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues> 32 + >({ 33 + ...props 34 + }: ControllerProps<TFieldValues, TName>) => { 35 + return ( 36 + <FormFieldContext.Provider value={{ name: props.name }}> 37 + <Controller {...props} /> 38 + </FormFieldContext.Provider> 39 + ) 40 + } 41 + 42 + const useFormField = () => { 43 + const fieldContext = React.useContext(FormFieldContext) 44 + const itemContext = React.useContext(FormItemContext) 45 + const { getFieldState, formState } = useFormContext() 46 + 47 + const fieldState = getFieldState(fieldContext.name, formState) 48 + 49 + if (!fieldContext) { 50 + throw new Error("useFormField should be used within <FormField>") 51 + } 52 + 53 + const { id } = itemContext 54 + 55 + return { 56 + id, 57 + name: fieldContext.name, 58 + formItemId: `${id}-form-item`, 59 + formDescriptionId: `${id}-form-item-description`, 60 + formMessageId: `${id}-form-item-message`, 61 + ...fieldState, 62 + } 63 + } 64 + 65 + type FormItemContextValue = { 66 + id: string 67 + } 68 + 69 + const FormItemContext = React.createContext<FormItemContextValue>( 70 + {} as FormItemContextValue 71 + ) 72 + 73 + const FormItem = React.forwardRef< 74 + HTMLDivElement, 75 + React.HTMLAttributes<HTMLDivElement> 76 + >(({ className, ...props }, ref) => { 77 + const id = React.useId() 78 + 79 + return ( 80 + <FormItemContext.Provider value={{ id }}> 81 + <div ref={ref} className={cn("space-y-2", className)} {...props} /> 82 + </FormItemContext.Provider> 83 + ) 84 + }) 85 + FormItem.displayName = "FormItem" 86 + 87 + const FormLabel = React.forwardRef< 88 + React.ElementRef<typeof LabelPrimitive.Root>, 89 + React.ComponentPropsWithoutRef<typeof LabelPrimitive.Root> 90 + >(({ className, ...props }, ref) => { 91 + const { error, formItemId } = useFormField() 92 + 93 + return ( 94 + <Label 95 + ref={ref} 96 + className={cn(error && "text-destructive", className)} 97 + htmlFor={formItemId} 98 + {...props} 99 + /> 100 + ) 101 + }) 102 + FormLabel.displayName = "FormLabel" 103 + 104 + const FormControl = React.forwardRef< 105 + React.ElementRef<typeof Slot>, 106 + React.ComponentPropsWithoutRef<typeof Slot> 107 + >(({ ...props }, ref) => { 108 + const { error, formItemId, formDescriptionId, formMessageId } = useFormField() 109 + 110 + return ( 111 + <Slot 112 + ref={ref} 113 + id={formItemId} 114 + aria-describedby={ 115 + !error 116 + ? `${formDescriptionId}` 117 + : `${formDescriptionId} ${formMessageId}` 118 + } 119 + aria-invalid={!!error} 120 + {...props} 121 + /> 122 + ) 123 + }) 124 + FormControl.displayName = "FormControl" 125 + 126 + const FormDescription = React.forwardRef< 127 + HTMLParagraphElement, 128 + React.HTMLAttributes<HTMLParagraphElement> 129 + >(({ className, ...props }, ref) => { 130 + const { formDescriptionId } = useFormField() 131 + 132 + return ( 133 + <p 134 + ref={ref} 135 + id={formDescriptionId} 136 + className={cn("text-sm text-muted-foreground", className)} 137 + {...props} 138 + /> 139 + ) 140 + }) 141 + FormDescription.displayName = "FormDescription" 142 + 143 + const FormMessage = React.forwardRef< 144 + HTMLParagraphElement, 145 + React.HTMLAttributes<HTMLParagraphElement> 146 + >(({ className, children, ...props }, ref) => { 147 + const { error, formMessageId } = useFormField() 148 + const body = error ? String(error?.message) : children 149 + 150 + if (!body) { 151 + return null 152 + } 153 + 154 + return ( 155 + <p 156 + ref={ref} 157 + id={formMessageId} 158 + className={cn("text-sm font-medium text-destructive", className)} 159 + {...props} 160 + > 161 + {body} 162 + </p> 163 + ) 164 + }) 165 + FormMessage.displayName = "FormMessage" 166 + 167 + export { 168 + useFormField, 169 + Form, 170 + FormItem, 171 + FormLabel, 172 + FormControl, 173 + FormDescription, 174 + FormMessage, 175 + FormField, 176 + }
+27
src/components/ui/hover-card.tsx
··· 1 + import * as React from "react" 2 + import * as HoverCardPrimitive from "@radix-ui/react-hover-card" 3 + 4 + import { cn } from "@/lib/utils" 5 + 6 + const HoverCard = HoverCardPrimitive.Root 7 + 8 + const HoverCardTrigger = HoverCardPrimitive.Trigger 9 + 10 + const HoverCardContent = React.forwardRef< 11 + React.ElementRef<typeof HoverCardPrimitive.Content>, 12 + React.ComponentPropsWithoutRef<typeof HoverCardPrimitive.Content> 13 + >(({ className, align = "center", sideOffset = 4, ...props }, ref) => ( 14 + <HoverCardPrimitive.Content 15 + ref={ref} 16 + align={align} 17 + sideOffset={sideOffset} 18 + className={cn( 19 + "z-50 w-64 rounded-md border bg-popover p-4 text-popover-foreground shadow-md outline-hidden data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2", 20 + className 21 + )} 22 + {...props} 23 + /> 24 + )) 25 + HoverCardContent.displayName = HoverCardPrimitive.Content.displayName 26 + 27 + export { HoverCard, HoverCardTrigger, HoverCardContent }
+69
src/components/ui/input-otp.tsx
··· 1 + import * as React from "react" 2 + import { OTPInput, OTPInputContext } from "input-otp" 3 + import { Dot } from "lucide-react" 4 + 5 + import { cn } from "@/lib/utils" 6 + 7 + const InputOTP = React.forwardRef< 8 + React.ElementRef<typeof OTPInput>, 9 + React.ComponentPropsWithoutRef<typeof OTPInput> 10 + >(({ className, containerClassName, ...props }, ref) => ( 11 + <OTPInput 12 + ref={ref} 13 + containerClassName={cn( 14 + "flex items-center gap-2 has-disabled:opacity-50", 15 + containerClassName 16 + )} 17 + className={cn("disabled:cursor-not-allowed", className)} 18 + {...props} 19 + /> 20 + )) 21 + InputOTP.displayName = "InputOTP" 22 + 23 + const InputOTPGroup = React.forwardRef< 24 + React.ElementRef<"div">, 25 + React.ComponentPropsWithoutRef<"div"> 26 + >(({ className, ...props }, ref) => ( 27 + <div ref={ref} className={cn("flex items-center", className)} {...props} /> 28 + )) 29 + InputOTPGroup.displayName = "InputOTPGroup" 30 + 31 + const InputOTPSlot = React.forwardRef< 32 + React.ElementRef<"div">, 33 + React.ComponentPropsWithoutRef<"div"> & { index: number } 34 + >(({ index, className, ...props }, ref) => { 35 + const inputOTPContext = React.useContext(OTPInputContext) 36 + const { char, hasFakeCaret, isActive } = inputOTPContext.slots[index] 37 + 38 + return ( 39 + <div 40 + ref={ref} 41 + className={cn( 42 + "relative flex h-10 w-10 items-center justify-center border-y border-r border-input text-sm transition-all first:rounded-l-md first:border-l last:rounded-r-md", 43 + isActive && "z-10 ring-2 ring-ring ring-offset-background", 44 + className 45 + )} 46 + {...props} 47 + > 48 + {char} 49 + {hasFakeCaret && ( 50 + <div className="pointer-events-none absolute inset-0 flex items-center justify-center"> 51 + <div className="h-4 w-px animate-caret-blink bg-foreground duration-1000" /> 52 + </div> 53 + )} 54 + </div> 55 + ) 56 + }) 57 + InputOTPSlot.displayName = "InputOTPSlot" 58 + 59 + const InputOTPSeparator = React.forwardRef< 60 + React.ElementRef<"div">, 61 + React.ComponentPropsWithoutRef<"div"> 62 + >(({ ...props }, ref) => ( 63 + <div ref={ref} role="separator" {...props}> 64 + <Dot /> 65 + </div> 66 + )) 67 + InputOTPSeparator.displayName = "InputOTPSeparator" 68 + 69 + export { InputOTP, InputOTPGroup, InputOTPSlot, InputOTPSeparator }
+22
src/components/ui/input.tsx
··· 1 + import * as React from "react" 2 + 3 + import { cn } from "@/lib/utils" 4 + 5 + const Input = React.forwardRef<HTMLInputElement, React.ComponentProps<"input">>( 6 + ({ className, type, ...props }, ref) => { 7 + return ( 8 + <input 9 + type={type} 10 + className={cn( 11 + "flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-base ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 md:text-sm", 12 + className 13 + )} 14 + ref={ref} 15 + {...props} 16 + /> 17 + ) 18 + } 19 + ) 20 + Input.displayName = "Input" 21 + 22 + export { Input }
+24
src/components/ui/label.tsx
··· 1 + import * as React from "react" 2 + import * as LabelPrimitive from "@radix-ui/react-label" 3 + import { cva, type VariantProps } from "class-variance-authority" 4 + 5 + import { cn } from "@/lib/utils" 6 + 7 + const labelVariants = cva( 8 + "text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70" 9 + ) 10 + 11 + const Label = React.forwardRef< 12 + React.ElementRef<typeof LabelPrimitive.Root>, 13 + React.ComponentPropsWithoutRef<typeof LabelPrimitive.Root> & 14 + VariantProps<typeof labelVariants> 15 + >(({ className, ...props }, ref) => ( 16 + <LabelPrimitive.Root 17 + ref={ref} 18 + className={cn(labelVariants(), className)} 19 + {...props} 20 + /> 21 + )) 22 + Label.displayName = LabelPrimitive.Root.displayName 23 + 24 + export { Label }
+234
src/components/ui/menubar.tsx
··· 1 + import * as React from "react" 2 + import * as MenubarPrimitive from "@radix-ui/react-menubar" 3 + import { Check, ChevronRight, Circle } from "lucide-react" 4 + 5 + import { cn } from "@/lib/utils" 6 + 7 + const MenubarMenu = MenubarPrimitive.Menu 8 + 9 + const MenubarGroup = MenubarPrimitive.Group 10 + 11 + const MenubarPortal = MenubarPrimitive.Portal 12 + 13 + const MenubarSub = MenubarPrimitive.Sub 14 + 15 + const MenubarRadioGroup = MenubarPrimitive.RadioGroup 16 + 17 + const Menubar = React.forwardRef< 18 + React.ElementRef<typeof MenubarPrimitive.Root>, 19 + React.ComponentPropsWithoutRef<typeof MenubarPrimitive.Root> 20 + >(({ className, ...props }, ref) => ( 21 + <MenubarPrimitive.Root 22 + ref={ref} 23 + className={cn( 24 + "flex h-10 items-center space-x-1 rounded-md border bg-background p-1", 25 + className 26 + )} 27 + {...props} 28 + /> 29 + )) 30 + Menubar.displayName = MenubarPrimitive.Root.displayName 31 + 32 + const MenubarTrigger = React.forwardRef< 33 + React.ElementRef<typeof MenubarPrimitive.Trigger>, 34 + React.ComponentPropsWithoutRef<typeof MenubarPrimitive.Trigger> 35 + >(({ className, ...props }, ref) => ( 36 + <MenubarPrimitive.Trigger 37 + ref={ref} 38 + className={cn( 39 + "flex cursor-default select-none items-center rounded-sm px-3 py-1.5 text-sm font-medium outline-hidden focus:bg-accent focus:text-accent-foreground data-[state=open]:bg-accent data-[state=open]:text-accent-foreground", 40 + className 41 + )} 42 + {...props} 43 + /> 44 + )) 45 + MenubarTrigger.displayName = MenubarPrimitive.Trigger.displayName 46 + 47 + const MenubarSubTrigger = React.forwardRef< 48 + React.ElementRef<typeof MenubarPrimitive.SubTrigger>, 49 + React.ComponentPropsWithoutRef<typeof MenubarPrimitive.SubTrigger> & { 50 + inset?: boolean 51 + } 52 + >(({ className, inset, children, ...props }, ref) => ( 53 + <MenubarPrimitive.SubTrigger 54 + ref={ref} 55 + className={cn( 56 + "flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-hidden focus:bg-accent focus:text-accent-foreground data-[state=open]:bg-accent data-[state=open]:text-accent-foreground", 57 + inset && "pl-8", 58 + className 59 + )} 60 + {...props} 61 + > 62 + {children} 63 + <ChevronRight className="ml-auto h-4 w-4" /> 64 + </MenubarPrimitive.SubTrigger> 65 + )) 66 + MenubarSubTrigger.displayName = MenubarPrimitive.SubTrigger.displayName 67 + 68 + const MenubarSubContent = React.forwardRef< 69 + React.ElementRef<typeof MenubarPrimitive.SubContent>, 70 + React.ComponentPropsWithoutRef<typeof MenubarPrimitive.SubContent> 71 + >(({ className, ...props }, ref) => ( 72 + <MenubarPrimitive.SubContent 73 + ref={ref} 74 + className={cn( 75 + "z-50 min-w-32 overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2", 76 + className 77 + )} 78 + {...props} 79 + /> 80 + )) 81 + MenubarSubContent.displayName = MenubarPrimitive.SubContent.displayName 82 + 83 + const MenubarContent = React.forwardRef< 84 + React.ElementRef<typeof MenubarPrimitive.Content>, 85 + React.ComponentPropsWithoutRef<typeof MenubarPrimitive.Content> 86 + >( 87 + ( 88 + { className, align = "start", alignOffset = -4, sideOffset = 8, ...props }, 89 + ref 90 + ) => ( 91 + <MenubarPrimitive.Portal> 92 + <MenubarPrimitive.Content 93 + ref={ref} 94 + align={align} 95 + alignOffset={alignOffset} 96 + sideOffset={sideOffset} 97 + className={cn( 98 + "z-50 min-w-48 overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md data-[state=open]:animate-in data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2", 99 + className 100 + )} 101 + {...props} 102 + /> 103 + </MenubarPrimitive.Portal> 104 + ) 105 + ) 106 + MenubarContent.displayName = MenubarPrimitive.Content.displayName 107 + 108 + const MenubarItem = React.forwardRef< 109 + React.ElementRef<typeof MenubarPrimitive.Item>, 110 + React.ComponentPropsWithoutRef<typeof MenubarPrimitive.Item> & { 111 + inset?: boolean 112 + } 113 + >(({ className, inset, ...props }, ref) => ( 114 + <MenubarPrimitive.Item 115 + ref={ref} 116 + className={cn( 117 + "relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-hidden focus:bg-accent focus:text-accent-foreground data-disabled:pointer-events-none data-disabled:opacity-50", 118 + inset && "pl-8", 119 + className 120 + )} 121 + {...props} 122 + /> 123 + )) 124 + MenubarItem.displayName = MenubarPrimitive.Item.displayName 125 + 126 + const MenubarCheckboxItem = React.forwardRef< 127 + React.ElementRef<typeof MenubarPrimitive.CheckboxItem>, 128 + React.ComponentPropsWithoutRef<typeof MenubarPrimitive.CheckboxItem> 129 + >(({ className, children, checked, ...props }, ref) => ( 130 + <MenubarPrimitive.CheckboxItem 131 + ref={ref} 132 + className={cn( 133 + "relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-hidden focus:bg-accent focus:text-accent-foreground data-disabled:pointer-events-none data-disabled:opacity-50", 134 + className 135 + )} 136 + checked={checked} 137 + {...props} 138 + > 139 + <span className="absolute left-2 flex h-3.5 w-3.5 items-center justify-center"> 140 + <MenubarPrimitive.ItemIndicator> 141 + <Check className="h-4 w-4" /> 142 + </MenubarPrimitive.ItemIndicator> 143 + </span> 144 + {children} 145 + </MenubarPrimitive.CheckboxItem> 146 + )) 147 + MenubarCheckboxItem.displayName = MenubarPrimitive.CheckboxItem.displayName 148 + 149 + const MenubarRadioItem = React.forwardRef< 150 + React.ElementRef<typeof MenubarPrimitive.RadioItem>, 151 + React.ComponentPropsWithoutRef<typeof MenubarPrimitive.RadioItem> 152 + >(({ className, children, ...props }, ref) => ( 153 + <MenubarPrimitive.RadioItem 154 + ref={ref} 155 + className={cn( 156 + "relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-hidden focus:bg-accent focus:text-accent-foreground data-disabled:pointer-events-none data-disabled:opacity-50", 157 + className 158 + )} 159 + {...props} 160 + > 161 + <span className="absolute left-2 flex h-3.5 w-3.5 items-center justify-center"> 162 + <MenubarPrimitive.ItemIndicator> 163 + <Circle className="h-2 w-2 fill-current" /> 164 + </MenubarPrimitive.ItemIndicator> 165 + </span> 166 + {children} 167 + </MenubarPrimitive.RadioItem> 168 + )) 169 + MenubarRadioItem.displayName = MenubarPrimitive.RadioItem.displayName 170 + 171 + const MenubarLabel = React.forwardRef< 172 + React.ElementRef<typeof MenubarPrimitive.Label>, 173 + React.ComponentPropsWithoutRef<typeof MenubarPrimitive.Label> & { 174 + inset?: boolean 175 + } 176 + >(({ className, inset, ...props }, ref) => ( 177 + <MenubarPrimitive.Label 178 + ref={ref} 179 + className={cn( 180 + "px-2 py-1.5 text-sm font-semibold", 181 + inset && "pl-8", 182 + className 183 + )} 184 + {...props} 185 + /> 186 + )) 187 + MenubarLabel.displayName = MenubarPrimitive.Label.displayName 188 + 189 + const MenubarSeparator = React.forwardRef< 190 + React.ElementRef<typeof MenubarPrimitive.Separator>, 191 + React.ComponentPropsWithoutRef<typeof MenubarPrimitive.Separator> 192 + >(({ className, ...props }, ref) => ( 193 + <MenubarPrimitive.Separator 194 + ref={ref} 195 + className={cn("-mx-1 my-1 h-px bg-muted", className)} 196 + {...props} 197 + /> 198 + )) 199 + MenubarSeparator.displayName = MenubarPrimitive.Separator.displayName 200 + 201 + const MenubarShortcut = ({ 202 + className, 203 + ...props 204 + }: React.HTMLAttributes<HTMLSpanElement>) => { 205 + return ( 206 + <span 207 + className={cn( 208 + "ml-auto text-xs tracking-widest text-muted-foreground", 209 + className 210 + )} 211 + {...props} 212 + /> 213 + ) 214 + } 215 + MenubarShortcut.displayname = "MenubarShortcut" 216 + 217 + export { 218 + Menubar, 219 + MenubarMenu, 220 + MenubarTrigger, 221 + MenubarContent, 222 + MenubarItem, 223 + MenubarSeparator, 224 + MenubarLabel, 225 + MenubarCheckboxItem, 226 + MenubarRadioGroup, 227 + MenubarRadioItem, 228 + MenubarPortal, 229 + MenubarSubContent, 230 + MenubarSubTrigger, 231 + MenubarGroup, 232 + MenubarSub, 233 + MenubarShortcut, 234 + }
+128
src/components/ui/navigation-menu.tsx
··· 1 + import * as React from "react" 2 + import * as NavigationMenuPrimitive from "@radix-ui/react-navigation-menu" 3 + import { cva } from "class-variance-authority" 4 + import { ChevronDown } from "lucide-react" 5 + 6 + import { cn } from "@/lib/utils" 7 + 8 + const NavigationMenu = React.forwardRef< 9 + React.ElementRef<typeof NavigationMenuPrimitive.Root>, 10 + React.ComponentPropsWithoutRef<typeof NavigationMenuPrimitive.Root> 11 + >(({ className, children, ...props }, ref) => ( 12 + <NavigationMenuPrimitive.Root 13 + ref={ref} 14 + className={cn( 15 + "relative z-10 flex max-w-max flex-1 items-center justify-center", 16 + className 17 + )} 18 + {...props} 19 + > 20 + {children} 21 + <NavigationMenuViewport /> 22 + </NavigationMenuPrimitive.Root> 23 + )) 24 + NavigationMenu.displayName = NavigationMenuPrimitive.Root.displayName 25 + 26 + const NavigationMenuList = React.forwardRef< 27 + React.ElementRef<typeof NavigationMenuPrimitive.List>, 28 + React.ComponentPropsWithoutRef<typeof NavigationMenuPrimitive.List> 29 + >(({ className, ...props }, ref) => ( 30 + <NavigationMenuPrimitive.List 31 + ref={ref} 32 + className={cn( 33 + "group flex flex-1 list-none items-center justify-center space-x-1", 34 + className 35 + )} 36 + {...props} 37 + /> 38 + )) 39 + NavigationMenuList.displayName = NavigationMenuPrimitive.List.displayName 40 + 41 + const NavigationMenuItem = NavigationMenuPrimitive.Item 42 + 43 + const navigationMenuTriggerStyle = cva( 44 + "group inline-flex h-10 w-max items-center justify-center rounded-md bg-background px-4 py-2 text-sm font-medium transition-colors hover:bg-accent hover:text-accent-foreground focus:bg-accent focus:text-accent-foreground focus:outline-hidden disabled:pointer-events-none disabled:opacity-50 data-active:bg-accent/50 data-[state=open]:bg-accent/50" 45 + ) 46 + 47 + const NavigationMenuTrigger = React.forwardRef< 48 + React.ElementRef<typeof NavigationMenuPrimitive.Trigger>, 49 + React.ComponentPropsWithoutRef<typeof NavigationMenuPrimitive.Trigger> 50 + >(({ className, children, ...props }, ref) => ( 51 + <NavigationMenuPrimitive.Trigger 52 + ref={ref} 53 + className={cn(navigationMenuTriggerStyle(), "group", className)} 54 + {...props} 55 + > 56 + {children}{" "} 57 + <ChevronDown 58 + className="relative top-px ml-1 h-3 w-3 transition duration-200 group-data-[state=open]:rotate-180" 59 + aria-hidden="true" 60 + /> 61 + </NavigationMenuPrimitive.Trigger> 62 + )) 63 + NavigationMenuTrigger.displayName = NavigationMenuPrimitive.Trigger.displayName 64 + 65 + const NavigationMenuContent = React.forwardRef< 66 + React.ElementRef<typeof NavigationMenuPrimitive.Content>, 67 + React.ComponentPropsWithoutRef<typeof NavigationMenuPrimitive.Content> 68 + >(({ className, ...props }, ref) => ( 69 + <NavigationMenuPrimitive.Content 70 + ref={ref} 71 + className={cn( 72 + "left-0 top-0 w-full data-[motion^=from-]:animate-in data-[motion^=to-]:animate-out data-[motion^=from-]:fade-in data-[motion^=to-]:fade-out data-[motion=from-end]:slide-in-from-right-52 data-[motion=from-start]:slide-in-from-left-52 data-[motion=to-end]:slide-out-to-right-52 data-[motion=to-start]:slide-out-to-left-52 md:absolute md:w-auto ", 73 + className 74 + )} 75 + {...props} 76 + /> 77 + )) 78 + NavigationMenuContent.displayName = NavigationMenuPrimitive.Content.displayName 79 + 80 + const NavigationMenuLink = NavigationMenuPrimitive.Link 81 + 82 + const NavigationMenuViewport = React.forwardRef< 83 + React.ElementRef<typeof NavigationMenuPrimitive.Viewport>, 84 + React.ComponentPropsWithoutRef<typeof NavigationMenuPrimitive.Viewport> 85 + >(({ className, ...props }, ref) => ( 86 + <div className={cn("absolute left-0 top-full flex justify-center")}> 87 + <NavigationMenuPrimitive.Viewport 88 + className={cn( 89 + "origin-top-center relative mt-1.5 h-(--radix-navigation-menu-viewport-height) w-full overflow-hidden rounded-md border bg-popover text-popover-foreground shadow-lg data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-90 md:w-(--radix-navigation-menu-viewport-width)", 90 + className 91 + )} 92 + ref={ref} 93 + {...props} 94 + /> 95 + </div> 96 + )) 97 + NavigationMenuViewport.displayName = 98 + NavigationMenuPrimitive.Viewport.displayName 99 + 100 + const NavigationMenuIndicator = React.forwardRef< 101 + React.ElementRef<typeof NavigationMenuPrimitive.Indicator>, 102 + React.ComponentPropsWithoutRef<typeof NavigationMenuPrimitive.Indicator> 103 + >(({ className, ...props }, ref) => ( 104 + <NavigationMenuPrimitive.Indicator 105 + ref={ref} 106 + className={cn( 107 + "top-full z-1 flex h-1.5 items-end justify-center overflow-hidden data-[state=visible]:animate-in data-[state=hidden]:animate-out data-[state=hidden]:fade-out data-[state=visible]:fade-in", 108 + className 109 + )} 110 + {...props} 111 + > 112 + <div className="relative top-[60%] h-2 w-2 rotate-45 rounded-tl-sm bg-border shadow-md" /> 113 + </NavigationMenuPrimitive.Indicator> 114 + )) 115 + NavigationMenuIndicator.displayName = 116 + NavigationMenuPrimitive.Indicator.displayName 117 + 118 + export { 119 + navigationMenuTriggerStyle, 120 + NavigationMenu, 121 + NavigationMenuList, 122 + NavigationMenuItem, 123 + NavigationMenuContent, 124 + NavigationMenuTrigger, 125 + NavigationMenuLink, 126 + NavigationMenuIndicator, 127 + NavigationMenuViewport, 128 + }
+117
src/components/ui/pagination.tsx
··· 1 + import * as React from "react" 2 + import { ChevronLeft, ChevronRight, MoreHorizontal } from "lucide-react" 3 + 4 + import { cn } from "@/lib/utils" 5 + import { ButtonProps, buttonVariants } from "@/components/ui/button" 6 + 7 + const Pagination = ({ className, ...props }: React.ComponentProps<"nav">) => ( 8 + <nav 9 + role="navigation" 10 + aria-label="pagination" 11 + className={cn("mx-auto flex w-full justify-center", className)} 12 + {...props} 13 + /> 14 + ) 15 + Pagination.displayName = "Pagination" 16 + 17 + const PaginationContent = React.forwardRef< 18 + HTMLUListElement, 19 + React.ComponentProps<"ul"> 20 + >(({ className, ...props }, ref) => ( 21 + <ul 22 + ref={ref} 23 + className={cn("flex flex-row items-center gap-1", className)} 24 + {...props} 25 + /> 26 + )) 27 + PaginationContent.displayName = "PaginationContent" 28 + 29 + const PaginationItem = React.forwardRef< 30 + HTMLLIElement, 31 + React.ComponentProps<"li"> 32 + >(({ className, ...props }, ref) => ( 33 + <li ref={ref} className={cn("", className)} {...props} /> 34 + )) 35 + PaginationItem.displayName = "PaginationItem" 36 + 37 + type PaginationLinkProps = { 38 + isActive?: boolean 39 + } & Pick<ButtonProps, "size"> & 40 + React.ComponentProps<"a"> 41 + 42 + const PaginationLink = ({ 43 + className, 44 + isActive, 45 + size = "icon", 46 + ...props 47 + }: PaginationLinkProps) => ( 48 + <a 49 + aria-current={isActive ? "page" : undefined} 50 + className={cn( 51 + buttonVariants({ 52 + variant: isActive ? "outline-solid" : "ghost", 53 + size, 54 + }), 55 + className 56 + )} 57 + {...props} 58 + /> 59 + ) 60 + PaginationLink.displayName = "PaginationLink" 61 + 62 + const PaginationPrevious = ({ 63 + className, 64 + ...props 65 + }: React.ComponentProps<typeof PaginationLink>) => ( 66 + <PaginationLink 67 + aria-label="Go to previous page" 68 + size="default" 69 + className={cn("gap-1 pl-2.5", className)} 70 + {...props} 71 + > 72 + <ChevronLeft className="h-4 w-4" /> 73 + <span>Previous</span> 74 + </PaginationLink> 75 + ) 76 + PaginationPrevious.displayName = "PaginationPrevious" 77 + 78 + const PaginationNext = ({ 79 + className, 80 + ...props 81 + }: React.ComponentProps<typeof PaginationLink>) => ( 82 + <PaginationLink 83 + aria-label="Go to next page" 84 + size="default" 85 + className={cn("gap-1 pr-2.5", className)} 86 + {...props} 87 + > 88 + <span>Next</span> 89 + <ChevronRight className="h-4 w-4" /> 90 + </PaginationLink> 91 + ) 92 + PaginationNext.displayName = "PaginationNext" 93 + 94 + const PaginationEllipsis = ({ 95 + className, 96 + ...props 97 + }: React.ComponentProps<"span">) => ( 98 + <span 99 + aria-hidden 100 + className={cn("flex h-9 w-9 items-center justify-center", className)} 101 + {...props} 102 + > 103 + <MoreHorizontal className="h-4 w-4" /> 104 + <span className="sr-only">More pages</span> 105 + </span> 106 + ) 107 + PaginationEllipsis.displayName = "PaginationEllipsis" 108 + 109 + export { 110 + Pagination, 111 + PaginationContent, 112 + PaginationEllipsis, 113 + PaginationItem, 114 + PaginationLink, 115 + PaginationNext, 116 + PaginationPrevious, 117 + }
+29
src/components/ui/popover.tsx
··· 1 + import * as React from "react" 2 + import * as PopoverPrimitive from "@radix-ui/react-popover" 3 + 4 + import { cn } from "@/lib/utils" 5 + 6 + const Popover = PopoverPrimitive.Root 7 + 8 + const PopoverTrigger = PopoverPrimitive.Trigger 9 + 10 + const PopoverContent = React.forwardRef< 11 + React.ElementRef<typeof PopoverPrimitive.Content>, 12 + React.ComponentPropsWithoutRef<typeof PopoverPrimitive.Content> 13 + >(({ className, align = "center", sideOffset = 4, ...props }, ref) => ( 14 + <PopoverPrimitive.Portal> 15 + <PopoverPrimitive.Content 16 + ref={ref} 17 + align={align} 18 + sideOffset={sideOffset} 19 + className={cn( 20 + "z-50 w-72 rounded-md border bg-popover p-4 text-popover-foreground shadow-md outline-hidden data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2", 21 + className 22 + )} 23 + {...props} 24 + /> 25 + </PopoverPrimitive.Portal> 26 + )) 27 + PopoverContent.displayName = PopoverPrimitive.Content.displayName 28 + 29 + export { Popover, PopoverTrigger, PopoverContent }
+26
src/components/ui/progress.tsx
··· 1 + import * as React from "react" 2 + import * as ProgressPrimitive from "@radix-ui/react-progress" 3 + 4 + import { cn } from "@/lib/utils" 5 + 6 + const Progress = React.forwardRef< 7 + React.ElementRef<typeof ProgressPrimitive.Root>, 8 + React.ComponentPropsWithoutRef<typeof ProgressPrimitive.Root> 9 + >(({ className, value, ...props }, ref) => ( 10 + <ProgressPrimitive.Root 11 + ref={ref} 12 + className={cn( 13 + "relative h-4 w-full overflow-hidden rounded-full bg-secondary", 14 + className 15 + )} 16 + {...props} 17 + > 18 + <ProgressPrimitive.Indicator 19 + className="h-full w-full flex-1 bg-primary transition-all" 20 + style={{ transform: `translateX(-${100 - (value || 0)}%)` }} 21 + /> 22 + </ProgressPrimitive.Root> 23 + )) 24 + Progress.displayName = ProgressPrimitive.Root.displayName 25 + 26 + export { Progress }
+42
src/components/ui/radio-group.tsx
··· 1 + import * as React from "react" 2 + import * as RadioGroupPrimitive from "@radix-ui/react-radio-group" 3 + import { Circle } from "lucide-react" 4 + 5 + import { cn } from "@/lib/utils" 6 + 7 + const RadioGroup = React.forwardRef< 8 + React.ElementRef<typeof RadioGroupPrimitive.Root>, 9 + React.ComponentPropsWithoutRef<typeof RadioGroupPrimitive.Root> 10 + >(({ className, ...props }, ref) => { 11 + return ( 12 + <RadioGroupPrimitive.Root 13 + className={cn("grid gap-2", className)} 14 + {...props} 15 + ref={ref} 16 + /> 17 + ) 18 + }) 19 + RadioGroup.displayName = RadioGroupPrimitive.Root.displayName 20 + 21 + const RadioGroupItem = React.forwardRef< 22 + React.ElementRef<typeof RadioGroupPrimitive.Item>, 23 + React.ComponentPropsWithoutRef<typeof RadioGroupPrimitive.Item> 24 + >(({ className, ...props }, ref) => { 25 + return ( 26 + <RadioGroupPrimitive.Item 27 + ref={ref} 28 + className={cn( 29 + "aspect-square h-4 w-4 rounded-full border border-primary text-primary ring-offset-background focus:outline-hidden focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50", 30 + className 31 + )} 32 + {...props} 33 + > 34 + <RadioGroupPrimitive.Indicator className="flex items-center justify-center"> 35 + <Circle className="h-2.5 w-2.5 fill-current text-current" /> 36 + </RadioGroupPrimitive.Indicator> 37 + </RadioGroupPrimitive.Item> 38 + ) 39 + }) 40 + RadioGroupItem.displayName = RadioGroupPrimitive.Item.displayName 41 + 42 + export { RadioGroup, RadioGroupItem }
+43
src/components/ui/resizable.tsx
··· 1 + import { GripVertical } from "lucide-react" 2 + import * as ResizablePrimitive from "react-resizable-panels" 3 + 4 + import { cn } from "@/lib/utils" 5 + 6 + const ResizablePanelGroup = ({ 7 + className, 8 + ...props 9 + }: React.ComponentProps<typeof ResizablePrimitive.PanelGroup>) => ( 10 + <ResizablePrimitive.PanelGroup 11 + className={cn( 12 + "flex h-full w-full data-[panel-group-direction=vertical]:flex-col", 13 + className 14 + )} 15 + {...props} 16 + /> 17 + ) 18 + 19 + const ResizablePanel = ResizablePrimitive.Panel 20 + 21 + const ResizableHandle = ({ 22 + withHandle, 23 + className, 24 + ...props 25 + }: React.ComponentProps<typeof ResizablePrimitive.PanelResizeHandle> & { 26 + withHandle?: boolean 27 + }) => ( 28 + <ResizablePrimitive.PanelResizeHandle 29 + className={cn( 30 + "relative flex w-px items-center justify-center bg-border after:absolute after:inset-y-0 after:left-1/2 after:w-1 after:-translate-x-1/2 focus-visible:outline-hidden focus-visible:ring-1 focus-visible:ring-ring focus-visible:ring-offset-1 data-[panel-group-direction=vertical]:h-px data-[panel-group-direction=vertical]:w-full data-[panel-group-direction=vertical]:after:left-0 data-[panel-group-direction=vertical]:after:h-1 data-[panel-group-direction=vertical]:after:w-full data-[panel-group-direction=vertical]:after:-translate-y-1/2 data-[panel-group-direction=vertical]:after:translate-x-0 [&[data-panel-group-direction=vertical]>div]:rotate-90", 31 + className 32 + )} 33 + {...props} 34 + > 35 + {withHandle && ( 36 + <div className="z-10 flex h-4 w-3 items-center justify-center rounded-sm border bg-border"> 37 + <GripVertical className="h-2.5 w-2.5" /> 38 + </div> 39 + )} 40 + </ResizablePrimitive.PanelResizeHandle> 41 + ) 42 + 43 + export { ResizablePanelGroup, ResizablePanel, ResizableHandle }
+46
src/components/ui/scroll-area.tsx
··· 1 + import * as React from "react" 2 + import * as ScrollAreaPrimitive from "@radix-ui/react-scroll-area" 3 + 4 + import { cn } from "@/lib/utils" 5 + 6 + const ScrollArea = React.forwardRef< 7 + React.ElementRef<typeof ScrollAreaPrimitive.Root>, 8 + React.ComponentPropsWithoutRef<typeof ScrollAreaPrimitive.Root> 9 + >(({ className, children, ...props }, ref) => ( 10 + <ScrollAreaPrimitive.Root 11 + ref={ref} 12 + className={cn("relative overflow-hidden", className)} 13 + {...props} 14 + > 15 + <ScrollAreaPrimitive.Viewport className="h-full w-full rounded-[inherit]"> 16 + {children} 17 + </ScrollAreaPrimitive.Viewport> 18 + <ScrollBar /> 19 + <ScrollAreaPrimitive.Corner /> 20 + </ScrollAreaPrimitive.Root> 21 + )) 22 + ScrollArea.displayName = ScrollAreaPrimitive.Root.displayName 23 + 24 + const ScrollBar = React.forwardRef< 25 + React.ElementRef<typeof ScrollAreaPrimitive.ScrollAreaScrollbar>, 26 + React.ComponentPropsWithoutRef<typeof ScrollAreaPrimitive.ScrollAreaScrollbar> 27 + >(({ className, orientation = "vertical", ...props }, ref) => ( 28 + <ScrollAreaPrimitive.ScrollAreaScrollbar 29 + ref={ref} 30 + orientation={orientation} 31 + className={cn( 32 + "flex touch-none select-none transition-colors", 33 + orientation === "vertical" && 34 + "h-full w-2.5 border-l border-l-transparent p-px", 35 + orientation === "horizontal" && 36 + "h-2.5 flex-col border-t border-t-transparent p-px", 37 + className 38 + )} 39 + {...props} 40 + > 41 + <ScrollAreaPrimitive.ScrollAreaThumb className="relative flex-1 rounded-full bg-border" /> 42 + </ScrollAreaPrimitive.ScrollAreaScrollbar> 43 + )) 44 + ScrollBar.displayName = ScrollAreaPrimitive.ScrollAreaScrollbar.displayName 45 + 46 + export { ScrollArea, ScrollBar }
+158
src/components/ui/select.tsx
··· 1 + import * as React from "react" 2 + import * as SelectPrimitive from "@radix-ui/react-select" 3 + import { Check, ChevronDown, ChevronUp } from "lucide-react" 4 + 5 + import { cn } from "@/lib/utils" 6 + 7 + const Select = SelectPrimitive.Root 8 + 9 + const SelectGroup = SelectPrimitive.Group 10 + 11 + const SelectValue = SelectPrimitive.Value 12 + 13 + const SelectTrigger = React.forwardRef< 14 + React.ElementRef<typeof SelectPrimitive.Trigger>, 15 + React.ComponentPropsWithoutRef<typeof SelectPrimitive.Trigger> 16 + >(({ className, children, ...props }, ref) => ( 17 + <SelectPrimitive.Trigger 18 + ref={ref} 19 + className={cn( 20 + "flex h-10 w-full items-center justify-between rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus:outline-hidden focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 [&>span]:line-clamp-1", 21 + className 22 + )} 23 + {...props} 24 + > 25 + {children} 26 + <SelectPrimitive.Icon asChild> 27 + <ChevronDown className="h-4 w-4 opacity-50" /> 28 + </SelectPrimitive.Icon> 29 + </SelectPrimitive.Trigger> 30 + )) 31 + SelectTrigger.displayName = SelectPrimitive.Trigger.displayName 32 + 33 + const SelectScrollUpButton = React.forwardRef< 34 + React.ElementRef<typeof SelectPrimitive.ScrollUpButton>, 35 + React.ComponentPropsWithoutRef<typeof SelectPrimitive.ScrollUpButton> 36 + >(({ className, ...props }, ref) => ( 37 + <SelectPrimitive.ScrollUpButton 38 + ref={ref} 39 + className={cn( 40 + "flex cursor-default items-center justify-center py-1", 41 + className 42 + )} 43 + {...props} 44 + > 45 + <ChevronUp className="h-4 w-4" /> 46 + </SelectPrimitive.ScrollUpButton> 47 + )) 48 + SelectScrollUpButton.displayName = SelectPrimitive.ScrollUpButton.displayName 49 + 50 + const SelectScrollDownButton = React.forwardRef< 51 + React.ElementRef<typeof SelectPrimitive.ScrollDownButton>, 52 + React.ComponentPropsWithoutRef<typeof SelectPrimitive.ScrollDownButton> 53 + >(({ className, ...props }, ref) => ( 54 + <SelectPrimitive.ScrollDownButton 55 + ref={ref} 56 + className={cn( 57 + "flex cursor-default items-center justify-center py-1", 58 + className 59 + )} 60 + {...props} 61 + > 62 + <ChevronDown className="h-4 w-4" /> 63 + </SelectPrimitive.ScrollDownButton> 64 + )) 65 + SelectScrollDownButton.displayName = 66 + SelectPrimitive.ScrollDownButton.displayName 67 + 68 + const SelectContent = React.forwardRef< 69 + React.ElementRef<typeof SelectPrimitive.Content>, 70 + React.ComponentPropsWithoutRef<typeof SelectPrimitive.Content> 71 + >(({ className, children, position = "popper", ...props }, ref) => ( 72 + <SelectPrimitive.Portal> 73 + <SelectPrimitive.Content 74 + ref={ref} 75 + className={cn( 76 + "relative z-50 max-h-96 min-w-32 overflow-hidden rounded-md border bg-popover text-popover-foreground shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2", 77 + position === "popper" && 78 + "data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1", 79 + className 80 + )} 81 + position={position} 82 + {...props} 83 + > 84 + <SelectScrollUpButton /> 85 + <SelectPrimitive.Viewport 86 + className={cn( 87 + "p-1", 88 + position === "popper" && 89 + "h-(--radix-select-trigger-height) w-full min-w-(--radix-select-trigger-width)" 90 + )} 91 + > 92 + {children} 93 + </SelectPrimitive.Viewport> 94 + <SelectScrollDownButton /> 95 + </SelectPrimitive.Content> 96 + </SelectPrimitive.Portal> 97 + )) 98 + SelectContent.displayName = SelectPrimitive.Content.displayName 99 + 100 + const SelectLabel = React.forwardRef< 101 + React.ElementRef<typeof SelectPrimitive.Label>, 102 + React.ComponentPropsWithoutRef<typeof SelectPrimitive.Label> 103 + >(({ className, ...props }, ref) => ( 104 + <SelectPrimitive.Label 105 + ref={ref} 106 + className={cn("py-1.5 pl-8 pr-2 text-sm font-semibold", className)} 107 + {...props} 108 + /> 109 + )) 110 + SelectLabel.displayName = SelectPrimitive.Label.displayName 111 + 112 + const SelectItem = React.forwardRef< 113 + React.ElementRef<typeof SelectPrimitive.Item>, 114 + React.ComponentPropsWithoutRef<typeof SelectPrimitive.Item> 115 + >(({ className, children, ...props }, ref) => ( 116 + <SelectPrimitive.Item 117 + ref={ref} 118 + className={cn( 119 + "relative flex w-full cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-hidden focus:bg-accent focus:text-accent-foreground data-disabled:pointer-events-none data-disabled:opacity-50", 120 + className 121 + )} 122 + {...props} 123 + > 124 + <span className="absolute left-2 flex h-3.5 w-3.5 items-center justify-center"> 125 + <SelectPrimitive.ItemIndicator> 126 + <Check className="h-4 w-4" /> 127 + </SelectPrimitive.ItemIndicator> 128 + </span> 129 + 130 + <SelectPrimitive.ItemText>{children}</SelectPrimitive.ItemText> 131 + </SelectPrimitive.Item> 132 + )) 133 + SelectItem.displayName = SelectPrimitive.Item.displayName 134 + 135 + const SelectSeparator = React.forwardRef< 136 + React.ElementRef<typeof SelectPrimitive.Separator>, 137 + React.ComponentPropsWithoutRef<typeof SelectPrimitive.Separator> 138 + >(({ className, ...props }, ref) => ( 139 + <SelectPrimitive.Separator 140 + ref={ref} 141 + className={cn("-mx-1 my-1 h-px bg-muted", className)} 142 + {...props} 143 + /> 144 + )) 145 + SelectSeparator.displayName = SelectPrimitive.Separator.displayName 146 + 147 + export { 148 + Select, 149 + SelectGroup, 150 + SelectValue, 151 + SelectTrigger, 152 + SelectContent, 153 + SelectLabel, 154 + SelectItem, 155 + SelectSeparator, 156 + SelectScrollUpButton, 157 + SelectScrollDownButton, 158 + }
+29
src/components/ui/separator.tsx
··· 1 + import * as React from "react" 2 + import * as SeparatorPrimitive from "@radix-ui/react-separator" 3 + 4 + import { cn } from "@/lib/utils" 5 + 6 + const Separator = React.forwardRef< 7 + React.ElementRef<typeof SeparatorPrimitive.Root>, 8 + React.ComponentPropsWithoutRef<typeof SeparatorPrimitive.Root> 9 + >( 10 + ( 11 + { className, orientation = "horizontal", decorative = true, ...props }, 12 + ref 13 + ) => ( 14 + <SeparatorPrimitive.Root 15 + ref={ref} 16 + decorative={decorative} 17 + orientation={orientation} 18 + className={cn( 19 + "shrink-0 bg-border", 20 + orientation === "horizontal" ? "h-px w-full" : "h-full w-px", 21 + className 22 + )} 23 + {...props} 24 + /> 25 + ) 26 + ) 27 + Separator.displayName = SeparatorPrimitive.Root.displayName 28 + 29 + export { Separator }
+131
src/components/ui/sheet.tsx
··· 1 + import * as SheetPrimitive from "@radix-ui/react-dialog" 2 + import { cva, type VariantProps } from "class-variance-authority" 3 + import { X } from "lucide-react" 4 + import * as React from "react" 5 + 6 + import { cn } from "@/lib/utils" 7 + 8 + const Sheet = SheetPrimitive.Root 9 + 10 + const SheetTrigger = SheetPrimitive.Trigger 11 + 12 + const SheetClose = SheetPrimitive.Close 13 + 14 + const SheetPortal = SheetPrimitive.Portal 15 + 16 + const SheetOverlay = React.forwardRef< 17 + React.ElementRef<typeof SheetPrimitive.Overlay>, 18 + React.ComponentPropsWithoutRef<typeof SheetPrimitive.Overlay> 19 + >(({ className, ...props }, ref) => ( 20 + <SheetPrimitive.Overlay 21 + className={cn( 22 + "fixed inset-0 z-50 bg-black/10 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0", 23 + className 24 + )} 25 + {...props} 26 + ref={ref} 27 + /> 28 + )) 29 + SheetOverlay.displayName = SheetPrimitive.Overlay.displayName 30 + 31 + const sheetVariants = cva( 32 + "fixed z-50 gap-4 bg-background p-6 shadow-lg transition ease-in-out data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:duration-300 data-[state=open]:duration-500", 33 + { 34 + variants: { 35 + side: { 36 + top: "inset-x-0 top-0 border-b data-[state=closed]:slide-out-to-top data-[state=open]:slide-in-from-top", 37 + bottom: 38 + "inset-x-0 bottom-0 border-t data-[state=closed]:slide-out-to-bottom data-[state=open]:slide-in-from-bottom", 39 + left: "inset-y-0 left-0 h-full w-3/4 border-r data-[state=closed]:slide-out-to-left data-[state=open]:slide-in-from-left sm:max-w-sm", 40 + right: 41 + "inset-y-0 right-0 h-full w-3/4 border-l data-[state=closed]:slide-out-to-right data-[state=open]:slide-in-from-right sm:max-w-sm", 42 + }, 43 + }, 44 + defaultVariants: { 45 + side: "right", 46 + }, 47 + } 48 + ) 49 + 50 + interface SheetContentProps 51 + extends React.ComponentPropsWithoutRef<typeof SheetPrimitive.Content>, 52 + VariantProps<typeof sheetVariants> { } 53 + 54 + const SheetContent = React.forwardRef< 55 + React.ElementRef<typeof SheetPrimitive.Content>, 56 + SheetContentProps 57 + >(({ side = "right", className, children, ...props }, ref) => ( 58 + <SheetPortal> 59 + <SheetOverlay /> 60 + <SheetPrimitive.Content 61 + ref={ref} 62 + className={cn(sheetVariants({ side }), className)} 63 + {...props} 64 + > 65 + {children} 66 + <SheetPrimitive.Close className="absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-hidden focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-secondary"> 67 + <X className="h-4 w-4" /> 68 + <span className="sr-only">Close</span> 69 + </SheetPrimitive.Close> 70 + </SheetPrimitive.Content> 71 + </SheetPortal> 72 + )) 73 + SheetContent.displayName = SheetPrimitive.Content.displayName 74 + 75 + const SheetHeader = ({ 76 + className, 77 + ...props 78 + }: React.HTMLAttributes<HTMLDivElement>) => ( 79 + <div 80 + className={cn( 81 + "flex flex-col space-y-2 text-center sm:text-left", 82 + className 83 + )} 84 + {...props} 85 + /> 86 + ) 87 + SheetHeader.displayName = "SheetHeader" 88 + 89 + const SheetFooter = ({ 90 + className, 91 + ...props 92 + }: React.HTMLAttributes<HTMLDivElement>) => ( 93 + <div 94 + className={cn( 95 + "flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2", 96 + className 97 + )} 98 + {...props} 99 + /> 100 + ) 101 + SheetFooter.displayName = "SheetFooter" 102 + 103 + const SheetTitle = React.forwardRef< 104 + React.ElementRef<typeof SheetPrimitive.Title>, 105 + React.ComponentPropsWithoutRef<typeof SheetPrimitive.Title> 106 + >(({ className, ...props }, ref) => ( 107 + <SheetPrimitive.Title 108 + ref={ref} 109 + className={cn("text-lg font-semibold text-foreground", className)} 110 + {...props} 111 + /> 112 + )) 113 + SheetTitle.displayName = SheetPrimitive.Title.displayName 114 + 115 + const SheetDescription = React.forwardRef< 116 + React.ElementRef<typeof SheetPrimitive.Description>, 117 + React.ComponentPropsWithoutRef<typeof SheetPrimitive.Description> 118 + >(({ className, ...props }, ref) => ( 119 + <SheetPrimitive.Description 120 + ref={ref} 121 + className={cn("text-sm text-muted-foreground", className)} 122 + {...props} 123 + /> 124 + )) 125 + SheetDescription.displayName = SheetPrimitive.Description.displayName 126 + 127 + export { 128 + Sheet, SheetClose, 129 + SheetContent, SheetDescription, SheetFooter, SheetHeader, SheetOverlay, SheetPortal, SheetTitle, SheetTrigger 130 + } 131 +
+761
src/components/ui/sidebar.tsx
··· 1 + import * as React from "react" 2 + import { Slot } from "@radix-ui/react-slot" 3 + import { VariantProps, cva } from "class-variance-authority" 4 + import { PanelLeft } from "lucide-react" 5 + 6 + import { useIsMobile } from "@/hooks/use-mobile" 7 + import { cn } from "@/lib/utils" 8 + import { Button } from "@/components/ui/button" 9 + import { Input } from "@/components/ui/input" 10 + import { Separator } from "@/components/ui/separator" 11 + import { Sheet, SheetContent } from "@/components/ui/sheet" 12 + import { Skeleton } from "@/components/ui/skeleton" 13 + import { 14 + Tooltip, 15 + TooltipContent, 16 + TooltipProvider, 17 + TooltipTrigger, 18 + } from "@/components/ui/tooltip" 19 + 20 + const SIDEBAR_COOKIE_NAME = "sidebar:state" 21 + const SIDEBAR_COOKIE_MAX_AGE = 60 * 60 * 24 * 7 22 + const SIDEBAR_WIDTH = "16rem" 23 + const SIDEBAR_WIDTH_MOBILE = "18rem" 24 + const SIDEBAR_WIDTH_ICON = "3rem" 25 + const SIDEBAR_KEYBOARD_SHORTCUT = "b" 26 + 27 + type SidebarContext = { 28 + state: "expanded" | "collapsed" 29 + open: boolean 30 + setOpen: (open: boolean) => void 31 + openMobile: boolean 32 + setOpenMobile: (open: boolean) => void 33 + isMobile: boolean 34 + toggleSidebar: () => void 35 + } 36 + 37 + const SidebarContext = React.createContext<SidebarContext | null>(null) 38 + 39 + function useSidebar() { 40 + const context = React.useContext(SidebarContext) 41 + if (!context) { 42 + throw new Error("useSidebar must be used within a SidebarProvider.") 43 + } 44 + 45 + return context 46 + } 47 + 48 + const SidebarProvider = React.forwardRef< 49 + HTMLDivElement, 50 + React.ComponentProps<"div"> & { 51 + defaultOpen?: boolean 52 + open?: boolean 53 + onOpenChange?: (open: boolean) => void 54 + } 55 + >( 56 + ( 57 + { 58 + defaultOpen = true, 59 + open: openProp, 60 + onOpenChange: setOpenProp, 61 + className, 62 + style, 63 + children, 64 + ...props 65 + }, 66 + ref 67 + ) => { 68 + const isMobile = useIsMobile() 69 + const [openMobile, setOpenMobile] = React.useState(false) 70 + 71 + // This is the internal state of the sidebar. 72 + // We use openProp and setOpenProp for control from outside the component. 73 + const [_open, _setOpen] = React.useState(defaultOpen) 74 + const open = openProp ?? _open 75 + const setOpen = React.useCallback( 76 + (value: boolean | ((value: boolean) => boolean)) => { 77 + const openState = typeof value === "function" ? value(open) : value 78 + if (setOpenProp) { 79 + setOpenProp(openState) 80 + } else { 81 + _setOpen(openState) 82 + } 83 + 84 + // This sets the cookie to keep the sidebar state. 85 + document.cookie = `${SIDEBAR_COOKIE_NAME}=${openState}; path=/; max-age=${SIDEBAR_COOKIE_MAX_AGE}` 86 + }, 87 + [setOpenProp, open] 88 + ) 89 + 90 + // Helper to toggle the sidebar. 91 + const toggleSidebar = React.useCallback(() => { 92 + return isMobile 93 + ? setOpenMobile((open) => !open) 94 + : setOpen((open) => !open) 95 + }, [isMobile, setOpen, setOpenMobile]) 96 + 97 + // Adds a keyboard shortcut to toggle the sidebar. 98 + React.useEffect(() => { 99 + const handleKeyDown = (event: KeyboardEvent) => { 100 + if ( 101 + event.key === SIDEBAR_KEYBOARD_SHORTCUT && 102 + (event.metaKey || event.ctrlKey) 103 + ) { 104 + event.preventDefault() 105 + toggleSidebar() 106 + } 107 + } 108 + 109 + window.addEventListener("keydown", handleKeyDown) 110 + return () => window.removeEventListener("keydown", handleKeyDown) 111 + }, [toggleSidebar]) 112 + 113 + // We add a state so that we can do data-state="expanded" or "collapsed". 114 + // This makes it easier to style the sidebar with Tailwind classes. 115 + const state = open ? "expanded" : "collapsed" 116 + 117 + const contextValue = React.useMemo<SidebarContext>( 118 + () => ({ 119 + state, 120 + open, 121 + setOpen, 122 + isMobile, 123 + openMobile, 124 + setOpenMobile, 125 + toggleSidebar, 126 + }), 127 + [state, open, setOpen, isMobile, openMobile, setOpenMobile, toggleSidebar] 128 + ) 129 + 130 + return ( 131 + <SidebarContext.Provider value={contextValue}> 132 + <TooltipProvider delayDuration={0}> 133 + <div 134 + style={ 135 + { 136 + "--sidebar-width": SIDEBAR_WIDTH, 137 + "--sidebar-width-icon": SIDEBAR_WIDTH_ICON, 138 + ...style, 139 + } as React.CSSProperties 140 + } 141 + className={cn( 142 + "group/sidebar-wrapper flex min-h-svh w-full has-data-[variant=inset]:bg-sidebar", 143 + className 144 + )} 145 + ref={ref} 146 + {...props} 147 + > 148 + {children} 149 + </div> 150 + </TooltipProvider> 151 + </SidebarContext.Provider> 152 + ) 153 + } 154 + ) 155 + SidebarProvider.displayName = "SidebarProvider" 156 + 157 + const Sidebar = React.forwardRef< 158 + HTMLDivElement, 159 + React.ComponentProps<"div"> & { 160 + side?: "left" | "right" 161 + variant?: "sidebar" | "floating" | "inset" 162 + collapsible?: "offcanvas" | "icon" | "none" 163 + } 164 + >( 165 + ( 166 + { 167 + side = "left", 168 + variant = "sidebar", 169 + collapsible = "offcanvas", 170 + className, 171 + children, 172 + ...props 173 + }, 174 + ref 175 + ) => { 176 + const { isMobile, state, openMobile, setOpenMobile } = useSidebar() 177 + 178 + if (collapsible === "none") { 179 + return ( 180 + <div 181 + className={cn( 182 + "flex h-full w-(--sidebar-width) flex-col bg-sidebar text-sidebar-foreground", 183 + className 184 + )} 185 + ref={ref} 186 + {...props} 187 + > 188 + {children} 189 + </div> 190 + ) 191 + } 192 + 193 + if (isMobile) { 194 + return ( 195 + <Sheet open={openMobile} onOpenChange={setOpenMobile} {...props}> 196 + <SheetContent 197 + data-sidebar="sidebar" 198 + data-mobile="true" 199 + className="w-(--sidebar-width) bg-sidebar p-0 text-sidebar-foreground [&>button]:hidden" 200 + style={ 201 + { 202 + "--sidebar-width": SIDEBAR_WIDTH_MOBILE, 203 + } as React.CSSProperties 204 + } 205 + side={side} 206 + > 207 + <div className="flex h-full w-full flex-col">{children}</div> 208 + </SheetContent> 209 + </Sheet> 210 + ) 211 + } 212 + 213 + return ( 214 + <div 215 + ref={ref} 216 + className="group peer hidden md:block text-sidebar-foreground" 217 + data-state={state} 218 + data-collapsible={state === "collapsed" ? collapsible : ""} 219 + data-variant={variant} 220 + data-side={side} 221 + > 222 + {/* This is what handles the sidebar gap on desktop */} 223 + <div 224 + className={cn( 225 + "duration-200 relative h-svh w-(--sidebar-width) bg-transparent transition-[width] ease-linear", 226 + "group-data-[collapsible=offcanvas]:w-0", 227 + "group-data-[side=right]:rotate-180", 228 + variant === "floating" || variant === "inset" 229 + ? "group-data-[collapsible=icon]:w-[calc(var(--sidebar-width-icon)+(--spacing(4)))]" 230 + : "group-data-[collapsible=icon]:w-(--sidebar-width-icon)" 231 + )} 232 + /> 233 + <div 234 + className={cn( 235 + "duration-200 fixed inset-y-0 z-10 hidden h-svh w-(--sidebar-width) transition-[left,right,width] ease-linear md:flex", 236 + side === "left" 237 + ? "left-0 group-data-[collapsible=offcanvas]:left-[calc(var(--sidebar-width)*-1)]" 238 + : "right-0 group-data-[collapsible=offcanvas]:right-[calc(var(--sidebar-width)*-1)]", 239 + // Adjust the padding for floating and inset variants. 240 + variant === "floating" || variant === "inset" 241 + ? "p-2 group-data-[collapsible=icon]:w-[calc(var(--sidebar-width-icon)+(--spacing(4))+2px)]" 242 + : "group-data-[collapsible=icon]:w-(--sidebar-width-icon) group-data-[side=left]:border-r group-data-[side=right]:border-l", 243 + className 244 + )} 245 + {...props} 246 + > 247 + <div 248 + data-sidebar="sidebar" 249 + className="flex h-full w-full flex-col bg-sidebar group-data-[variant=floating]:rounded-lg group-data-[variant=floating]:border group-data-[variant=floating]:border-sidebar-border group-data-[variant=floating]:shadow-sm" 250 + > 251 + {children} 252 + </div> 253 + </div> 254 + </div> 255 + ) 256 + } 257 + ) 258 + Sidebar.displayName = "Sidebar" 259 + 260 + const SidebarTrigger = React.forwardRef< 261 + React.ElementRef<typeof Button>, 262 + React.ComponentProps<typeof Button> 263 + >(({ className, onClick, ...props }, ref) => { 264 + const { toggleSidebar } = useSidebar() 265 + 266 + return ( 267 + <Button 268 + ref={ref} 269 + data-sidebar="trigger" 270 + variant="ghost" 271 + size="icon" 272 + className={cn("h-7 w-7", className)} 273 + onClick={(event) => { 274 + onClick?.(event) 275 + toggleSidebar() 276 + }} 277 + {...props} 278 + > 279 + <PanelLeft /> 280 + <span className="sr-only">Toggle Sidebar</span> 281 + </Button> 282 + ) 283 + }) 284 + SidebarTrigger.displayName = "SidebarTrigger" 285 + 286 + const SidebarRail = React.forwardRef< 287 + HTMLButtonElement, 288 + React.ComponentProps<"button"> 289 + >(({ className, ...props }, ref) => { 290 + const { toggleSidebar } = useSidebar() 291 + 292 + return ( 293 + <button 294 + ref={ref} 295 + data-sidebar="rail" 296 + aria-label="Toggle Sidebar" 297 + tabIndex={-1} 298 + onClick={toggleSidebar} 299 + title="Toggle Sidebar" 300 + className={cn( 301 + "absolute inset-y-0 z-20 hidden w-4 -translate-x-1/2 transition-all ease-linear after:absolute after:inset-y-0 after:left-1/2 after:w-[2px] hover:after:bg-sidebar-border group-data-[side=left]:-right-4 group-data-[side=right]:left-0 sm:flex", 302 + "in-data-[side=left]:cursor-w-resize in-data-[side=right]:cursor-e-resize", 303 + "[[data-side=left][data-state=collapsed]_&]:cursor-e-resize [[data-side=right][data-state=collapsed]_&]:cursor-w-resize", 304 + "group-data-[collapsible=offcanvas]:translate-x-0 group-data-[collapsible=offcanvas]:after:left-full hover:group-data-[collapsible=offcanvas]:bg-sidebar", 305 + "[[data-side=left][data-collapsible=offcanvas]_&]:-right-2", 306 + "[[data-side=right][data-collapsible=offcanvas]_&]:-left-2", 307 + className 308 + )} 309 + {...props} 310 + /> 311 + ) 312 + }) 313 + SidebarRail.displayName = "SidebarRail" 314 + 315 + const SidebarInset = React.forwardRef< 316 + HTMLDivElement, 317 + React.ComponentProps<"main"> 318 + >(({ className, ...props }, ref) => { 319 + return ( 320 + <main 321 + ref={ref} 322 + className={cn( 323 + "relative flex min-h-svh flex-1 flex-col bg-background", 324 + "peer-data-[variant=inset]:min-h-[calc(100svh-(--spacing(4)))] md:peer-data-[variant=inset]:m-2 md:peer-data-[variant=inset]:peer-data-[state=collapsed]:ml-2 md:peer-data-[variant=inset]:ml-0 md:peer-data-[variant=inset]:rounded-xl md:peer-data-[variant=inset]:shadow-sm", 325 + className 326 + )} 327 + {...props} 328 + /> 329 + ) 330 + }) 331 + SidebarInset.displayName = "SidebarInset" 332 + 333 + const SidebarInput = React.forwardRef< 334 + React.ElementRef<typeof Input>, 335 + React.ComponentProps<typeof Input> 336 + >(({ className, ...props }, ref) => { 337 + return ( 338 + <Input 339 + ref={ref} 340 + data-sidebar="input" 341 + className={cn( 342 + "h-8 w-full bg-background shadow-none focus-visible:ring-2 focus-visible:ring-sidebar-ring", 343 + className 344 + )} 345 + {...props} 346 + /> 347 + ) 348 + }) 349 + SidebarInput.displayName = "SidebarInput" 350 + 351 + const SidebarHeader = React.forwardRef< 352 + HTMLDivElement, 353 + React.ComponentProps<"div"> 354 + >(({ className, ...props }, ref) => { 355 + return ( 356 + <div 357 + ref={ref} 358 + data-sidebar="header" 359 + className={cn("flex flex-col gap-2 p-2", className)} 360 + {...props} 361 + /> 362 + ) 363 + }) 364 + SidebarHeader.displayName = "SidebarHeader" 365 + 366 + const SidebarFooter = React.forwardRef< 367 + HTMLDivElement, 368 + React.ComponentProps<"div"> 369 + >(({ className, ...props }, ref) => { 370 + return ( 371 + <div 372 + ref={ref} 373 + data-sidebar="footer" 374 + className={cn("flex flex-col gap-2 p-2", className)} 375 + {...props} 376 + /> 377 + ) 378 + }) 379 + SidebarFooter.displayName = "SidebarFooter" 380 + 381 + const SidebarSeparator = React.forwardRef< 382 + React.ElementRef<typeof Separator>, 383 + React.ComponentProps<typeof Separator> 384 + >(({ className, ...props }, ref) => { 385 + return ( 386 + <Separator 387 + ref={ref} 388 + data-sidebar="separator" 389 + className={cn("mx-2 w-auto bg-sidebar-border", className)} 390 + {...props} 391 + /> 392 + ) 393 + }) 394 + SidebarSeparator.displayName = "SidebarSeparator" 395 + 396 + const SidebarContent = React.forwardRef< 397 + HTMLDivElement, 398 + React.ComponentProps<"div"> 399 + >(({ className, ...props }, ref) => { 400 + return ( 401 + <div 402 + ref={ref} 403 + data-sidebar="content" 404 + className={cn( 405 + "flex min-h-0 flex-1 flex-col gap-2 overflow-auto group-data-[collapsible=icon]:overflow-hidden", 406 + className 407 + )} 408 + {...props} 409 + /> 410 + ) 411 + }) 412 + SidebarContent.displayName = "SidebarContent" 413 + 414 + const SidebarGroup = React.forwardRef< 415 + HTMLDivElement, 416 + React.ComponentProps<"div"> 417 + >(({ className, ...props }, ref) => { 418 + return ( 419 + <div 420 + ref={ref} 421 + data-sidebar="group" 422 + className={cn("relative flex w-full min-w-0 flex-col p-2", className)} 423 + {...props} 424 + /> 425 + ) 426 + }) 427 + SidebarGroup.displayName = "SidebarGroup" 428 + 429 + const SidebarGroupLabel = React.forwardRef< 430 + HTMLDivElement, 431 + React.ComponentProps<"div"> & { asChild?: boolean } 432 + >(({ className, asChild = false, ...props }, ref) => { 433 + const Comp = asChild ? Slot : "div" 434 + 435 + return ( 436 + <Comp 437 + ref={ref} 438 + data-sidebar="group-label" 439 + className={cn( 440 + "duration-200 flex h-8 shrink-0 items-center rounded-md px-2 text-xs font-medium text-sidebar-foreground/70 outline-hidden ring-sidebar-ring transition-[margin,opa] ease-linear focus-visible:ring-2 [&>svg]:size-4 [&>svg]:shrink-0", 441 + "group-data-[collapsible=icon]:-mt-8 group-data-[collapsible=icon]:opacity-0", 442 + className 443 + )} 444 + {...props} 445 + /> 446 + ) 447 + }) 448 + SidebarGroupLabel.displayName = "SidebarGroupLabel" 449 + 450 + const SidebarGroupAction = React.forwardRef< 451 + HTMLButtonElement, 452 + React.ComponentProps<"button"> & { asChild?: boolean } 453 + >(({ className, asChild = false, ...props }, ref) => { 454 + const Comp = asChild ? Slot : "button" 455 + 456 + return ( 457 + <Comp 458 + ref={ref} 459 + data-sidebar="group-action" 460 + className={cn( 461 + "absolute right-3 top-3.5 flex aspect-square w-5 items-center justify-center rounded-md p-0 text-sidebar-foreground outline-hidden ring-sidebar-ring transition-transform hover:bg-sidebar-accent hover:text-sidebar-accent-foreground focus-visible:ring-2 [&>svg]:size-4 [&>svg]:shrink-0", 462 + // Increases the hit area of the button on mobile. 463 + "after:absolute after:-inset-2 md:after:hidden", 464 + "group-data-[collapsible=icon]:hidden", 465 + className 466 + )} 467 + {...props} 468 + /> 469 + ) 470 + }) 471 + SidebarGroupAction.displayName = "SidebarGroupAction" 472 + 473 + const SidebarGroupContent = React.forwardRef< 474 + HTMLDivElement, 475 + React.ComponentProps<"div"> 476 + >(({ className, ...props }, ref) => ( 477 + <div 478 + ref={ref} 479 + data-sidebar="group-content" 480 + className={cn("w-full text-sm", className)} 481 + {...props} 482 + /> 483 + )) 484 + SidebarGroupContent.displayName = "SidebarGroupContent" 485 + 486 + const SidebarMenu = React.forwardRef< 487 + HTMLUListElement, 488 + React.ComponentProps<"ul"> 489 + >(({ className, ...props }, ref) => ( 490 + <ul 491 + ref={ref} 492 + data-sidebar="menu" 493 + className={cn("flex w-full min-w-0 flex-col gap-1", className)} 494 + {...props} 495 + /> 496 + )) 497 + SidebarMenu.displayName = "SidebarMenu" 498 + 499 + const SidebarMenuItem = React.forwardRef< 500 + HTMLLIElement, 501 + React.ComponentProps<"li"> 502 + >(({ className, ...props }, ref) => ( 503 + <li 504 + ref={ref} 505 + data-sidebar="menu-item" 506 + className={cn("group/menu-item relative", className)} 507 + {...props} 508 + /> 509 + )) 510 + SidebarMenuItem.displayName = "SidebarMenuItem" 511 + 512 + const sidebarMenuButtonVariants = cva( 513 + "peer/menu-button flex w-full items-center gap-2 overflow-hidden rounded-md p-2 text-left text-sm outline-hidden ring-sidebar-ring transition-[width,height,padding] hover:bg-sidebar-accent hover:text-sidebar-accent-foreground focus-visible:ring-2 active:bg-sidebar-accent active:text-sidebar-accent-foreground disabled:pointer-events-none disabled:opacity-50 group-has-data-[sidebar=menu-action]/menu-item:pr-8 aria-disabled:pointer-events-none aria-disabled:opacity-50 data-[active=true]:bg-sidebar-accent data-[active=true]:font-medium data-[active=true]:text-sidebar-accent-foreground data-[state=open]:hover:bg-sidebar-accent data-[state=open]:hover:text-sidebar-accent-foreground group-data-[collapsible=icon]:size-8! group-data-[collapsible=icon]:p-2! [&>span:last-child]:truncate [&>svg]:size-4 [&>svg]:shrink-0", 514 + { 515 + variants: { 516 + variant: { 517 + default: "hover:bg-sidebar-accent hover:text-sidebar-accent-foreground", 518 + outline: 519 + "bg-background shadow-[0_0_0_1px_hsl(var(--sidebar-border))] hover:bg-sidebar-accent hover:text-sidebar-accent-foreground hover:shadow-[0_0_0_1px_hsl(var(--sidebar-accent))]", 520 + }, 521 + size: { 522 + default: "h-8 text-sm", 523 + sm: "h-7 text-xs", 524 + lg: "h-12 text-sm group-data-[collapsible=icon]:p-0!", 525 + }, 526 + }, 527 + defaultVariants: { 528 + variant: "default", 529 + size: "default", 530 + }, 531 + } 532 + ) 533 + 534 + const SidebarMenuButton = React.forwardRef< 535 + HTMLButtonElement, 536 + React.ComponentProps<"button"> & { 537 + asChild?: boolean 538 + isActive?: boolean 539 + tooltip?: string | React.ComponentProps<typeof TooltipContent> 540 + } & VariantProps<typeof sidebarMenuButtonVariants> 541 + >( 542 + ( 543 + { 544 + asChild = false, 545 + isActive = false, 546 + variant = "default", 547 + size = "default", 548 + tooltip, 549 + className, 550 + ...props 551 + }, 552 + ref 553 + ) => { 554 + const Comp = asChild ? Slot : "button" 555 + const { isMobile, state } = useSidebar() 556 + 557 + const button = ( 558 + <Comp 559 + ref={ref} 560 + data-sidebar="menu-button" 561 + data-size={size} 562 + data-active={isActive} 563 + className={cn(sidebarMenuButtonVariants({ variant, size }), className)} 564 + {...props} 565 + /> 566 + ) 567 + 568 + if (!tooltip) { 569 + return button 570 + } 571 + 572 + if (typeof tooltip === "string") { 573 + tooltip = { 574 + children: tooltip, 575 + } 576 + } 577 + 578 + return ( 579 + <Tooltip> 580 + <TooltipTrigger asChild>{button}</TooltipTrigger> 581 + <TooltipContent 582 + side="right" 583 + align="center" 584 + hidden={state !== "collapsed" || isMobile} 585 + {...tooltip} 586 + /> 587 + </Tooltip> 588 + ) 589 + } 590 + ) 591 + SidebarMenuButton.displayName = "SidebarMenuButton" 592 + 593 + const SidebarMenuAction = React.forwardRef< 594 + HTMLButtonElement, 595 + React.ComponentProps<"button"> & { 596 + asChild?: boolean 597 + showOnHover?: boolean 598 + } 599 + >(({ className, asChild = false, showOnHover = false, ...props }, ref) => { 600 + const Comp = asChild ? Slot : "button" 601 + 602 + return ( 603 + <Comp 604 + ref={ref} 605 + data-sidebar="menu-action" 606 + className={cn( 607 + "absolute right-1 top-1.5 flex aspect-square w-5 items-center justify-center rounded-md p-0 text-sidebar-foreground outline-hidden ring-sidebar-ring transition-transform hover:bg-sidebar-accent hover:text-sidebar-accent-foreground focus-visible:ring-2 peer-hover/menu-button:text-sidebar-accent-foreground [&>svg]:size-4 [&>svg]:shrink-0", 608 + // Increases the hit area of the button on mobile. 609 + "after:absolute after:-inset-2 md:after:hidden", 610 + "peer-data-[size=sm]/menu-button:top-1", 611 + "peer-data-[size=default]/menu-button:top-1.5", 612 + "peer-data-[size=lg]/menu-button:top-2.5", 613 + "group-data-[collapsible=icon]:hidden", 614 + showOnHover && 615 + "group-focus-within/menu-item:opacity-100 group-hover/menu-item:opacity-100 data-[state=open]:opacity-100 peer-data-[active=true]/menu-button:text-sidebar-accent-foreground md:opacity-0", 616 + className 617 + )} 618 + {...props} 619 + /> 620 + ) 621 + }) 622 + SidebarMenuAction.displayName = "SidebarMenuAction" 623 + 624 + const SidebarMenuBadge = React.forwardRef< 625 + HTMLDivElement, 626 + React.ComponentProps<"div"> 627 + >(({ className, ...props }, ref) => ( 628 + <div 629 + ref={ref} 630 + data-sidebar="menu-badge" 631 + className={cn( 632 + "absolute right-1 flex h-5 min-w-5 items-center justify-center rounded-md px-1 text-xs font-medium tabular-nums text-sidebar-foreground select-none pointer-events-none", 633 + "peer-hover/menu-button:text-sidebar-accent-foreground peer-data-[active=true]/menu-button:text-sidebar-accent-foreground", 634 + "peer-data-[size=sm]/menu-button:top-1", 635 + "peer-data-[size=default]/menu-button:top-1.5", 636 + "peer-data-[size=lg]/menu-button:top-2.5", 637 + "group-data-[collapsible=icon]:hidden", 638 + className 639 + )} 640 + {...props} 641 + /> 642 + )) 643 + SidebarMenuBadge.displayName = "SidebarMenuBadge" 644 + 645 + const SidebarMenuSkeleton = React.forwardRef< 646 + HTMLDivElement, 647 + React.ComponentProps<"div"> & { 648 + showIcon?: boolean 649 + } 650 + >(({ className, showIcon = false, ...props }, ref) => { 651 + // Random width between 50 to 90%. 652 + const width = React.useMemo(() => { 653 + return `${Math.floor(Math.random() * 40) + 50}%` 654 + }, []) 655 + 656 + return ( 657 + <div 658 + ref={ref} 659 + data-sidebar="menu-skeleton" 660 + className={cn("rounded-md h-8 flex gap-2 px-2 items-center", className)} 661 + {...props} 662 + > 663 + {showIcon && ( 664 + <Skeleton 665 + className="size-4 rounded-md" 666 + data-sidebar="menu-skeleton-icon" 667 + /> 668 + )} 669 + <Skeleton 670 + className="h-4 flex-1 max-w-(--skeleton-width)" 671 + data-sidebar="menu-skeleton-text" 672 + style={ 673 + { 674 + "--skeleton-width": width, 675 + } as React.CSSProperties 676 + } 677 + /> 678 + </div> 679 + ) 680 + }) 681 + SidebarMenuSkeleton.displayName = "SidebarMenuSkeleton" 682 + 683 + const SidebarMenuSub = React.forwardRef< 684 + HTMLUListElement, 685 + React.ComponentProps<"ul"> 686 + >(({ className, ...props }, ref) => ( 687 + <ul 688 + ref={ref} 689 + data-sidebar="menu-sub" 690 + className={cn( 691 + "mx-3.5 flex min-w-0 translate-x-px flex-col gap-1 border-l border-sidebar-border px-2.5 py-0.5", 692 + "group-data-[collapsible=icon]:hidden", 693 + className 694 + )} 695 + {...props} 696 + /> 697 + )) 698 + SidebarMenuSub.displayName = "SidebarMenuSub" 699 + 700 + const SidebarMenuSubItem = React.forwardRef< 701 + HTMLLIElement, 702 + React.ComponentProps<"li"> 703 + >(({ ...props }, ref) => <li ref={ref} {...props} />) 704 + SidebarMenuSubItem.displayName = "SidebarMenuSubItem" 705 + 706 + const SidebarMenuSubButton = React.forwardRef< 707 + HTMLAnchorElement, 708 + React.ComponentProps<"a"> & { 709 + asChild?: boolean 710 + size?: "sm" | "md" 711 + isActive?: boolean 712 + } 713 + >(({ asChild = false, size = "md", isActive, className, ...props }, ref) => { 714 + const Comp = asChild ? Slot : "a" 715 + 716 + return ( 717 + <Comp 718 + ref={ref} 719 + data-sidebar="menu-sub-button" 720 + data-size={size} 721 + data-active={isActive} 722 + className={cn( 723 + "flex h-7 min-w-0 -translate-x-px items-center gap-2 overflow-hidden rounded-md px-2 text-sidebar-foreground outline-hidden ring-sidebar-ring hover:bg-sidebar-accent hover:text-sidebar-accent-foreground focus-visible:ring-2 active:bg-sidebar-accent active:text-sidebar-accent-foreground disabled:pointer-events-none disabled:opacity-50 aria-disabled:pointer-events-none aria-disabled:opacity-50 [&>span:last-child]:truncate [&>svg]:size-4 [&>svg]:shrink-0 [&>svg]:text-sidebar-accent-foreground", 724 + "data-[active=true]:bg-sidebar-accent data-[active=true]:text-sidebar-accent-foreground", 725 + size === "sm" && "text-xs", 726 + size === "md" && "text-sm", 727 + "group-data-[collapsible=icon]:hidden", 728 + className 729 + )} 730 + {...props} 731 + /> 732 + ) 733 + }) 734 + SidebarMenuSubButton.displayName = "SidebarMenuSubButton" 735 + 736 + export { 737 + Sidebar, 738 + SidebarContent, 739 + SidebarFooter, 740 + SidebarGroup, 741 + SidebarGroupAction, 742 + SidebarGroupContent, 743 + SidebarGroupLabel, 744 + SidebarHeader, 745 + SidebarInput, 746 + SidebarInset, 747 + SidebarMenu, 748 + SidebarMenuAction, 749 + SidebarMenuBadge, 750 + SidebarMenuButton, 751 + SidebarMenuItem, 752 + SidebarMenuSkeleton, 753 + SidebarMenuSub, 754 + SidebarMenuSubButton, 755 + SidebarMenuSubItem, 756 + SidebarProvider, 757 + SidebarRail, 758 + SidebarSeparator, 759 + SidebarTrigger, 760 + useSidebar, 761 + }
+15
src/components/ui/skeleton.tsx
··· 1 + import { cn } from "@/lib/utils" 2 + 3 + function Skeleton({ 4 + className, 5 + ...props 6 + }: React.HTMLAttributes<HTMLDivElement>) { 7 + return ( 8 + <div 9 + className={cn("animate-pulse rounded-md bg-muted", className)} 10 + {...props} 11 + /> 12 + ) 13 + } 14 + 15 + export { Skeleton }
+26
src/components/ui/slider.tsx
··· 1 + import * as React from "react" 2 + import * as SliderPrimitive from "@radix-ui/react-slider" 3 + 4 + import { cn } from "@/lib/utils" 5 + 6 + const Slider = React.forwardRef< 7 + React.ElementRef<typeof SliderPrimitive.Root>, 8 + React.ComponentPropsWithoutRef<typeof SliderPrimitive.Root> 9 + >(({ className, ...props }, ref) => ( 10 + <SliderPrimitive.Root 11 + ref={ref} 12 + className={cn( 13 + "relative flex w-full touch-none select-none items-center", 14 + className 15 + )} 16 + {...props} 17 + > 18 + <SliderPrimitive.Track className="relative h-2 w-full grow overflow-hidden rounded-full bg-secondary"> 19 + <SliderPrimitive.Range className="absolute h-full bg-primary" /> 20 + </SliderPrimitive.Track> 21 + <SliderPrimitive.Thumb className="block h-5 w-5 rounded-full border-2 border-primary bg-secondary transition-[color,box-shadow] focus-visible:outline-hidden hover:ring-4 ring-primary/40 focus-visible:ring-4 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50" /> 22 + </SliderPrimitive.Root> 23 + )) 24 + Slider.displayName = SliderPrimitive.Root.displayName 25 + 26 + export { Slider }
+29
src/components/ui/sonner.tsx
··· 1 + import { useTheme } from "next-themes" 2 + import { Toaster as Sonner, toast } from "sonner" 3 + 4 + type ToasterProps = React.ComponentProps<typeof Sonner> 5 + 6 + const Toaster = ({ ...props }: ToasterProps) => { 7 + const { theme = "system" } = useTheme() 8 + 9 + return ( 10 + <Sonner 11 + theme={theme as ToasterProps["theme"]} 12 + className="toaster group" 13 + toastOptions={{ 14 + classNames: { 15 + toast: 16 + "group toast group-[.toaster]:bg-background group-[.toaster]:text-foreground group-[.toaster]:border-border group-[.toaster]:shadow-lg", 17 + description: "group-[.toast]:text-muted-foreground", 18 + actionButton: 19 + "group-[.toast]:bg-primary group-[.toast]:text-primary-foreground", 20 + cancelButton: 21 + "group-[.toast]:bg-muted group-[.toast]:text-muted-foreground", 22 + }, 23 + }} 24 + {...props} 25 + /> 26 + ) 27 + } 28 + 29 + export { Toaster, toast }
+27
src/components/ui/switch.tsx
··· 1 + import * as React from "react" 2 + import * as SwitchPrimitives from "@radix-ui/react-switch" 3 + 4 + import { cn } from "@/lib/utils" 5 + 6 + const Switch = React.forwardRef< 7 + React.ElementRef<typeof SwitchPrimitives.Root>, 8 + React.ComponentPropsWithoutRef<typeof SwitchPrimitives.Root> 9 + >(({ className, ...props }, ref) => ( 10 + <SwitchPrimitives.Root 11 + className={cn( 12 + "peer inline-flex h-6 w-11 shrink-0 cursor-pointer items-center rounded-full border-2 border-transparent transition-colors focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=unchecked]:bg-input", 13 + className 14 + )} 15 + {...props} 16 + ref={ref} 17 + > 18 + <SwitchPrimitives.Thumb 19 + className={cn( 20 + "pointer-events-none block h-5 w-5 rounded-full bg-background shadow-lg ring-0 transition-transform data-[state=checked]:translate-x-5 data-[state=unchecked]:translate-x-0" 21 + )} 22 + /> 23 + </SwitchPrimitives.Root> 24 + )) 25 + Switch.displayName = SwitchPrimitives.Root.displayName 26 + 27 + export { Switch }
+117
src/components/ui/table.tsx
··· 1 + import * as React from "react" 2 + 3 + import { cn } from "@/lib/utils" 4 + 5 + const Table = React.forwardRef< 6 + HTMLTableElement, 7 + React.HTMLAttributes<HTMLTableElement> 8 + >(({ className, ...props }, ref) => ( 9 + <div className="relative w-full overflow-auto"> 10 + <table 11 + ref={ref} 12 + className={cn("w-full caption-bottom text-sm", className)} 13 + {...props} 14 + /> 15 + </div> 16 + )) 17 + Table.displayName = "Table" 18 + 19 + const TableHeader = React.forwardRef< 20 + HTMLTableSectionElement, 21 + React.HTMLAttributes<HTMLTableSectionElement> 22 + >(({ className, ...props }, ref) => ( 23 + <thead ref={ref} className={cn("[&_tr]:border-b", className)} {...props} /> 24 + )) 25 + TableHeader.displayName = "TableHeader" 26 + 27 + const TableBody = React.forwardRef< 28 + HTMLTableSectionElement, 29 + React.HTMLAttributes<HTMLTableSectionElement> 30 + >(({ className, ...props }, ref) => ( 31 + <tbody 32 + ref={ref} 33 + className={cn("[&_tr:last-child]:border-0", className)} 34 + {...props} 35 + /> 36 + )) 37 + TableBody.displayName = "TableBody" 38 + 39 + const TableFooter = React.forwardRef< 40 + HTMLTableSectionElement, 41 + React.HTMLAttributes<HTMLTableSectionElement> 42 + >(({ className, ...props }, ref) => ( 43 + <tfoot 44 + ref={ref} 45 + className={cn( 46 + "border-t bg-muted/50 font-medium last:[&>tr]:border-b-0", 47 + className 48 + )} 49 + {...props} 50 + /> 51 + )) 52 + TableFooter.displayName = "TableFooter" 53 + 54 + const TableRow = React.forwardRef< 55 + HTMLTableRowElement, 56 + React.HTMLAttributes<HTMLTableRowElement> 57 + >(({ className, ...props }, ref) => ( 58 + <tr 59 + ref={ref} 60 + className={cn( 61 + "border-b transition-colors hover:bg-muted/50 data-[state=selected]:bg-muted", 62 + className 63 + )} 64 + {...props} 65 + /> 66 + )) 67 + TableRow.displayName = "TableRow" 68 + 69 + const TableHead = React.forwardRef< 70 + HTMLTableCellElement, 71 + React.ThHTMLAttributes<HTMLTableCellElement> 72 + >(({ className, ...props }, ref) => ( 73 + <th 74 + ref={ref} 75 + className={cn( 76 + "h-12 px-4 text-left align-middle font-medium text-muted-foreground [&:has([role=checkbox])]:pr-0", 77 + className 78 + )} 79 + {...props} 80 + /> 81 + )) 82 + TableHead.displayName = "TableHead" 83 + 84 + const TableCell = React.forwardRef< 85 + HTMLTableCellElement, 86 + React.TdHTMLAttributes<HTMLTableCellElement> 87 + >(({ className, ...props }, ref) => ( 88 + <td 89 + ref={ref} 90 + className={cn("p-4 align-middle [&:has([role=checkbox])]:pr-0", className)} 91 + {...props} 92 + /> 93 + )) 94 + TableCell.displayName = "TableCell" 95 + 96 + const TableCaption = React.forwardRef< 97 + HTMLTableCaptionElement, 98 + React.HTMLAttributes<HTMLTableCaptionElement> 99 + >(({ className, ...props }, ref) => ( 100 + <caption 101 + ref={ref} 102 + className={cn("mt-4 text-sm text-muted-foreground", className)} 103 + {...props} 104 + /> 105 + )) 106 + TableCaption.displayName = "TableCaption" 107 + 108 + export { 109 + Table, 110 + TableHeader, 111 + TableBody, 112 + TableFooter, 113 + TableHead, 114 + TableRow, 115 + TableCell, 116 + TableCaption, 117 + }
+53
src/components/ui/tabs.tsx
··· 1 + import * as React from "react" 2 + import * as TabsPrimitive from "@radix-ui/react-tabs" 3 + 4 + import { cn } from "@/lib/utils" 5 + 6 + const Tabs = TabsPrimitive.Root 7 + 8 + const TabsList = React.forwardRef< 9 + React.ElementRef<typeof TabsPrimitive.List>, 10 + React.ComponentPropsWithoutRef<typeof TabsPrimitive.List> 11 + >(({ className, ...props }, ref) => ( 12 + <TabsPrimitive.List 13 + ref={ref} 14 + className={cn( 15 + "inline-flex h-10 items-center justify-center rounded-md bg-muted p-1 text-muted-foreground", 16 + className 17 + )} 18 + {...props} 19 + /> 20 + )) 21 + TabsList.displayName = TabsPrimitive.List.displayName 22 + 23 + const TabsTrigger = React.forwardRef< 24 + React.ElementRef<typeof TabsPrimitive.Trigger>, 25 + React.ComponentPropsWithoutRef<typeof TabsPrimitive.Trigger> 26 + >(({ className, ...props }, ref) => ( 27 + <TabsPrimitive.Trigger 28 + ref={ref} 29 + className={cn( 30 + "inline-flex items-center justify-center whitespace-nowrap rounded-sm px-3 py-1.5 text-sm font-medium ring-offset-background transition-all focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 data-[state=active]:bg-background data-[state=active]:text-foreground data-[state=active]:shadow-xs", 31 + className 32 + )} 33 + {...props} 34 + /> 35 + )) 36 + TabsTrigger.displayName = TabsPrimitive.Trigger.displayName 37 + 38 + const TabsContent = React.forwardRef< 39 + React.ElementRef<typeof TabsPrimitive.Content>, 40 + React.ComponentPropsWithoutRef<typeof TabsPrimitive.Content> 41 + >(({ className, ...props }, ref) => ( 42 + <TabsPrimitive.Content 43 + ref={ref} 44 + className={cn( 45 + "mt-2 ring-offset-background focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2", 46 + className 47 + )} 48 + {...props} 49 + /> 50 + )) 51 + TabsContent.displayName = TabsPrimitive.Content.displayName 52 + 53 + export { Tabs, TabsList, TabsTrigger, TabsContent }
+24
src/components/ui/textarea.tsx
··· 1 + import * as React from "react" 2 + 3 + import { cn } from "@/lib/utils" 4 + 5 + export interface TextareaProps 6 + extends React.TextareaHTMLAttributes<HTMLTextAreaElement> {} 7 + 8 + const Textarea = React.forwardRef<HTMLTextAreaElement, TextareaProps>( 9 + ({ className, ...props }, ref) => { 10 + return ( 11 + <textarea 12 + className={cn( 13 + "flex min-h-[80px] w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50", 14 + className 15 + )} 16 + ref={ref} 17 + {...props} 18 + /> 19 + ) 20 + } 21 + ) 22 + Textarea.displayName = "Textarea" 23 + 24 + export { Textarea }
+127
src/components/ui/toast.tsx
··· 1 + import * as React from "react" 2 + import * as ToastPrimitives from "@radix-ui/react-toast" 3 + import { cva, type VariantProps } from "class-variance-authority" 4 + import { X } from "lucide-react" 5 + 6 + import { cn } from "@/lib/utils" 7 + 8 + const ToastProvider = ToastPrimitives.Provider 9 + 10 + const ToastViewport = React.forwardRef< 11 + React.ElementRef<typeof ToastPrimitives.Viewport>, 12 + React.ComponentPropsWithoutRef<typeof ToastPrimitives.Viewport> 13 + >(({ className, ...props }, ref) => ( 14 + <ToastPrimitives.Viewport 15 + ref={ref} 16 + className={cn( 17 + "fixed top-0 z-100 flex max-h-screen w-full flex-col-reverse p-4 sm:bottom-0 sm:right-0 sm:top-auto sm:flex-col md:max-w-[420px]", 18 + className 19 + )} 20 + {...props} 21 + /> 22 + )) 23 + ToastViewport.displayName = ToastPrimitives.Viewport.displayName 24 + 25 + const toastVariants = cva( 26 + "group pointer-events-auto relative flex w-full items-center justify-between space-x-4 overflow-hidden rounded-md border p-6 pr-8 shadow-lg transition-all data-[swipe=cancel]:translate-x-0 data-[swipe=end]:translate-x-(--radix-toast-swipe-end-x) data-[swipe=move]:translate-x-(--radix-toast-swipe-move-x) data-[swipe=move]:transition-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[swipe=end]:animate-out data-[state=closed]:fade-out-80 data-[state=closed]:slide-out-to-right-full data-[state=open]:slide-in-from-top-full sm:data-[state=open]:slide-in-from-bottom-full", 27 + { 28 + variants: { 29 + variant: { 30 + default: "border bg-background text-foreground", 31 + destructive: 32 + "destructive group border-destructive bg-destructive text-destructive-foreground", 33 + }, 34 + }, 35 + defaultVariants: { 36 + variant: "default", 37 + }, 38 + } 39 + ) 40 + 41 + const Toast = React.forwardRef< 42 + React.ElementRef<typeof ToastPrimitives.Root>, 43 + React.ComponentPropsWithoutRef<typeof ToastPrimitives.Root> & 44 + VariantProps<typeof toastVariants> 45 + >(({ className, variant, ...props }, ref) => { 46 + return ( 47 + <ToastPrimitives.Root 48 + ref={ref} 49 + className={cn(toastVariants({ variant }), className)} 50 + {...props} 51 + /> 52 + ) 53 + }) 54 + Toast.displayName = ToastPrimitives.Root.displayName 55 + 56 + const ToastAction = React.forwardRef< 57 + React.ElementRef<typeof ToastPrimitives.Action>, 58 + React.ComponentPropsWithoutRef<typeof ToastPrimitives.Action> 59 + >(({ className, ...props }, ref) => ( 60 + <ToastPrimitives.Action 61 + ref={ref} 62 + className={cn( 63 + "inline-flex h-8 shrink-0 items-center justify-center rounded-md border bg-transparent px-3 text-sm font-medium ring-offset-background transition-colors hover:bg-secondary focus:outline-hidden focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 group-[.destructive]:border-muted/40 hover:group-[.destructive]:border-destructive/30 hover:group-[.destructive]:bg-destructive hover:group-[.destructive]:text-destructive-foreground focus:group-[.destructive]:ring-destructive", 64 + className 65 + )} 66 + {...props} 67 + /> 68 + )) 69 + ToastAction.displayName = ToastPrimitives.Action.displayName 70 + 71 + const ToastClose = React.forwardRef< 72 + React.ElementRef<typeof ToastPrimitives.Close>, 73 + React.ComponentPropsWithoutRef<typeof ToastPrimitives.Close> 74 + >(({ className, ...props }, ref) => ( 75 + <ToastPrimitives.Close 76 + ref={ref} 77 + className={cn( 78 + "absolute right-2 top-2 rounded-md p-1 text-foreground/50 opacity-0 transition-opacity hover:text-foreground focus:opacity-100 focus:outline-hidden focus:ring-2 group-hover:opacity-100 group-[.destructive]:text-red-300 hover:group-[.destructive]:text-red-50 focus:group-[.destructive]:ring-red-400 focus:group-[.destructive]:ring-offset-red-600", 79 + className 80 + )} 81 + toast-close="" 82 + {...props} 83 + > 84 + <X className="h-4 w-4" /> 85 + </ToastPrimitives.Close> 86 + )) 87 + ToastClose.displayName = ToastPrimitives.Close.displayName 88 + 89 + const ToastTitle = React.forwardRef< 90 + React.ElementRef<typeof ToastPrimitives.Title>, 91 + React.ComponentPropsWithoutRef<typeof ToastPrimitives.Title> 92 + >(({ className, ...props }, ref) => ( 93 + <ToastPrimitives.Title 94 + ref={ref} 95 + className={cn("text-sm font-semibold", className)} 96 + {...props} 97 + /> 98 + )) 99 + ToastTitle.displayName = ToastPrimitives.Title.displayName 100 + 101 + const ToastDescription = React.forwardRef< 102 + React.ElementRef<typeof ToastPrimitives.Description>, 103 + React.ComponentPropsWithoutRef<typeof ToastPrimitives.Description> 104 + >(({ className, ...props }, ref) => ( 105 + <ToastPrimitives.Description 106 + ref={ref} 107 + className={cn("text-sm opacity-90", className)} 108 + {...props} 109 + /> 110 + )) 111 + ToastDescription.displayName = ToastPrimitives.Description.displayName 112 + 113 + type ToastProps = React.ComponentPropsWithoutRef<typeof Toast> 114 + 115 + type ToastActionElement = React.ReactElement<typeof ToastAction> 116 + 117 + export { 118 + type ToastProps, 119 + type ToastActionElement, 120 + ToastProvider, 121 + ToastViewport, 122 + Toast, 123 + ToastTitle, 124 + ToastDescription, 125 + ToastClose, 126 + ToastAction, 127 + }
+35
src/components/ui/toaster.tsx
··· 1 + import { useToast } from "@/hooks/use-toast" 2 + import { 3 + Toast, 4 + ToastClose, 5 + ToastDescription, 6 + ToastProvider, 7 + ToastTitle, 8 + ToastViewport, 9 + } from "@/components/ui/toast" 10 + import { useMobile } from "@/hooks/use-mobile" 11 + 12 + export function Toaster() { 13 + const { toasts } = useToast() 14 + const isMobile = useMobile() 15 + 16 + return ( 17 + <ToastProvider> 18 + {toasts.map(function ({ id, title, description, action, ...props }) { 19 + return ( 20 + <Toast key={id} {...props} > 21 + <div className="grid gap-1"> 22 + {title && <ToastTitle>{title}</ToastTitle>} 23 + {description && ( 24 + <ToastDescription>{description}</ToastDescription> 25 + )} 26 + </div> 27 + {action} 28 + <ToastClose /> 29 + </Toast> 30 + ) 31 + })} 32 + <ToastViewport /> 33 + </ToastProvider> 34 + ) 35 + }
+59
src/components/ui/toggle-group.tsx
··· 1 + import * as React from "react" 2 + import * as ToggleGroupPrimitive from "@radix-ui/react-toggle-group" 3 + import { type VariantProps } from "class-variance-authority" 4 + 5 + import { cn } from "@/lib/utils" 6 + import { toggleVariants } from "@/components/ui/toggle" 7 + 8 + const ToggleGroupContext = React.createContext< 9 + VariantProps<typeof toggleVariants> 10 + >({ 11 + size: "default", 12 + variant: "default", 13 + }) 14 + 15 + const ToggleGroup = React.forwardRef< 16 + React.ElementRef<typeof ToggleGroupPrimitive.Root>, 17 + React.ComponentPropsWithoutRef<typeof ToggleGroupPrimitive.Root> & 18 + VariantProps<typeof toggleVariants> 19 + >(({ className, variant, size, children, ...props }, ref) => ( 20 + <ToggleGroupPrimitive.Root 21 + ref={ref} 22 + className={cn("flex items-center justify-center gap-1", className)} 23 + {...props} 24 + > 25 + <ToggleGroupContext.Provider value={{ variant, size }}> 26 + {children} 27 + </ToggleGroupContext.Provider> 28 + </ToggleGroupPrimitive.Root> 29 + )) 30 + 31 + ToggleGroup.displayName = ToggleGroupPrimitive.Root.displayName 32 + 33 + const ToggleGroupItem = React.forwardRef< 34 + React.ElementRef<typeof ToggleGroupPrimitive.Item>, 35 + React.ComponentPropsWithoutRef<typeof ToggleGroupPrimitive.Item> & 36 + VariantProps<typeof toggleVariants> 37 + >(({ className, children, variant, size, ...props }, ref) => { 38 + const context = React.useContext(ToggleGroupContext) 39 + 40 + return ( 41 + <ToggleGroupPrimitive.Item 42 + ref={ref} 43 + className={cn( 44 + toggleVariants({ 45 + variant: context.variant || variant, 46 + size: context.size || size, 47 + }), 48 + className 49 + )} 50 + {...props} 51 + > 52 + {children} 53 + </ToggleGroupPrimitive.Item> 54 + ) 55 + }) 56 + 57 + ToggleGroupItem.displayName = ToggleGroupPrimitive.Item.displayName 58 + 59 + export { ToggleGroup, ToggleGroupItem }
+43
src/components/ui/toggle.tsx
··· 1 + import * as React from "react" 2 + import * as TogglePrimitive from "@radix-ui/react-toggle" 3 + import { cva, type VariantProps } from "class-variance-authority" 4 + 5 + import { cn } from "@/lib/utils" 6 + 7 + const toggleVariants = cva( 8 + "inline-flex items-center justify-center rounded-md text-sm font-medium ring-offset-background transition-colors hover:bg-muted hover:text-muted-foreground focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 data-[state=on]:bg-accent data-[state=on]:text-accent-foreground", 9 + { 10 + variants: { 11 + variant: { 12 + default: "bg-transparent", 13 + outline: 14 + "border border-input bg-transparent hover:bg-accent hover:text-accent-foreground", 15 + }, 16 + size: { 17 + default: "h-10 px-3", 18 + sm: "h-9 px-2.5", 19 + lg: "h-11 px-5", 20 + }, 21 + }, 22 + defaultVariants: { 23 + variant: "default", 24 + size: "default", 25 + }, 26 + } 27 + ) 28 + 29 + const Toggle = React.forwardRef< 30 + React.ElementRef<typeof TogglePrimitive.Root>, 31 + React.ComponentPropsWithoutRef<typeof TogglePrimitive.Root> & 32 + VariantProps<typeof toggleVariants> 33 + >(({ className, variant, size, ...props }, ref) => ( 34 + <TogglePrimitive.Root 35 + ref={ref} 36 + className={cn(toggleVariants({ variant, size, className }))} 37 + {...props} 38 + /> 39 + )) 40 + 41 + Toggle.displayName = TogglePrimitive.Root.displayName 42 + 43 + export { Toggle, toggleVariants }
+28
src/components/ui/tooltip.tsx
··· 1 + import * as React from "react" 2 + import * as TooltipPrimitive from "@radix-ui/react-tooltip" 3 + 4 + import { cn } from "@/lib/utils" 5 + 6 + const TooltipProvider = TooltipPrimitive.Provider 7 + 8 + const Tooltip = TooltipPrimitive.Root 9 + 10 + const TooltipTrigger = TooltipPrimitive.Trigger 11 + 12 + const TooltipContent = React.forwardRef< 13 + React.ElementRef<typeof TooltipPrimitive.Content>, 14 + React.ComponentPropsWithoutRef<typeof TooltipPrimitive.Content> 15 + >(({ className, sideOffset = 4, ...props }, ref) => ( 16 + <TooltipPrimitive.Content 17 + ref={ref} 18 + sideOffset={sideOffset} 19 + className={cn( 20 + "z-50 overflow-hidden rounded-md border bg-popover px-3 py-1.5 text-sm text-popover-foreground shadow-md animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2", 21 + className 22 + )} 23 + {...props} 24 + /> 25 + )) 26 + TooltipContent.displayName = TooltipPrimitive.Content.displayName 27 + 28 + export { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider }
+3
src/components/ui/use-toast.ts
··· 1 + import { useToast, toast } from "@/hooks/use-toast"; 2 + 3 + export { useToast, toast };
+177
src/contexts/MockupContext.tsx
··· 1 + import { create } from 'zustand'; 2 + import { persist, createJSONStorage, StateStorage } from 'zustand/middleware'; 3 + import { get, set, del } from 'idb-keyval'; 4 + 5 + interface DevicePosition { 6 + x: number; 7 + y: number; 8 + scale: number; 9 + rotation: number; 10 + } 11 + 12 + interface Rotation3D { 13 + rotateX: number; 14 + rotateY: number; 15 + rotateZ: number; 16 + skew: number; 17 + } 18 + 19 + interface ImageBorder { 20 + width: number; 21 + color: string; 22 + radius: number; 23 + shadow: string; 24 + enabled: boolean; 25 + } 26 + 27 + interface CustomBackground { 28 + name: string; 29 + image: string; // Base64 string 30 + } 31 + 32 + interface MockupState { 33 + uploadedImage: string | null; 34 + backgroundColor: string; 35 + backgroundType: 'solid' | 'gradient' | 'pattern'; 36 + gradientColors: string[]; 37 + gradientDirection: string; 38 + devicePosition: DevicePosition; 39 + rotation3D: Rotation3D; 40 + imageBorder: ImageBorder; 41 + aspectRatio: string; 42 + backgroundImage: string | null; 43 + fixedMargin: boolean; 44 + // marginSize: number; 45 + margin: { top: number; right: number; bottom: number; left: number }; 46 + customBackgrounds: CustomBackground[]; 47 + } 48 + 49 + interface MockupStore extends MockupState { 50 + setUploadedImage: (image: string | null) => void; 51 + setBackgroundColor: (color: string) => void; 52 + setBackgroundType: (type: 'solid' | 'gradient' | 'pattern') => void; 53 + setGradientColors: (colors: string[]) => void; 54 + setGradientDirection: (direction: string) => void; 55 + updateDevicePosition: (position: Partial<DevicePosition>) => void; 56 + setUnsplashImage: (image: string | null) => void; 57 + set3DRotation: (rotation: Partial<Rotation3D>) => void; 58 + setImageBorder: (border: Partial<ImageBorder>) => void; 59 + setAspectRatio: (ratio: string) => void; 60 + setBackgroundImage: (image: string | null) => void; 61 + setFixedMargin: (enabled: boolean) => void; 62 + // setMarginSize: (size: number) => void; 63 + setMargin: (margin: Partial<{ top: number; right: number; bottom: number; left: number }>) => void; 64 + setCustomBackgrounds: (backgrounds: CustomBackground[]) => void; 65 + addCustomBackground: (background: CustomBackground) => void; 66 + } 67 + 68 + // Custom IndexedDB storage for Zustand 69 + const indexedDBStorage: StateStorage = { 70 + getItem: async (name: string): Promise<string | null> => { 71 + try { 72 + if (typeof indexedDB === 'undefined') { 73 + console.warn('IndexedDB not available, skipping getItem'); 74 + return null; 75 + } 76 + const value = await get(name); 77 + return value || null; // Return raw value (string) or null 78 + } catch (error) { 79 + console.error('IndexedDB getItem error:', error); 80 + return null; 81 + } 82 + }, 83 + setItem: async (name: string, value: string): Promise<void> => { 84 + try { 85 + if (typeof indexedDB === 'undefined') { 86 + console.warn('IndexedDB not available, skipping setItem'); 87 + return; 88 + } 89 + await set(name, value); // Store raw string value 90 + } catch (error) { 91 + console.error('IndexedDB setItem error:', error); 92 + throw error; // Let Zustand handle the error 93 + } 94 + }, 95 + removeItem: async (name: string): Promise<void> => { 96 + try { 97 + if (typeof indexedDB === 'undefined') { 98 + console.warn('IndexedDB not available, skipping removeItem'); 99 + return; 100 + } 101 + await del(name); 102 + } catch (error) { 103 + console.error('IndexedDB removeItem error:', error); 104 + throw error; 105 + } 106 + }, 107 + }; 108 + 109 + export const useMockupStore = create<MockupStore>()( 110 + persist( 111 + (set) => ({ 112 + uploadedImage: null, 113 + backgroundColor: '#FF6B6B', 114 + backgroundType: 'pattern', 115 + backgroundImage: '/assets/deep_horizon.webp', 116 + gradientDirection: 'to-br', 117 + gradientColors: ['#667eea', '#764ba2'], 118 + devicePosition: { x: 0, y: 0, scale: 1, rotation: 0 }, 119 + aspectRatio: '16:9', 120 + rotation3D: { rotateX: 0, rotateY: 0, rotateZ: 0, skew: 0 }, 121 + imageBorder: { 122 + width: 8, 123 + color: '#FF6B6B', 124 + radius: 12, 125 + shadow: 'rgba(0, 0, 0, 0.16) 0px 3px 6px, rgba(0, 0, 0, 0.23) 0px 3px 6px', 126 + enabled: true, 127 + }, 128 + fixedMargin: false, 129 + // marginSize: 35, 130 + margin: { top: 35, right: 35, bottom: 35, left: 35 }, 131 + customBackgrounds: [], 132 + 133 + setUploadedImage: (image) => set({ uploadedImage: image }), 134 + setBackgroundColor: (color) => set({ backgroundColor: color }), 135 + setBackgroundType: (type) => set({ backgroundType: type }), 136 + setGradientColors: (colors) => set({ gradientColors: colors }), 137 + setGradientDirection: (direction) => set({ gradientDirection: direction }), 138 + updateDevicePosition: (position) => 139 + set((state) => ({ 140 + devicePosition: { ...state.devicePosition, ...position }, 141 + })), 142 + setUnsplashImage: (image) => set({ uploadedImage: image }), 143 + set3DRotation: (rotation) => 144 + set((state) => ({ 145 + rotation3D: { ...state.rotation3D, ...rotation }, 146 + })), 147 + setImageBorder: (border) => 148 + set((state) => ({ 149 + imageBorder: { ...state.imageBorder, ...border }, 150 + })), 151 + setAspectRatio: (ratio) => set({ aspectRatio: ratio }), 152 + setBackgroundImage: (image) => set({ backgroundImage: image }), 153 + setFixedMargin: (enabled) => set({ fixedMargin: enabled }), 154 + // setMarginSize: (size) => set({ marginSize: size }), 155 + setMargin: (margin) => 156 + set((state) => ({ 157 + margin: { ...state.margin, ...margin }, 158 + })), 159 + setCustomBackgrounds: (backgrounds) => set({ customBackgrounds: backgrounds }), 160 + addCustomBackground: (background) => 161 + set((state) => ({ 162 + customBackgrounds: [...state.customBackgrounds, background], 163 + })), 164 + }), 165 + { 166 + name: 'mockup-store', 167 + storage: createJSONStorage(() => indexedDBStorage), 168 + onRehydrateStorage: () => (state, error) => { 169 + if (error) { 170 + console.error('Zustand hydration error:', error); 171 + } else { 172 + console.log('Zustand store hydrated successfully'); 173 + } 174 + }, 175 + } 176 + ) 177 + );
+22
src/hooks/use-mobile.tsx
··· 1 + import { useEffect, useState } from 'react'; 2 + 3 + export const useMobile = () => { 4 + const [isMobile, setIsMobile] = useState(false); 5 + 6 + useEffect(() => { 7 + const checkMobile = () => { 8 + setIsMobile(window.innerWidth < 768); 9 + }; 10 + 11 + // Initial check 12 + checkMobile(); 13 + 14 + // Add event listener 15 + window.addEventListener('resize', checkMobile); 16 + 17 + // Cleanup 18 + return () => window.removeEventListener('resize', checkMobile); 19 + }, []); 20 + 21 + return isMobile; 22 + };
+191
src/hooks/use-toast.ts
··· 1 + import * as React from "react" 2 + 3 + import type { 4 + ToastActionElement, 5 + ToastProps, 6 + } from "@/components/ui/toast" 7 + 8 + const TOAST_LIMIT = 1 9 + const TOAST_REMOVE_DELAY = 1000000 10 + 11 + type ToasterToast = ToastProps & { 12 + id: string 13 + title?: React.ReactNode 14 + description?: React.ReactNode 15 + action?: ToastActionElement 16 + } 17 + 18 + const actionTypes = { 19 + ADD_TOAST: "ADD_TOAST", 20 + UPDATE_TOAST: "UPDATE_TOAST", 21 + DISMISS_TOAST: "DISMISS_TOAST", 22 + REMOVE_TOAST: "REMOVE_TOAST", 23 + } as const 24 + 25 + let count = 0 26 + 27 + function genId() { 28 + count = (count + 1) % Number.MAX_SAFE_INTEGER 29 + return count.toString() 30 + } 31 + 32 + type ActionType = typeof actionTypes 33 + 34 + type Action = 35 + | { 36 + type: ActionType["ADD_TOAST"] 37 + toast: ToasterToast 38 + } 39 + | { 40 + type: ActionType["UPDATE_TOAST"] 41 + toast: Partial<ToasterToast> 42 + } 43 + | { 44 + type: ActionType["DISMISS_TOAST"] 45 + toastId?: ToasterToast["id"] 46 + } 47 + | { 48 + type: ActionType["REMOVE_TOAST"] 49 + toastId?: ToasterToast["id"] 50 + } 51 + 52 + interface State { 53 + toasts: ToasterToast[] 54 + } 55 + 56 + const toastTimeouts = new Map<string, ReturnType<typeof setTimeout>>() 57 + 58 + const addToRemoveQueue = (toastId: string) => { 59 + if (toastTimeouts.has(toastId)) { 60 + return 61 + } 62 + 63 + const timeout = setTimeout(() => { 64 + toastTimeouts.delete(toastId) 65 + dispatch({ 66 + type: "REMOVE_TOAST", 67 + toastId: toastId, 68 + }) 69 + }, TOAST_REMOVE_DELAY) 70 + 71 + toastTimeouts.set(toastId, timeout) 72 + } 73 + 74 + export const reducer = (state: State, action: Action): State => { 75 + switch (action.type) { 76 + case "ADD_TOAST": 77 + return { 78 + ...state, 79 + toasts: [action.toast, ...state.toasts].slice(0, TOAST_LIMIT), 80 + } 81 + 82 + case "UPDATE_TOAST": 83 + return { 84 + ...state, 85 + toasts: state.toasts.map((t) => 86 + t.id === action.toast.id ? { ...t, ...action.toast } : t 87 + ), 88 + } 89 + 90 + case "DISMISS_TOAST": { 91 + const { toastId } = action 92 + 93 + // ! Side effects ! - This could be extracted into a dismissToast() action, 94 + // but I'll keep it here for simplicity 95 + if (toastId) { 96 + addToRemoveQueue(toastId) 97 + } else { 98 + state.toasts.forEach((toast) => { 99 + addToRemoveQueue(toast.id) 100 + }) 101 + } 102 + 103 + return { 104 + ...state, 105 + toasts: state.toasts.map((t) => 106 + t.id === toastId || toastId === undefined 107 + ? { 108 + ...t, 109 + open: false, 110 + } 111 + : t 112 + ), 113 + } 114 + } 115 + case "REMOVE_TOAST": 116 + if (action.toastId === undefined) { 117 + return { 118 + ...state, 119 + toasts: [], 120 + } 121 + } 122 + return { 123 + ...state, 124 + toasts: state.toasts.filter((t) => t.id !== action.toastId), 125 + } 126 + } 127 + } 128 + 129 + const listeners: Array<(state: State) => void> = [] 130 + 131 + let memoryState: State = { toasts: [] } 132 + 133 + function dispatch(action: Action) { 134 + memoryState = reducer(memoryState, action) 135 + listeners.forEach((listener) => { 136 + listener(memoryState) 137 + }) 138 + } 139 + 140 + type Toast = Omit<ToasterToast, "id"> 141 + 142 + function toast({ ...props }: Toast) { 143 + const id = genId() 144 + 145 + const update = (props: ToasterToast) => 146 + dispatch({ 147 + type: "UPDATE_TOAST", 148 + toast: { ...props, id }, 149 + }) 150 + const dismiss = () => dispatch({ type: "DISMISS_TOAST", toastId: id }) 151 + 152 + dispatch({ 153 + type: "ADD_TOAST", 154 + toast: { 155 + ...props, 156 + id, 157 + open: true, 158 + onOpenChange: (open) => { 159 + if (!open) dismiss() 160 + }, 161 + }, 162 + }) 163 + 164 + return { 165 + id: id, 166 + dismiss, 167 + update, 168 + } 169 + } 170 + 171 + function useToast() { 172 + const [state, setState] = React.useState<State>(memoryState) 173 + 174 + React.useEffect(() => { 175 + listeners.push(setState) 176 + return () => { 177 + const index = listeners.indexOf(setState) 178 + if (index > -1) { 179 + listeners.splice(index, 1) 180 + } 181 + } 182 + }, [state]) 183 + 184 + return { 185 + ...state, 186 + toast, 187 + dismiss: (toastId?: string) => dispatch({ type: "DISMISS_TOAST", toastId }), 188 + } 189 + } 190 + 191 + export { useToast, toast }
+174
src/index.css
··· 1 + @import 'tailwindcss'; 2 + 3 + @plugin 'tailwindcss-animate'; 4 + 5 + @custom-variant dark (&:is(.dark *)); 6 + 7 + @theme { 8 + --font-inter: Inter, sans-serif; 9 + 10 + --color-border: hsl(var(--border)); 11 + --color-input: hsl(var(--input)); 12 + --color-ring: hsl(var(--ring)); 13 + --color-background: hsl(var(--background)); 14 + --color-foreground: hsl(var(--foreground)); 15 + 16 + --color-primary: hsl(var(--primary)); 17 + --color-primary-foreground: hsl(var(--primary-foreground)); 18 + 19 + --color-secondary: hsl(var(--secondary)); 20 + --color-secondary-foreground: hsl(var(--secondary-foreground)); 21 + 22 + --color-destructive: hsl(var(--destructive)); 23 + --color-destructive-foreground: hsl(var(--destructive-foreground)); 24 + 25 + --color-muted: hsl(var(--muted)); 26 + --color-muted-foreground: hsl(var(--muted-foreground)); 27 + 28 + --color-accent: hsl(var(--accent)); 29 + --color-accent-foreground: hsl(var(--accent-foreground)); 30 + 31 + --color-popover: hsl(var(--popover)); 32 + --color-popover-foreground: hsl(var(--popover-foreground)); 33 + 34 + --color-card: hsl(var(--card)); 35 + --color-card-foreground: hsl(var(--card-foreground)); 36 + 37 + --color-sidebar: hsl(var(--sidebar-background)); 38 + --color-sidebar-foreground: hsl(var(--sidebar-foreground)); 39 + --color-sidebar-primary: hsl(var(--sidebar-primary)); 40 + --color-sidebar-primary-foreground: hsl(var(--sidebar-primary-foreground)); 41 + --color-sidebar-accent: hsl(var(--sidebar-accent)); 42 + --color-sidebar-accent-foreground: hsl(var(--sidebar-accent-foreground)); 43 + --color-sidebar-border: hsl(var(--sidebar-border)); 44 + --color-sidebar-ring: hsl(var(--sidebar-ring)); 45 + 46 + --color-gray-50: oklch(98.5% 0 0); 47 + --color-gray-100: oklch(96.7% 0.001 286.375); 48 + --color-gray-200: oklch(92% 0.004 286.32); 49 + --color-gray-300: oklch(87.1% 0.006 286.286); 50 + --color-gray-400: oklch(70.5% 0.015 286.067); 51 + --color-gray-500: oklch(55.2% 0.016 285.938); 52 + --color-gray-600: oklch(44.2% 0.017 285.786); 53 + --color-gray-700: oklch(37% 0.013 285.805); 54 + --color-gray-800: oklch(27.4% 0.006 286.033); 55 + --color-gray-900: oklch(21% 0.006 285.885); 56 + --color-gray-950: oklch(14.1% 0.005 285.823); 57 + 58 + 59 + --radius-lg: var(--radius); 60 + --radius-md: calc(var(--radius) - 2px); 61 + --radius-sm: calc(var(--radius) - 4px); 62 + 63 + --animate-accordion-down: accordion-down 0.2s ease-out; 64 + --animate-accordion-up: accordion-up 0.2s ease-out; 65 + 66 + @keyframes accordion-down { 67 + from { 68 + height: 0; 69 + } 70 + to { 71 + height: var(--radix-accordion-content-height); 72 + } 73 + } 74 + @keyframes accordion-up { 75 + from { 76 + height: var(--radix-accordion-content-height); 77 + } 78 + to { 79 + height: 0; 80 + } 81 + } 82 + } 83 + 84 + @utility container { 85 + margin-inline: auto; 86 + padding-inline: 2rem; 87 + @media (width >= --theme(--breakpoint-sm)) { 88 + max-width: none; 89 + } 90 + @media (width >= 1400px) { 91 + max-width: 1400px; 92 + } 93 + } 94 + 95 + /* 96 + The default border color has changed to `currentcolor` in Tailwind CSS v4, 97 + so we've added these compatibility styles to make sure everything still 98 + looks the same as it did with Tailwind CSS v3. 99 + 100 + If we ever want to remove these styles, we need to add an explicit border 101 + color utility to any element that depends on these defaults. 102 + */ 103 + @layer base { 104 + *, 105 + ::after, 106 + ::before, 107 + ::backdrop, 108 + ::file-selector-button { 109 + border-color: var(--color-gray-200, currentcolor); 110 + } 111 + } 112 + 113 + @layer base { 114 + :root { 115 + --background: 111 7% 6%; 116 + --foreground: 60 9% 98%; 117 + 118 + --card: 111 7% 6%; 119 + --card-foreground: 60 9% 98%; 120 + 121 + --popover: 111 7% 6%; 122 + --popover-foreground: 60 9% 98%; 123 + 124 + --primary: 110 43% 69%; 125 + --primary-foreground: 111 7% 6%; 126 + 127 + --secondary: 111 13% 15%; 128 + --secondary-foreground: 60 9% 98%; 129 + 130 + --muted: 111 13% 15%; 131 + --muted-foreground: 60 5% 64%; 132 + 133 + --accent: 111 13% 15%; 134 + --accent-foreground: 60 9% 98%; 135 + 136 + --destructive: 0 84% 37%; 137 + --destructive-foreground: 60 9% 98%; 138 + 139 + --border: 111 13% 15%; 140 + --input: 111 13% 15%; 141 + --ring: 110 43% 69%; /* Olive ring with better contrast */ 142 + 143 + --radius: 0.5rem; 144 + 145 + /* Olive sidebar theme with better contrast */ 146 + --sidebar-background: 111 13% 10%; 147 + --sidebar-foreground: 60 9% 95%; 148 + --sidebar-primary: 110 43% 69%; /* Better contrast olive */ 149 + --sidebar-primary-foreground: 111 7% 6%; 150 + --sidebar-accent: 111 13% 18%; 151 + --sidebar-accent-foreground: 60 9% 95%; 152 + --sidebar-border: 111 13% 18%; 153 + --sidebar-ring: 110 43% 69%; /* Better contrast olive */ 154 + } 155 + } 156 + 157 + @layer base { 158 + * { 159 + @apply border-border; 160 + } 161 + 162 + body { 163 + @apply bg-background text-foreground; 164 + font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; 165 + } 166 + 167 + /* Canvas gradient with olive accent */ 168 + .canvas-gradient { 169 + background: linear-gradient(135deg, 170 + hsl(111, 13%, 10%) 0%, 171 + hsl(111, 13%, 12%) 50%, 172 + hsl(111, 13%, 10%) 100%); 173 + } 174 + }
+6
src/lib/utils.ts
··· 1 + import { clsx, type ClassValue } from "clsx" 2 + import { twMerge } from "tailwind-merge" 3 + 4 + export function cn(...inputs: ClassValue[]) { 5 + return twMerge(clsx(inputs)) 6 + }
+5
src/main.tsx
··· 1 + import { createRoot } from 'react-dom/client' 2 + import App from './App.tsx' 3 + import './index.css' 4 + 5 + createRoot(document.getElementById("root")!).render(<App />);
+8
src/pages/Index.tsx
··· 1 + 2 + import { Editor } from '@/components/editor/Editor'; 3 + 4 + const Index = () => { 5 + return <Editor />; 6 + }; 7 + 8 + export default Index;
+70
src/types/canvas.ts
··· 1 + 2 + export interface MockupState { 3 + uploadedImage: string | null; 4 + selectedFrame: string; 5 + backgroundColor: string; 6 + backgroundType: 'solid' | 'gradient' | 'pattern'; 7 + backgroundImage?: string; 8 + gradientDirection: string; 9 + gradientColors: string[]; 10 + pattern: string; 11 + imagePosition: { 12 + x: number; 13 + y: number; 14 + scale: number; 15 + }; 16 + devicePosition: { 17 + x: number; 18 + y: number; 19 + scale: number; 20 + rotation: number; 21 + }; 22 + aspectRatio: string; 23 + rotation3D: { 24 + rotateX: number; 25 + rotateY: number; 26 + rotateZ: number; 27 + skew: number; 28 + }; 29 + imageBorder: { 30 + width: number; 31 + color: string; 32 + radius: number; 33 + shadow: string; 34 + enabled: boolean; 35 + }; 36 + } 37 + 38 + export interface DeviceFrame { 39 + id: string; 40 + name: string; 41 + type: 'phone' | 'laptop' | 'tablet' | 'desktop'; 42 + colors: string[]; 43 + dimensions: { 44 + width: number; 45 + height: number; 46 + screenWidth: number; 47 + screenHeight: number; 48 + screenX: number; 49 + screenY: number; 50 + }; 51 + recommendedResolution?: string; 52 + } 53 + 54 + // Additional types for CanvasContext compatibility 55 + export interface CanvasObject { 56 + id: string; 57 + type: string; 58 + position: { x: number; y: number }; 59 + size: { width: number; height: number }; 60 + } 61 + 62 + export interface CanvasState { 63 + objects: CanvasObject[]; 64 + selectedObjects: string[]; 65 + history: any[]; 66 + historyIndex: number; 67 + canvasSize: { width: number; height: number }; 68 + backgroundColor: string; 69 + deviceFrame: string; 70 + }
+59
src/utils/colorExtractor.ts
··· 1 + 2 + export const extractDominantColor = (imageUrl: string): Promise<string> => { 3 + return new Promise((resolve) => { 4 + const canvas = document.createElement('canvas'); 5 + const ctx = canvas.getContext('2d'); 6 + const img = new Image(); 7 + 8 + img.crossOrigin = 'anonymous'; 9 + img.onload = () => { 10 + canvas.width = img.width; 11 + canvas.height = img.height; 12 + ctx?.drawImage(img, 0, 0); 13 + 14 + const imageData = ctx?.getImageData(0, 0, canvas.width, canvas.height); 15 + if (!imageData) { 16 + resolve('#9CA389'); // fallback color 17 + return; 18 + } 19 + 20 + const colorMap = new Map<string, number>(); 21 + const data = imageData.data; 22 + 23 + // Sample every 10th pixel for performance 24 + for (let i = 0; i < data.length; i += 40) { 25 + const r = data[i]; 26 + const g = data[i + 1]; 27 + const b = data[i + 2]; 28 + const alpha = data[i + 3]; 29 + 30 + // Skip transparent pixels 31 + if (alpha < 128) continue; 32 + 33 + // Convert to hex 34 + const hex = `#${((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1)}`; 35 + 36 + colorMap.set(hex, (colorMap.get(hex) || 0) + 1); 37 + } 38 + 39 + // Find the most frequent color 40 + let dominantColor = '#9CA389'; 41 + let maxCount = 0; 42 + 43 + for (const [color, count] of colorMap.entries()) { 44 + if (count > maxCount) { 45 + maxCount = count; 46 + dominantColor = color; 47 + } 48 + } 49 + 50 + resolve(dominantColor); 51 + }; 52 + 53 + img.onerror = () => { 54 + resolve('#9CA389'); // fallback color 55 + }; 56 + 57 + img.src = imageUrl; 58 + }); 59 + };
+1
src/vite-env.d.ts
··· 1 + /// <reference types="vite/client" />
+30
tsconfig.app.json
··· 1 + { 2 + "compilerOptions": { 3 + "target": "ES2020", 4 + "useDefineForClassFields": true, 5 + "lib": ["ES2020", "DOM", "DOM.Iterable"], 6 + "module": "ESNext", 7 + "skipLibCheck": true, 8 + 9 + /* Bundler mode */ 10 + "moduleResolution": "bundler", 11 + "allowImportingTsExtensions": true, 12 + "isolatedModules": true, 13 + "moduleDetection": "force", 14 + "noEmit": true, 15 + "jsx": "react-jsx", 16 + 17 + /* Linting */ 18 + "strict": false, 19 + "noUnusedLocals": false, 20 + "noUnusedParameters": false, 21 + "noImplicitAny": false, 22 + "noFallthroughCasesInSwitch": false, 23 + 24 + "baseUrl": ".", 25 + "paths": { 26 + "@/*": ["./src/*"] 27 + } 28 + }, 29 + "include": ["src"] 30 + }
+19
tsconfig.json
··· 1 + { 2 + "files": [], 3 + "references": [ 4 + { "path": "./tsconfig.app.json" }, 5 + { "path": "./tsconfig.node.json" } 6 + ], 7 + "compilerOptions": { 8 + "baseUrl": ".", 9 + "paths": { 10 + "@/*": ["./src/*"] 11 + }, 12 + "noImplicitAny": false, 13 + "noUnusedParameters": false, 14 + "skipLibCheck": true, 15 + "allowJs": true, 16 + "noUnusedLocals": false, 17 + "strictNullChecks": false 18 + } 19 + }
+22
tsconfig.node.json
··· 1 + { 2 + "compilerOptions": { 3 + "target": "ES2022", 4 + "lib": ["ES2023"], 5 + "module": "ESNext", 6 + "skipLibCheck": true, 7 + 8 + /* Bundler mode */ 9 + "moduleResolution": "bundler", 10 + "allowImportingTsExtensions": true, 11 + "isolatedModules": true, 12 + "moduleDetection": "force", 13 + "noEmit": true, 14 + 15 + /* Linting */ 16 + "strict": true, 17 + "noUnusedLocals": false, 18 + "noUnusedParameters": false, 19 + "noFallthroughCasesInSwitch": true 20 + }, 21 + "include": ["vite.config.ts"] 22 + }
+14
vite.config.ts
··· 1 + import { defineConfig } from "vite"; 2 + import react from "@vitejs/plugin-react-swc"; 3 + import path from "path"; 4 + import tailwindcss from "@tailwindcss/vite"; 5 + 6 + // https://vitejs.dev/config/ 7 + export default defineConfig(() => ({ 8 + plugins: [react(), tailwindcss()], 9 + resolve: { 10 + alias: { 11 + "@": path.resolve(__dirname, "./src"), 12 + }, 13 + }, 14 + }));