metabinaries 1.3.3

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,788 @@
1
+ // Clean Layout & Components (Simple Frontend Only)
2
+ export const layoutTemplates = {
3
+ 'lib/providers.tsx': (projectName, options = {}) => {
4
+ const { includeRedux = false, includeAIChat = false } = options;
5
+ const useRedux = includeRedux || includeAIChat;
6
+
7
+ return `'use client';
8
+
9
+ import { ThemeProvider as NextThemeProvider } from 'next-themes';
10
+ ${useRedux ? "import { StoreProvider } from '@/store/features/aiChat/Provider';" : ""}
11
+ import { ReactNode } from 'react';
12
+ import { Toaster } from '@/components/ui/sonner';
13
+ import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
14
+
15
+ const queryClient = new QueryClient({
16
+ defaultOptions: {
17
+ queries: {
18
+ staleTime: 5 * 1000,
19
+ retry: 1,
20
+ refetchOnWindowFocus: false,
21
+ refetchOnReconnect: true,
22
+ },
23
+ mutations: {
24
+ retry: 1,
25
+ },
26
+ },
27
+ });
28
+
29
+ interface ProvidersProps {
30
+ children: ReactNode;
31
+ }
32
+
33
+ export function Providers({ children }: ProvidersProps) {
34
+ return (
35
+ <QueryClientProvider client={queryClient}>
36
+ ${useRedux ? "<StoreProvider>" : ""}
37
+ <NextThemeProvider
38
+ attribute='class'
39
+ defaultTheme='system'
40
+ enableSystem
41
+ disableTransitionOnChange
42
+ themes={['light', 'dark', 'system']}
43
+ >
44
+ {children}
45
+ <Toaster
46
+ position='bottom-right'
47
+ expand={true}
48
+ richColors={true}
49
+ closeButton={true}
50
+ />
51
+ </NextThemeProvider>
52
+ ${useRedux ? "</StoreProvider>" : ""}
53
+ </QueryClientProvider>
54
+ );
55
+ }`;
56
+ },
57
+
58
+ 'components/layout/LanguageSwitcher.tsx': `'use client';
59
+
60
+ import { Globe } from 'lucide-react';
61
+ import { Button } from '@/components/ui/button';
62
+ import {
63
+ DropdownMenu,
64
+ DropdownMenuContent,
65
+ DropdownMenuItem,
66
+ DropdownMenuTrigger,
67
+ } from '@/components/ui/dropdown-menu';
68
+ import { useParams, useSearchParams } from 'next/navigation';
69
+ import { useRouter, usePathname } from '@/i18n/navigation';
70
+ import { cn } from '@/lib/utils';
71
+ import { useTransition } from 'react';
72
+
73
+ type Locale = 'en' | 'ar';
74
+
75
+ const LANGUAGES = [
76
+ { code: 'en', name: 'English', flag: '🇬🇧' },
77
+ { code: 'ar', name: 'العربية', flag: '🇸🇦', dir: 'rtl' },
78
+ ] as const;
79
+
80
+ export function LanguageSwitcher() {
81
+ const params = useParams();
82
+ const searchParams = useSearchParams();
83
+ const router = useRouter();
84
+ const pathname = usePathname();
85
+ const [isPending, startTransition] = useTransition();
86
+
87
+ const currentLocale = (params?.locale as Locale) || 'en';
88
+
89
+ const handleLocaleChange = (newLocale: Locale) => {
90
+ if (newLocale === currentLocale || isPending) return;
91
+
92
+ startTransition(() => {
93
+ // Build query string from current search params
94
+ const queryString = searchParams.toString();
95
+ const pathnameWithQuery = queryString
96
+ ? \`\${pathname}?\${queryString}\`
97
+ : pathname;
98
+
99
+ router.replace(
100
+ pathnameWithQuery,
101
+ { locale: newLocale }
102
+ );
103
+ });
104
+ };
105
+
106
+ return (
107
+ <DropdownMenu>
108
+ <DropdownMenuTrigger asChild>
109
+ <Button
110
+ variant="ghost"
111
+ size="sm"
112
+ title="Change Language"
113
+ disabled={isPending}
114
+ className="relative text-foreground hover:bg-accent transition-colors gap-2 px-3 h-10 focus-visible:outline-none focus:ring-0 focus:ring-offset-0"
115
+ aria-label="Change language"
116
+ >
117
+ <Globe className={cn(
118
+ 'h-[18px] w-[18px] text-muted-foreground',
119
+ isPending && 'animate-spin'
120
+ )} />
121
+ <span className="text-sm font-semibold uppercase tracking-wide">
122
+ {currentLocale}
123
+ </span>
124
+ <span className="sr-only">Change Language</span>
125
+ </Button>
126
+ </DropdownMenuTrigger>
127
+ <DropdownMenuContent align="end" className="min-w-[160px]" onCloseAutoFocus={(e) => e.preventDefault()}>
128
+ {LANGUAGES.map((lang) => {
129
+ const isActive = currentLocale === lang.code;
130
+ return (
131
+ <DropdownMenuItem
132
+ key={lang.code}
133
+ onClick={() => handleLocaleChange(lang.code as Locale)}
134
+ disabled={isPending}
135
+ className={cn(
136
+ 'cursor-pointer justify-between',
137
+ isActive && 'bg-muted font-medium'
138
+ )}
139
+ >
140
+ <span className="flex items-center gap-2">
141
+ <span className="text-lg">{lang.flag}</span>
142
+ <span>{lang.name}</span>
143
+ </span>
144
+ {isActive && (
145
+ <span className="text-primary font-bold">✓</span>
146
+ )}
147
+ </DropdownMenuItem>
148
+ );
149
+ })}
150
+ </DropdownMenuContent>
151
+ </DropdownMenu>
152
+ );
153
+ }`,
154
+
155
+ 'components/layout/ThemeSwitcher.tsx': `'use client';
156
+
157
+ import { Moon, Sun, Monitor } from 'lucide-react';
158
+ import { useTheme } from 'next-themes';
159
+ import { Button } from '@/components/ui/button';
160
+ import {
161
+ DropdownMenu,
162
+ DropdownMenuContent,
163
+ DropdownMenuItem,
164
+ DropdownMenuTrigger,
165
+ } from '@/components/ui/dropdown-menu';
166
+
167
+ export function ThemeSwitcher() {
168
+ const { theme, setTheme } = useTheme();
169
+
170
+ return (
171
+ <DropdownMenu>
172
+ <DropdownMenuTrigger asChild>
173
+ <Button
174
+ variant='ghost'
175
+ size='sm'
176
+ title='Toggle theme'
177
+ className='text-foreground hover:bg-accent h-10 w-10 p-0 rounded-full transition-colors focus-visible:outline-none focus:ring-0 focus:ring-offset-0'
178
+ >
179
+ <Sun className='h-[18px] w-[18px] rotate-0 scale-100 transition-all dark:-rotate-90 dark:scale-0 text-muted-foreground' />
180
+ <Moon className='absolute h-[18px] w-[18px] rotate-90 scale-0 transition-all dark:rotate-0 dark:scale-100 text-muted-foreground' />
181
+ <span className='sr-only'>Toggle theme</span>
182
+ </Button>
183
+ </DropdownMenuTrigger>
184
+ <DropdownMenuContent align='end' onCloseAutoFocus={(e) => e.preventDefault()}>
185
+ <DropdownMenuItem onClick={() => setTheme('light')}>
186
+ <Sun className='mr-2 h-4 w-4' />
187
+ Light
188
+ {theme === 'light' && <span className='ml-auto'>✓</span>}
189
+ </DropdownMenuItem>
190
+ <DropdownMenuItem onClick={() => setTheme('dark')}>
191
+ <Moon className='mr-2 h-4 w-4' />
192
+ Dark
193
+ {theme === 'dark' && <span className='ml-auto'>✓</span>}
194
+ </DropdownMenuItem>
195
+ <DropdownMenuItem onClick={() => setTheme('system')}>
196
+ <Monitor className='mr-2 h-4 w-4' />
197
+ System
198
+ {theme === 'system' && <span className='ml-auto'>✓</span>}
199
+ </DropdownMenuItem>
200
+ </DropdownMenuContent>
201
+ </DropdownMenu>
202
+ );
203
+ }`,
204
+
205
+ 'components/layout/Header.tsx': `'use client';
206
+
207
+ import Link from 'next/link';
208
+ import * as React from 'react';
209
+ import { usePathname } from 'next/navigation';
210
+ import { LanguageSwitcher } from './LanguageSwitcher';
211
+ import { ThemeSwitcher } from './ThemeSwitcher';
212
+ import { Button } from '@/components/ui/button';
213
+ import { Menu, X, ArrowRight } from 'lucide-react';
214
+ import { useState } from 'react';
215
+ import Logo from '@/components/ui/logo';
216
+ import { cn } from '@/lib/utils';
217
+
218
+ const defaultNavigation = [
219
+ { name: 'Home', href: '/' },
220
+ ];
221
+
222
+ export function Header({
223
+ siteName = 'MetaBinaries',
224
+ navigation = defaultNavigation,
225
+ }) {
226
+ const pathname = usePathname();
227
+ const [mobileMenuOpen, setMobileMenuOpen] = useState(false);
228
+
229
+ const locale = pathname?.split('/')[1] || 'en';
230
+ const currentPath = pathname?.replace(\`/\${locale}\`, '') || '/';
231
+
232
+ return (
233
+ <header className='sticky top-0 z-50 w-full bg-background/80 backdrop-blur-md border-b border-border/40 py-4'>
234
+ <div className='w-full flex h-14 items-center justify-between px-4 sm:px-12'>
235
+
236
+ <Link
237
+ href={\`/\${locale}\`}
238
+ className='flex items-center gap-3 group shrink-0'
239
+ >
240
+ <Logo className='w-auto h-auto' />
241
+ <span className='text-xl font-bold tracking-tight text-foreground'>
242
+ {siteName}
243
+ </span>
244
+ </Link>
245
+
246
+ {/* Desktop Nav */}
247
+ <nav className='hidden md:flex items-center gap-1 p-1'>
248
+ {navigation.map(item => {
249
+ const isActive =
250
+ currentPath === item.href ||
251
+ (item.href !== '/' && currentPath.startsWith(item.href));
252
+ return (
253
+ <Link
254
+ key={item.name}
255
+ href={\`/\${locale}\${item.href}\`}
256
+ className={cn(
257
+ 'px-6 py-2 rounded-full text-sm font-medium transition-all duration-300',
258
+ isActive
259
+ ? 'text-blue-600 bg-blue-500/10 dark:text-blue-400'
260
+ : 'text-muted-foreground hover:text-foreground hover:bg-accent'
261
+ )}
262
+ >
263
+ {item.name}
264
+ </Link>
265
+ );
266
+ })}
267
+ </nav>
268
+
269
+ <div className='flex items-center gap-4'>
270
+ <div className='hidden sm:flex items-center gap-2'>
271
+ <React.Suspense fallback={<div className='w-8 h-8' />}>
272
+ <LanguageSwitcher />
273
+ </React.Suspense>
274
+ <div className='w-[1px] h-4 bg-border mx-1' />
275
+ <ThemeSwitcher />
276
+ </div>
277
+
278
+ <Button className='hidden md:flex rounded-full bg-blue-600 hover:bg-blue-700 text-white px-6 h-11 border-none font-semibold shadow-lg shadow-blue-500/20 gap-2 transition-all hover:translate-y-[-1px] active:scale-95'>
279
+ Start Project
280
+ <ArrowRight className='w-4 h-4' />
281
+ </Button>
282
+
283
+ <Button
284
+ variant='ghost'
285
+ size='icon'
286
+ className='md:hidden rounded-full text-foreground hover:bg-accent'
287
+ onClick={() => setMobileMenuOpen(!mobileMenuOpen)}
288
+ >
289
+ {mobileMenuOpen ? (
290
+ <X className='w-6 h-6' />
291
+ ) : (
292
+ <Menu className='w-6 h-6' />
293
+ )}
294
+ </Button>
295
+ </div>
296
+ </div>
297
+
298
+ {/* Mobile Menu */}
299
+ {mobileMenuOpen && (
300
+ <div className='md:hidden absolute top-full left-0 w-full bg-background border-b border-border animate-in fade-in slide-in-from-top-4'>
301
+ <div className='container py-8 flex flex-col gap-6'>
302
+ {navigation.map(item => (
303
+ <Link
304
+ key={item.name}
305
+ href={\`/\${locale}\${item.href}\`}
306
+ onClick={() => setMobileMenuOpen(false)}
307
+ className='text-xl font-medium px-4 py-3 text-muted-foreground hover:text-foreground hover:bg-accent rounded-lg transition-colors'
308
+ >
309
+ {item.name}
310
+ </Link>
311
+ ))}
312
+ <div className='flex items-center justify-between gap-4 px-4 pt-6 border-t border-border'>
313
+ <div className='flex items-center gap-4'>
314
+ <React.Suspense fallback={<div className='w-8 h-8' />}>
315
+ <LanguageSwitcher />
316
+ </React.Suspense>
317
+ <ThemeSwitcher />
318
+ </div>
319
+ <Button className='rounded-xl bg-blue-600 px-8 text-white'>
320
+ Get Started
321
+ </Button>
322
+ </div>
323
+ </div>
324
+ </div>
325
+ )}
326
+ </header>
327
+ );
328
+ }`,
329
+
330
+ 'components/layout/Footer.tsx': `'use client';
331
+
332
+ import React from 'react';
333
+ import Link from 'next/link';
334
+ import { Facebook, Twitter, Instagram, Github, Mail } from 'lucide-react';
335
+ import Logo from '@/components/ui/logo';
336
+
337
+ interface FooterProps {
338
+ siteName?: string;
339
+ description?: string;
340
+ }
341
+
342
+ export function Footer({
343
+ siteName = 'MetaBinaries',
344
+ description = 'Empowering businesses with modern digital solutions and intelligent workspaces.'
345
+ }: FooterProps) {
346
+ const currentYear = new Date().getFullYear();
347
+
348
+ const footerLinks = [
349
+ {
350
+ title: 'Product',
351
+ links: [
352
+ { name: 'Features', href: '/#features' },
353
+ { name: 'Documentation', href: 'https://nextjs.org/docs' },
354
+ ],
355
+ },
356
+ {
357
+ title: 'Company',
358
+ links: [
359
+ { name: 'Privacy Policy', href: '/privacy' },
360
+ { name: 'Terms', href: '/terms' },
361
+ ],
362
+ },
363
+ ];
364
+
365
+ return (
366
+ <footer className='bg-slate-50 dark:bg-slate-900 border-t border-slate-200 dark:border-slate-800 transition-colors duration-300'>
367
+ <div className='container mx-auto px-4 pt-16 pb-8'>
368
+ <div className='grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-12 mb-12'>
369
+ {/* Brand Section */}
370
+ <div className='flex flex-col gap-4'>
371
+ <Link href='/' className='flex items-center gap-2 group'>
372
+ <Logo className='w-10 h-10 group-hover:scale-110 transition-transform' />
373
+ <span className='text-2xl font-bold tracking-tight text-slate-900 dark:text-white'>
374
+ {siteName}
375
+ </span>
376
+ </Link>
377
+ <p className='text-slate-500 dark:text-slate-400 text-sm leading-relaxed max-w-xs'>
378
+ {description}
379
+ </p>
380
+ <div className='flex items-center gap-4 mt-2'>
381
+ <Link href='#' className='text-slate-400 hover:text-blue-600 transition-colors'><Facebook size={20} /></Link>
382
+ <Link href='#' className='text-slate-400 hover:text-blue-400 transition-colors'><Twitter size={20} /></Link>
383
+ <Link href='#' className='text-slate-400 hover:text-pink-600 transition-colors'><Instagram size={20} /></Link>
384
+ <Link href='#' className='text-slate-400 hover:text-slate-900 dark:hover:text-white transition-colors'><Github size={20} /></Link>
385
+ </div>
386
+ </div>
387
+
388
+ {/* Links Sections */}
389
+ {footerLinks.map((section) => (
390
+ <div key={section.title} className='flex flex-col gap-4'>
391
+ <h4 className='text-sm font-bold uppercase tracking-widest text-slate-900 dark:text-white'>
392
+ {section.title}
393
+ </h4>
394
+ <ul className='flex flex-col gap-2'>
395
+ {section.links.map((link) => (
396
+ <li key={link.name}>
397
+ <Link
398
+ href={link.href}
399
+ className='text-slate-500 dark:text-slate-400 hover:text-blue-600 dark:hover:text-blue-400 text-sm transition-colors decoration-2 underline-offset-4 hover:underline'
400
+ >
401
+ {link.name}
402
+ </Link>
403
+ </li>
404
+ ))}
405
+ </ul>
406
+ </div>
407
+ ))}
408
+ </div>
409
+
410
+ {/* Bottom Bar */}
411
+ <div className='pt-8 border-t border-slate-200 dark:border-slate-800 flex flex-col md:flex-row justify-between items-center gap-4'>
412
+ <p className='text-slate-400 text-xs'>
413
+ © {currentYear} {siteName}. All rights reserved.
414
+ </p>
415
+ <div className='flex items-center gap-6'>
416
+ <Link href='#' className='text-slate-400 hover:text-slate-600 dark:hover:text-slate-300 text-xs flex items-center gap-1 transition-colors'>
417
+ <Mail size={14} />
418
+ hello@{siteName.toLowerCase()}.com
419
+ </Link>
420
+ </div>
421
+ </div>
422
+ </div>
423
+ </footer>
424
+ );
425
+ }`,
426
+
427
+ 'components/ui/logo.tsx': `'use client';
428
+
429
+ import React from 'react';
430
+ import { cn } from '@/lib/utils';
431
+
432
+ interface LogoProps {
433
+ className?: string;
434
+ showText?: boolean;
435
+ }
436
+
437
+ const Logo: React.FC<LogoProps> = ({ className }) => {
438
+ return (
439
+ <div className={cn('flex items-center gap-2', className)}>
440
+ <div className='relative w-8 h-8 flex items-center justify-center bg-blue-600 rounded-lg shrink-0 overflow-hidden'>
441
+ <div className='w-3 h-3 bg-white rounded-[2px]' />
442
+ {/* Subtle inner detail */}
443
+ <div className='absolute inset-0 border-[3px] border-white/10 rounded-lg' />
444
+ </div>
445
+ </div>
446
+ );
447
+ };
448
+
449
+ export default Logo;`,
450
+
451
+ 'components/layout/MainMenu.tsx': `'use client';
452
+ import React from 'react';
453
+ import Link from 'next/link';
454
+ import { X } from 'lucide-react';
455
+
456
+ export interface MenuLink {
457
+ name: string;
458
+ url: string;
459
+ }
460
+
461
+ export interface MenuSection {
462
+ title: string;
463
+ links: MenuLink[];
464
+ }
465
+
466
+ interface MainMenuProps {
467
+ open: boolean;
468
+ onClose: () => void;
469
+ sections: MenuSection[];
470
+ isRTL?: boolean;
471
+ className?: string;
472
+ overlayClassName?: string;
473
+ }
474
+
475
+ export const MainMenu: React.FC<MainMenuProps> = ({
476
+ open,
477
+ onClose,
478
+ sections,
479
+ isRTL = false,
480
+ className = '',
481
+ overlayClassName = '',
482
+ }) => {
483
+ if (!open) return null;
484
+
485
+ return (
486
+ <div
487
+ className={\`fixed inset-0 z-50 pt-16 xl:pt-20 bg-black/95 backdrop-blur-md flex animate-in fade-in slide-in-from-bottom-8 overflow-y-auto \${overlayClassName}\`}
488
+ >
489
+ <button
490
+ className={\`fixed top-4 xl:top-6 \${isRTL ? 'left-4 xl:left-6' : 'right-4 xl:right-6'
491
+ } text-white hover:bg-white/10 p-2 rounded-full transition-colors z-[60]\`}
492
+ onClick={onClose}
493
+ aria-label='Close menu'
494
+ >
495
+ <X className='w-6 h-6' />
496
+ </button>
497
+
498
+ <div className={\`container mx-auto px-6 py-12 \${className}\`}>
499
+ <div className='grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-12'>
500
+ {sections.map((section, idx) => (
501
+ <div key={idx} className='flex flex-col'>
502
+ <h3 className='text-white text-xl font-bold mb-6 pb-2 border-b border-white/20 uppercase tracking-wider'>
503
+ {section.title}
504
+ </h3>
505
+ <div className='flex flex-col space-y-4'>
506
+ {section.links.map((link, lIdx) => (
507
+ <Link
508
+ key={lIdx}
509
+ href={link.url}
510
+ onClick={onClose}
511
+ className='text-white/70 hover:text-white transition-colors text-lg hover:translate-x-1 duration-200 inline-block'
512
+ >
513
+ {link.name}
514
+ </Link>
515
+ ))}
516
+ </div>
517
+ </div>
518
+ ))}
519
+ </div>
520
+ </div>
521
+ </div>
522
+ );
523
+ };
524
+ export default MainMenu;`,
525
+
526
+ 'components/layout/MobileNavbar.tsx': `'use client';
527
+ import Link from 'next/link';
528
+ import { Button } from '@/components/ui/button';
529
+ import { MenuIcon, UserIcon, Search, LogOut, X, Shield } from 'lucide-react';
530
+ import { useState } from 'react';
531
+ import { useTranslations, useLocale } from 'next-intl';
532
+ import { useRouter, usePathname, useSearchParams } from 'next/navigation';
533
+ import Logo from '@/components/ui/logo';
534
+
535
+ const MobileNavbar = () => {
536
+ const t = useTranslations('aiChat'); // Fallback to aiChat or similar if wanted
537
+ const locale = useLocale();
538
+ const [menuOpen, setMenuOpen] = useState(false);
539
+ const router = useRouter();
540
+ const pathname = usePathname();
541
+ const searchParams = useSearchParams();
542
+
543
+ const currentLocale = pathname?.split('/')[1] === 'ar' ? 'ar' : 'en';
544
+ const otherLocale = currentLocale === 'en' ? 'ar' : 'en';
545
+ const basePath = pathname?.replace(/^\\/(en|ar)/, '') || '/';
546
+
547
+ const searchParamsString = searchParams.toString();
548
+ const queryString = searchParamsString ? \`?\${searchParamsString}\` : '';
549
+
550
+ const currentSection =
551
+ pathname
552
+ ?.replace(/^\\/(en|ar)/, '')
553
+ .split('/')
554
+ .filter(Boolean)[0] || '';
555
+
556
+ const handleLinkClick = () => {
557
+ setMenuOpen(false);
558
+ };
559
+
560
+ const isRTL = locale === 'ar';
561
+
562
+ return (
563
+ <>
564
+ <header className='lg:hidden w-full flex sticky top-0 z-50'>
565
+ <nav className='w-full flex items-center justify-between px-3 md:px-6 bg-blue-600 border-b-blue-500 border-b-2 h-14 sm:h-16 text-white'>
566
+ <Link href={\`/\${currentLocale}\`} onClick={handleLinkClick} className='flex-shrink-0'>
567
+ <Logo className='w-10 h-10 sm:w-12' />
568
+ </Link>
569
+
570
+ <div className='flex items-center gap-2'>
571
+ <button
572
+ className='flex items-center gap-1 px-2 py-1 rounded bg-white/10 hover:bg-white/20 transition text-sm'
573
+ onClick={() =>
574
+ router.push(\`/\${otherLocale}\${basePath}\${queryString}\`)
575
+ }
576
+ >
577
+ {otherLocale === 'ar' ? '🇸🇦' : '🇬🇧'}
578
+ </button>
579
+ +
580
+ <button
581
+ className='p-2 rounded-full hover:bg-white/10 transition'
582
+ onClick={() => setMenuOpen(!menuOpen)}
583
+ >
584
+ {menuOpen ? <X className='w-5 h-5' /> : <MenuIcon className='w-5 h-5' />}
585
+ </button>
586
+ </div>
587
+ </nav>
588
+ </header>
589
+
590
+ {menuOpen && (
591
+ <div className='lg:hidden fixed inset-0 z-50 pt-16 bg-blue-600 bg-opacity-95 animate-in fade-in slide-in-from-top-8'>
592
+ <div className='flex flex-col h-full'>
593
+ <div className='flex-1 px-6 pb-6 overflow-y-auto'>
594
+ <ul className='space-y-4 text-white'>
595
+ {[
596
+ { name: 'Home', path: '' },
597
+ ].map((item) => (
598
+ <li key={item.path}>
599
+ <Link
600
+ href={\`/\${currentLocale}/\${item.path}\`}
601
+ onClick={handleLinkClick}
602
+ className={\`block py-3 text-lg font-semibold rounded-lg transition-colors \${currentSection === item.path
603
+ ? 'bg-white/20 text-white'
604
+ : 'hover:bg-white/10'
605
+ }\`}
606
+ >
607
+ {item.name}
608
+ </Link>
609
+ </li>
610
+ ))}
611
+ </ul>
612
+
613
+ <div className='mt-8 pt-8 border-t border-white/20 space-y-4'>
614
+ <Button className='w-full bg-white text-blue-600 hover:bg-white/90 h-12 rounded-xl font-bold'>
615
+ Get Started
616
+ </Button>
617
+ </div>
618
+ </div>
619
+ </div>
620
+ </div>
621
+ )}
622
+ </>
623
+ );
624
+ };
625
+ export default MobileNavbar;`,
626
+
627
+ 'components/shared/loading-ui.tsx': `'use client';
628
+
629
+ import { cn } from '@/lib/utils';
630
+ import type { VariantProps } from 'class-variance-authority';
631
+ import { cva } from 'class-variance-authority';
632
+ import { motion } from 'framer-motion';
633
+ import Logo from '@/components/ui/logo';
634
+
635
+ const loadingVariants = cva(
636
+ 'flex items-center justify-center relative overflow-hidden',
637
+ {
638
+ variants: {
639
+ variant: {
640
+ default: 'min-h-[100dvh] w-full bg-background',
641
+ inline: 'h-full w-full py-12',
642
+ overlay: 'fixed inset-0 z-[100] bg-background/80 backdrop-blur-md',
643
+ },
644
+ size: {
645
+ sm: 'scale-75',
646
+ default: 'scale-100',
647
+ lg: 'scale-125',
648
+ },
649
+ },
650
+ defaultVariants: {
651
+ variant: 'default',
652
+ size: 'default',
653
+ },
654
+ }
655
+ );
656
+
657
+ interface LoadingProps extends VariantProps<typeof loadingVariants> {
658
+ text?: string;
659
+ className?: string;
660
+ showDots?: boolean;
661
+ }
662
+
663
+ export function Loading({
664
+ variant,
665
+ size,
666
+ text = 'Loading',
667
+ className,
668
+ showDots = true
669
+ }: LoadingProps) {
670
+ return (
671
+ <div
672
+ className={cn(loadingVariants({ variant, size }), className)}
673
+ role="status"
674
+ aria-live="polite"
675
+ aria-label={text}
676
+ >
677
+ {/* Dynamic Background Blobs */}
678
+ {variant === 'default' && (
679
+ <div className="absolute inset-0 overflow-hidden pointer-events-none">
680
+ <motion.div
681
+ animate={{
682
+ scale: [1, 1.2, 1],
683
+ x: [0, 50, 0],
684
+ y: [0, 30, 0],
685
+ }}
686
+ transition={{
687
+ duration: 8,
688
+ repeat: Infinity,
689
+ ease: "easeInOut"
690
+ }}
691
+ className="absolute top-[-10%] left-[-10%] w-[40%] h-[40%] bg-blue-500/5 rounded-full blur-[100px]"
692
+ />
693
+ <motion.div
694
+ animate={{
695
+ scale: [1, 1.1, 1],
696
+ x: [0, -30, 0],
697
+ y: [0, -50, 0],
698
+ }}
699
+ transition={{
700
+ duration: 10,
701
+ repeat: Infinity,
702
+ ease: "easeInOut",
703
+ delay: 1
704
+ }}
705
+ className="absolute bottom-[-10%] right-[-10%] w-[50%] h-[50%] bg-blue-600/5 rounded-full blur-[100px]"
706
+ />
707
+ </div>
708
+ )}
709
+
710
+ <div className="flex flex-col items-center gap-8 relative z-10">
711
+ {/* Modern Animated Logo Container */}
712
+ <div className="relative flex items-center justify-center">
713
+ {/* Outer Rotating Ring */}
714
+ <motion.div
715
+ animate={{ rotate: 360 }}
716
+ transition={{ duration: 3, repeat: Infinity, ease: "linear" }}
717
+ className="absolute w-24 h-24 rounded-full border-[3px] border-blue-600/10 border-t-blue-600"
718
+ />
719
+
720
+ {/* Inner Pulsing Circle */}
721
+ <motion.div
722
+ animate={{ scale: [0.9, 1.05, 0.9], opacity: [0.5, 0.8, 0.5] }}
723
+ transition={{ duration: 2, repeat: Infinity, ease: "easeInOut" }}
724
+ className="absolute w-16 h-16 rounded-full bg-blue-600/10"
725
+ />
726
+
727
+ {/* Core Logo */}
728
+ <motion.div
729
+ initial={{ scale: 0.8, opacity: 0 }}
730
+ animate={{ scale: 1, opacity: 1 }}
731
+ transition={{ duration: 0.5 }}
732
+ className="relative z-20 p-4 bg-blue-600 rounded-2xl shadow-xl shadow-blue-600/20"
733
+ >
734
+ <Logo className="w-8 h-8 text-white" />
735
+ </motion.div>
736
+ </div>
737
+
738
+ {/* Text Area */}
739
+ {text && (
740
+ <div className="flex flex-col items-center gap-3">
741
+ <div className="flex items-center gap-2">
742
+ <motion.p
743
+ initial={{ opacity: 0, y: 10 }}
744
+ animate={{ opacity: 1, y: 0 }}
745
+ transition={{ delay: 0.2 }}
746
+ className="text-lg font-bold tracking-tight text-foreground"
747
+ >
748
+ {text}
749
+ </motion.p>
750
+ {showDots && (
751
+ <div className="flex gap-1.5 pt-1">
752
+ {[0, 1, 2].map((i) => (
753
+ <motion.span
754
+ key={i}
755
+ animate={{
756
+ scale: [1, 1.5, 1],
757
+ opacity: [0.3, 1, 0.3],
758
+ }}
759
+ transition={{
760
+ duration: 1,
761
+ repeat: Infinity,
762
+ delay: i * 0.2,
763
+ ease: "easeInOut",
764
+ }}
765
+ className="h-1.5 w-1.5 rounded-full bg-blue-600"
766
+ />
767
+ ))}
768
+ </div>
769
+ )}
770
+ </div>
771
+
772
+ <motion.p
773
+ initial={{ opacity: 0 }}
774
+ animate={{ opacity: 1 }}
775
+ transition={{ delay: 0.4 }}
776
+ className="text-[11px] font-semibold text-muted-foreground/50 uppercase tracking-[0.3em]"
777
+ >
778
+ Building your experience
779
+ </motion.p>
780
+ </div>
781
+ )}
782
+ </div>
783
+
784
+ <span className="sr-only">Please wait while content is loading</span>
785
+ </div>
786
+ );
787
+ }`,
788
+ };