metabinaries 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,279 @@
1
+ // Layout & Shared Components Templates
2
+ export const layoutTemplates = {
3
+ 'components/footer.tsx': `export default function Footer() {
4
+ return <footer className="py-8 text-center text-gray-400">Β© 2025 MetaBinaries</footer>;
5
+ }`,
6
+
7
+ 'components/layout/LanguageSwitcher.tsx': `'use client';
8
+
9
+ import { Globe } from 'lucide-react';
10
+ import { Button } from '@/components/ui/button';
11
+ import {
12
+ DropdownMenu,
13
+ DropdownMenuContent,
14
+ DropdownMenuItem,
15
+ DropdownMenuTrigger,
16
+ } from '@/components/ui/dropdown-menu';
17
+ import { useParams } from 'next/navigation';
18
+ import { useRouter, usePathname } from '@/i18n/navigation';
19
+ import { cn } from '@/lib/utils';
20
+
21
+ export function LanguageSwitcher() {
22
+ const params = useParams();
23
+ const router = useRouter();
24
+ const pathname = usePathname();
25
+ const locale = params?.locale as string;
26
+ const currentLocale = (locale as 'en' | 'ar') || 'en';
27
+
28
+ const handleLocaleChange = (newLocale: 'en' | 'ar') => {
29
+ if (newLocale === currentLocale) return;
30
+
31
+ // Base path removed
32
+ const BASE_PATH = '';
33
+
34
+ // Get current pathname from browser
35
+ const currentPath = window.location.pathname;
36
+
37
+ // Remove base path if present (e.g., "/workspace/en/admin" -> "/en/admin")
38
+ const pathWithoutBase = currentPath.startsWith(BASE_PATH)
39
+ ? currentPath.slice(BASE_PATH.length)
40
+ : currentPath;
41
+
42
+ // Split by current locale to get the path without locale
43
+ // e.g., "/en/admin/users" -> ["", "admin/users"]
44
+ const pathParts = pathWithoutBase.split(\`/\${currentLocale}\`);
45
+ const pathWithoutLocale =
46
+ pathParts.length > 1
47
+ ? pathParts[1] || '/'
48
+ : pathWithoutBase.startsWith('/')
49
+ ? pathWithoutBase.slice(1)
50
+ : pathWithoutBase;
51
+
52
+ // Ensure the path starts with / and normalize it
53
+ const normalizedPath = pathWithoutLocale.startsWith('/')
54
+ ? pathWithoutLocale
55
+ : \`/\${pathWithoutLocale}\`;
56
+
57
+ // Construct the new path with base path and new locale
58
+ const newPath =
59
+ normalizedPath === '/'
60
+ ? \`\${BASE_PATH}/\${newLocale}\`
61
+ : \`\${BASE_PATH}/\${newLocale}\${normalizedPath}\`;
62
+
63
+ // Preserve query parameters and hash if any
64
+ const searchParams = window.location.search;
65
+ const hash = window.location.hash;
66
+ const newUrl = \`\${newPath}\${searchParams}\${hash}\`;
67
+
68
+ // Navigate to the new locale path using window.location for reliability
69
+ window.location.href = newUrl;
70
+ };
71
+
72
+ return (
73
+ <DropdownMenu>
74
+ <DropdownMenuTrigger asChild>
75
+ <Button
76
+ variant='ghost'
77
+ size='sm'
78
+ className='text-setup-text hover:text-setup-text hover:bg-setup-primary/20'
79
+ title='Change Language'
80
+ >
81
+ <Globe className='h-4 w-4' />
82
+ <span className='ml-2 text-xs uppercase'>{currentLocale}</span>
83
+ <span className='sr-only'>Change Language</span>
84
+ </Button>
85
+ </DropdownMenuTrigger>
86
+ <DropdownMenuContent
87
+ align='end'
88
+ className='bg-setup-primary border-setup-secondary/30'
89
+ >
90
+ <DropdownMenuItem
91
+ onClick={() => handleLocaleChange('en')}
92
+ className={cn(
93
+ 'text-setup-text hover:text-setup-text hover:bg-setup-secondary/20',
94
+ currentLocale === 'en' && 'bg-setup-secondary/10'
95
+ )}
96
+ >
97
+ <span className='mr-2'>πŸ‡¬πŸ‡§</span>
98
+ English
99
+ {currentLocale === 'en' && (
100
+ <span className='ml-auto text-setup-secondary'>βœ“</span>
101
+ )}
102
+ </DropdownMenuItem>
103
+ <DropdownMenuItem
104
+ onClick={() => handleLocaleChange('ar')}
105
+ className={cn(
106
+ 'text-setup-text hover:text-setup-text hover:bg-setup-secondary/20',
107
+ currentLocale === 'ar' && 'bg-setup-secondary/10'
108
+ )}
109
+ >
110
+ <span className='mr-2'>πŸ‡ΈπŸ‡¦</span>
111
+ Ψ§Ω„ΨΉΨ±Ψ¨ΩŠΨ©
112
+ {currentLocale === 'ar' && (
113
+ <span className='ml-auto text-setup-secondary'>βœ“</span>
114
+ )}
115
+ </DropdownMenuItem>
116
+ </DropdownMenuContent>
117
+ </DropdownMenu>
118
+ );
119
+ }`,
120
+
121
+ 'components/layout/providers.tsx': `'use client';
122
+
123
+ import { ThemeProvider as NextThemeProvider } from 'next-themes';
124
+ import { Provider as ReduxProvider } from 'react-redux';
125
+ import { ReactNode } from 'react';
126
+ import { store } from '@/lib/shared/store';
127
+ import { NotificationProvider } from '@/lib/features/notifications/NotificationContext';
128
+
129
+ interface ProvidersProps {
130
+ children: ReactNode;
131
+ }
132
+
133
+ export function Providers({ children }: ProvidersProps) {
134
+ return (
135
+ <ReduxProvider store={store}>
136
+ <NotificationProvider>
137
+ <NextThemeProvider
138
+ attribute='class'
139
+ defaultTheme='system'
140
+ enableSystem
141
+ disableTransitionOnChange
142
+ themes={['light', 'dark', 'system']}
143
+ >
144
+ {children}
145
+ </NextThemeProvider>
146
+ </NotificationProvider>
147
+ </ReduxProvider>
148
+ );
149
+ }`,
150
+
151
+ 'components/shared/ConfirmationDialog.tsx': `'use client';
152
+
153
+ import React from 'react';
154
+ import { Button } from '@/components/ui/button';
155
+ import {
156
+ Dialog,
157
+ DialogContent,
158
+ DialogDescription,
159
+ DialogFooter,
160
+ DialogHeader,
161
+ DialogTitle,
162
+ } from '@/components/ui/dialog';
163
+ import type { VariantProps } from 'class-variance-authority';
164
+ import { buttonVariants } from '@/components/ui/button';
165
+
166
+ interface ConfirmationDialogProps {
167
+ isOpen: boolean;
168
+ onOpenChange: (open: boolean) => void;
169
+ onConfirm: () => void;
170
+ onCancel?: () => void;
171
+ title?: string;
172
+ description?: string;
173
+ confirmLabel?: string;
174
+ cancelLabel?: string;
175
+ confirmVariant?: VariantProps<typeof buttonVariants>['variant'];
176
+ isLoading?: boolean;
177
+ disabled?: boolean;
178
+ children?: React.ReactNode;
179
+ /**
180
+ * When true, shows the footer buttons even when children are provided.
181
+ * When false (default), children completely replace the default content.
182
+ */
183
+ showFooterWithChildren?: boolean;
184
+ }
185
+
186
+ export function ConfirmationDialog({
187
+ isOpen,
188
+ onOpenChange,
189
+ onConfirm,
190
+ onCancel,
191
+ title = 'Confirm Action',
192
+ description,
193
+ confirmLabel = 'Confirm',
194
+ cancelLabel = 'Cancel',
195
+ confirmVariant = 'destructive',
196
+ isLoading = false,
197
+ disabled = false,
198
+ children,
199
+ showFooterWithChildren = false,
200
+ }: ConfirmationDialogProps) {
201
+ const handleCancel = () => {
202
+ if (onCancel) {
203
+ onCancel();
204
+ } else {
205
+ onOpenChange(false);
206
+ }
207
+ };
208
+
209
+ const handleConfirm = () => {
210
+ onConfirm();
211
+ };
212
+
213
+ const footerButtons = (
214
+ <DialogFooter>
215
+ <Button
216
+ variant='outline'
217
+ onClick={handleCancel}
218
+ disabled={isLoading || disabled}
219
+ >
220
+ {cancelLabel}
221
+ </Button>
222
+ <Button
223
+ variant={confirmVariant}
224
+ onClick={handleConfirm}
225
+ disabled={isLoading || disabled}
226
+ >
227
+ {isLoading ? 'Processing...' : confirmLabel}
228
+ </Button>
229
+ </DialogFooter>
230
+ );
231
+
232
+ return (
233
+ <Dialog open={isOpen} onOpenChange={onOpenChange}>
234
+ <DialogContent>
235
+ {children ? (
236
+ <>
237
+ {children}
238
+ {showFooterWithChildren && footerButtons}
239
+ </>
240
+ ) : (
241
+ <>
242
+ <DialogHeader>
243
+ <DialogTitle>{title}</DialogTitle>
244
+ {description && (
245
+ <DialogDescription>{description}</DialogDescription>
246
+ )}
247
+ </DialogHeader>
248
+ {footerButtons}
249
+ </>
250
+ )}
251
+ </DialogContent>
252
+ </Dialog>
253
+ );
254
+ }`,
255
+
256
+ 'hooks/use-mobile.tsx': `import * as React from 'react';
257
+
258
+ const MOBILE_BREAKPOINT = 768;
259
+
260
+ export function useIsMobile() {
261
+ const [isMobile, setIsMobile] = React.useState<boolean | undefined>(
262
+ undefined
263
+ );
264
+
265
+ React.useEffect(() => {
266
+ const mql = window.matchMedia(\`(max-width: \${MOBILE_BREAKPOINT - 1}px)\`);
267
+ const onChange = () => {
268
+ setIsMobile(window.innerWidth < MOBILE_BREAKPOINT);
269
+ };
270
+ mql.addEventListener('change', onChange);
271
+ setIsMobile(window.innerWidth < MOBILE_BREAKPOINT);
272
+ return () => mql.removeEventListener('change', onChange);
273
+ }, []);
274
+
275
+ return !!isMobile;
276
+ }`,
277
+
278
+ 'hooks/use-mobile.ts': `export function useIsMobile() { return false; }`,
279
+ };
@@ -0,0 +1,277 @@
1
+ export const miscTemplates = {
2
+ 'docs/EXPLAIN_METABINARIES_FILE_STRUCTURE.md': `# MetaBinaries: Advanced CRM Architecture Guide
3
+
4
+ This project is built using the **MetaBinaries Feature-Based Internal Architecture**. It is designed for maximum scalability, maintainability, and enterprise-grade performance.
5
+
6
+ ## πŸ—οΈ Core Philosophy
7
+
8
+ The structure follows a **Feature-Driven Development (FDD)** approach. Instead of grouping files by type (e.g., all slices in one folder), we group them by **Domain/Feature**. This minimizes "folder jumping" and keeps related logic together.
9
+
10
+ ---
11
+
12
+ ## πŸ“ Detailed File Structure Explained
13
+
14
+ ### 🌍 Application Root (\`app/[locale]\`)
15
+ Next.js 15 App Router with dynamic locale segments.
16
+ - **\`layout.tsx\`**: The global entry point. Handles fonts, base providers, and internationalization context.
17
+ - **\`globals.css\`**: Tailwind CSS base styles and design system variables (HSL).
18
+ - **\`(auth)/\`**: Grouped routes for authentication.
19
+ - \`login/\`, \`sign-up/\`, \`forgot-password/\`: Stateless pages for user entry.
20
+ - **\`(workspace)/\`**: The internal CRM area (Protected).
21
+ - \`(home)/\`: Grouped route for the main dashboard.
22
+ - \`page.tsx\`: The main Dashboard page.
23
+ - \`_Components/\`: **Flat structure** for layout-specific components (Sidebar, Header, Search).
24
+
25
+ ### πŸ§ͺ Business Logic (\`lib/features/NAME\`)
26
+ This is where the "Brain" of your feature lives. For every new feature (e.g., "leads"), you should create:
27
+ - **\`NAME.api.ts\`**: Defines API endpoints using Axios.
28
+ - **\`NAME.service.ts\`**: Data transformation and business logic (Layer between API and State).
29
+ - **\`NAME.slice.ts\`**: Redux Toolkit slice for state management.
30
+ - **\`NAME.types.ts\`**: TypeScript interfaces/types specific to this feature.
31
+ - **\`NAME.utils.ts\`**: Helper functions used only within this feature.
32
+ - **\`useNAME.ts\`**: A custom React hook for easy access to state and actions from components.
33
+
34
+ ### βš™οΈ Shared Logic (\`lib/shared\`)
35
+ - **\`axios.ts\`**: Pre-configured Axios instance with base URL and interceptors.
36
+ - **\`store.ts\`**: Centralized Redux store configuration.
37
+ - **\`utils/validation/\`**: Zod schemas for forms and API validation.
38
+ - \`common-rules.ts\`: Reusable rules (Email, Password strength).
39
+ - \`NAME.validation.ts\`: Feature-specific form schemas.
40
+
41
+ ### 🍱 UI Components (\`components/\`)
42
+ - **\`ui/\`**: Atomic components (Buttons, Inputs, Dialogs) from Shadcn UI. **Do not put business logic here.**
43
+ - **\`shared/\`**: Reusable higher-order components (Table wrappers, Confirmation dialogs).
44
+ - **\`layout/\`**: App-wide structural logic (AuthGuard, LanguageSwitcher, Providers).
45
+
46
+ ### 🌐 Internationalization (\`i18n/\`)
47
+ - **\`routing.ts\`**: Configuration for supported locales (\`en\`, \`ar\`) and default locale.
48
+ - **\`navigation.ts\`**: Type-safe navigation wrappers (\`Link\`, \`useRouter\`, \`usePathname\`).
49
+ - **\`request.ts\`**: Server-side logic to load the correct JSON messages.
50
+
51
+ ---
52
+
53
+ ## πŸ› οΈ Usage Guides
54
+
55
+ ### How to add a new Locale?
56
+ 1. Open \`i18n/routing.ts\` and add the locale code to \`locales\`.
57
+ 2. Create a new JSON file in \`messages/[code].json\`.
58
+ 3. Update the matching in \`middleware.ts\`.
59
+
60
+ ### How to create a new Feature?
61
+ 1. Create a folder in \`lib/features/[featureName]\`.
62
+ 2. Add the 6 core files (api, service, slice, types, utils, hook).
63
+ 3. Register the new \`slice\` in \`lib/shared/store.ts\`.
64
+ 4. Create the UI for it in \`app/[locale]/(workspace)/[featureName]\`.
65
+
66
+ ---
67
+
68
+ ## πŸ›‘οΈ Security & Quality
69
+ - **Rate Limiting**: Use \`rateLimitMiddleware\` in \`middleware.ts\` for sensitive routes.
70
+ - **Husky**: Automated checks ensure you never commit broken types or bad formatting.
71
+ - **Conventional Commits**: Keeps a clean version history for easier debugging.
72
+
73
+ ---
74
+ *Created by MetaBinaries CLI - Professional Next.js Scaffolding*`,
75
+
76
+ 'README.md': projectName => `# ${projectName}
77
+
78
+ Built with [MetaBinaries](https://metabinaries.com) - The advanced CLI for CRM-style Next.js applications.
79
+
80
+ ## πŸš€ Features
81
+
82
+ - **Next.js 15 (App Router)** - High-performance web architecture.
83
+ - **Internationalization (i18n)** - Full support for English (LTR) and Arabic (RTL) using \`next-intl\`.
84
+ - **Feature-Based Architecture** - Scalable organization under \`lib/features/\`.
85
+ - **State Management** - Centralized store using **Redux Toolkit**.
86
+ - **Modern UI System** - Powered by **Tailwind CSS**, **Shadcn UI**, and **Radix UI**.
87
+ - **Developer Experience**:
88
+ - **Husky Hooks**: Automated pre-commit checks (Type-checking & Formatting).
89
+ - **Conventional Commits**: Standardized commit history.
90
+ - **Turbopack Build**: Optimized development and build speeds.
91
+ - **Security & Utilities**:
92
+ - **Rate Limiting**: Built-in request limiting for protected routes.
93
+ - **Axios Configuration**: Centralized API client with interceptors.
94
+ - **Zod Validation**: Robust schema validation for forms and APIs.
95
+
96
+ ## πŸ› οΈ Tech Stack
97
+
98
+ - **Framework**: Next.js 15
99
+ - **Styling**: Tailwind CSS
100
+ - **State**: Redux Toolkit
101
+ - **Internationalization**: next-intl
102
+ - **Forms**: React Hook Form + Zod
103
+ - **Icons**: Lucide React
104
+ - **Notifications**: Sonner
105
+ - **Themes**: next-themes
106
+ - **Animations**: Framer Motion
107
+
108
+ ## πŸ“ Project Structure
109
+
110
+ \`\`\`
111
+ β”œβ”€ app/[locale] # Root of the application (i18n)
112
+ β”‚ β”œβ”€ (auth) # Authentication routes (Login, SignUp)
113
+ β”‚ └─ (workspace) # Protected CRM workspace routes
114
+ β”‚ └─ (home) # Main dashboard area
115
+ β”œβ”€ components/
116
+ β”‚ β”œβ”€ ui/ # Shadcn UI base components
117
+ β”‚ β”œβ”€ shared/ # Reusable common components
118
+ β”‚ └─ layout/ # AuthGuards, ThemeProviders, etc.
119
+ β”œβ”€ lib/
120
+ β”‚ β”œβ”€ features/ # Business logic grouped by feature
121
+ β”‚ β”œβ”€ shared/ # Centralized Axios, Redux Store
122
+ β”‚ └─ utils/ # Common helper functions
123
+ β”œβ”€ i18n/ # Translation routing and configurations
124
+ └─ messages/ # JSON translation files (en/ar)
125
+ \`\`\`
126
+
127
+ ## 🏁 Getting Started
128
+
129
+ ### 1. Installation
130
+ \`\`\`bash
131
+ npm install
132
+ \`\`\`
133
+
134
+ ### 2. Run Locally
135
+ \`\`\`bash
136
+ npm run dev
137
+ \`\`\`
138
+
139
+ ### 3. Build & Production
140
+ \`\`\`bash
141
+ npm run build
142
+ \`\`\`
143
+
144
+ ### πŸ“ Scripts
145
+
146
+ - \`npm run dev\`: Start the development server.
147
+ - \`npm run build\`: Clean build, format check, and Next.js build.
148
+ - \`npm run format\`: Format entire codebase with Prettier.
149
+ - \`npm run type-check\`: Run TypeScript compiler checks.
150
+
151
+ ## 🀝 Contributing
152
+
153
+ This project follows **Conventional Commits**. Please make sure your commit messages match the required format:
154
+ \`type(scope): subject\` (e.g., \`feat(auth): add google sign-in\`).
155
+
156
+ ---
157
+ Generated by **MetaBinaries CLI**`,
158
+
159
+ '.husky/pre-commit': `echo "πŸ” Running pre-commit checks..."
160
+ echo ""
161
+
162
+ # Step 1: Run type check first (fail fast if there are type errors)
163
+ echo "πŸ”Ž Running TypeScript type check..."
164
+ if ! npm run type-check; then
165
+ echo ""
166
+ echo "❌ Type check failed! Please fix type errors before committing."
167
+ echo ""
168
+ echo "To commit anyway (not recommended), use:"
169
+ echo " git commit --no-verify"
170
+ echo ""
171
+ exit 1
172
+ fi
173
+ echo "βœ… Type check passed!"
174
+ echo ""
175
+
176
+ # Step 2: Format code after type check passes
177
+ echo "πŸ“ Formatting code with Prettier..."
178
+ if ! npm run format; then
179
+ echo ""
180
+ echo "❌ Formatting failed! Please fix formatting issues."
181
+ echo ""
182
+ exit 1
183
+ fi
184
+ echo "βœ… Code formatted successfully!"
185
+ echo ""
186
+
187
+ echo "βœ… All pre-commit checks passed! Proceeding with commit..."`,
188
+
189
+ '.husky/commit-msg': `# Validates commit message format
190
+
191
+ # Conventional commit format: type(scope): subject
192
+ # Note: Added optional ! for breaking changes (e.g., feat!: or fix!:)
193
+ commit_regex='^(feat|fix|docs|style|refactor|test|chore|perf|ci|build|revert|hotfix|release)(!)?(\\(.+\\))?: .{1,}'
194
+
195
+ if ! grep -qE "$commit_regex" "$1"; then
196
+ echo "❌ Invalid commit message format!"
197
+ echo ""
198
+ echo "Commit message must follow conventional commit format:"
199
+ echo " type(scope): subject"
200
+ echo " or type!: subject (for breaking changes)"
201
+ echo ""
202
+ echo "Types: feat, fix, docs, style, refactor, test, chore, perf, ci, build, revert, hotfix, release"
203
+ echo ""
204
+ echo "Examples:"
205
+ echo " feat: add user authentication"
206
+ echo " fix(api): resolve login bug"
207
+ echo " feat!: change API response format (breaking change)"
208
+ echo " docs: update README"
209
+ echo ""
210
+ exit 1
211
+ fi
212
+
213
+ echo "βœ… Commit message format is valid!"`,
214
+
215
+ '.husky/pre-push': `echo "\ud83d\ude80 Running pre-push checks..."
216
+ echo ""
217
+
218
+ # Get current branch
219
+ current_branch=$(git rev-parse --abbrev-ref HEAD)
220
+
221
+ # Protect main/master branches from direct pushes
222
+ protected_branches="^(main|master|production)$"
223
+ if echo "$current_branch" | grep -qE "$protected_branches"; then
224
+ echo "❌ Direct pushes to $current_branch are not allowed!"
225
+ echo ""
226
+ echo "Please create a feature branch and open a pull request:"
227
+ echo " git checkout -b feature/your-feature-name"
228
+ echo " git push origin feature/your-feature-name"
229
+ echo ""
230
+ echo "Or if you really need to push directly, use:"
231
+ echo " git push --no-verify"
232
+ echo ""
233
+ exit 1
234
+ fi
235
+
236
+ # Run tests if test script exists
237
+ if npm run --silent 2>&1 | grep -q "\\"test\\""; then
238
+ echo "πŸ§ͺ Running tests..."
239
+ if ! npm test; then
240
+ echo ""
241
+ echo "❌ Tests failed! Please fix failing tests before pushing."
242
+ echo ""
243
+ echo "To push anyway (not recommended), use:"
244
+ echo " git push --no-verify"
245
+ echo ""
246
+ exit 1
247
+ fi
248
+ echo "βœ… All tests passed!"
249
+ echo ""
250
+ fi
251
+
252
+ echo "βœ… All pre-push checks passed! Proceeding with push..."`,
253
+
254
+ '.husky/prepare-commit-msg': `# Add branch name to commit message automatically
255
+ COMMIT_MSG_FILE=$1
256
+ COMMIT_SOURCE=$2
257
+
258
+ # Only add branch name for regular commits (not merge, squash, etc.)
259
+ if [ -z "$COMMIT_SOURCE" ]; then
260
+ BRANCH_NAME=$(git rev-parse --abbrev-ref HEAD 2>/dev/null)
261
+
262
+ # Skip if on main/master or if branch name already in message
263
+ if ! echo "$BRANCH_NAME" | grep -qE "^(main|master|HEAD)$"; then
264
+ COMMIT_MSG=$(cat "$COMMIT_MSG_FILE")
265
+
266
+ # Only add if not already present
267
+ if ! echo "$COMMIT_MSG" | grep -q "$BRANCH_NAME"; then
268
+ # Extract ticket number from branch name (e.g., feature/PROJ-123-description -> PROJ-123)
269
+ TICKET=$(echo "$BRANCH_NAME" | grep -oE "[A-Z]+-[0-9]+" | head -1)
270
+
271
+ if [ -n "$TICKET" ]; then
272
+ echo "$COMMIT_MSG" | sed "1s/$/ [$TICKET]/" > "$COMMIT_MSG_FILE"
273
+ fi
274
+ fi
275
+ fi
276
+ fi`,
277
+ };
@@ -0,0 +1,42 @@
1
+ export const dependencies = [
2
+ '@reduxjs/toolkit',
3
+ 'react-redux',
4
+ 'next-intl',
5
+ 'axios',
6
+ 'clsx',
7
+ 'tailwind-merge',
8
+ 'zod',
9
+ 'react-hook-form',
10
+ '@hookform/resolvers',
11
+ 'date-fns',
12
+ 'lucide-react',
13
+ 'sonner',
14
+ 'next-themes',
15
+ 'tailwindcss-animate',
16
+ 'class-variance-authority',
17
+ '@radix-ui/react-avatar',
18
+ '@radix-ui/react-collapsible',
19
+ '@radix-ui/react-dialog',
20
+ '@radix-ui/react-dropdown-menu',
21
+ '@radix-ui/react-label',
22
+ '@radix-ui/react-navigation-menu',
23
+ '@radix-ui/react-popover',
24
+ '@radix-ui/react-scroll-area',
25
+ '@radix-ui/react-separator',
26
+ '@radix-ui/react-slot',
27
+ '@radix-ui/react-switch',
28
+ '@radix-ui/react-tooltip',
29
+ 'framer-motion',
30
+ 'input-otp',
31
+ 'cmdk',
32
+ 'vaul',
33
+ ];
34
+
35
+ export const devDependencies = [
36
+ '@types/node',
37
+ 'prettier',
38
+ 'prettier-plugin-tailwindcss',
39
+ 'husky',
40
+ 'lint-staged',
41
+ 'rimraf',
42
+ ];