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,391 @@
1
+ export const configTemplates = {
2
+ '.gitattributes': `# Auto detect text files and perform LF normalization
3
+ * text=auto
4
+
5
+ # Force Unix line endings for shell scripts
6
+ *.sh text eol=lf
7
+ .husky/* text eol=lf
8
+
9
+ # Force Windows line endings for batch files
10
+ *.bat text eol=crlf
11
+
12
+ # Binary files
13
+ *.png binary
14
+ *.jpg binary
15
+ *.ico binary
16
+ *.gif binary
17
+ *.woff binary
18
+ *.woff2 binary
19
+ `,
20
+
21
+ '.gitignore': `# dependencies
22
+ /node_modules
23
+ /.pnp
24
+ .pnp.js
25
+
26
+ # testing
27
+ /coverage
28
+
29
+ # next.js
30
+ /.next/
31
+ /out/
32
+
33
+ # production
34
+ /build
35
+
36
+ # debug
37
+ npm-debug.log*
38
+ yarn-debug.log*
39
+ yarn-error.log*
40
+
41
+ # local env files
42
+ .env
43
+ .env.local
44
+ .env.development.local
45
+ .env.test.local
46
+ .env.production.local
47
+
48
+ # vercel
49
+ .vercel
50
+
51
+ # typescript
52
+ *.tsbuildinfo
53
+ next-env.d.ts`,
54
+
55
+ '.env': `NEXT_PUBLIC_APP_URL=http://localhost:3000
56
+ NEXT_PUBLIC_SITE_NAME=Next.js Starter Template
57
+ NEXT_PUBLIC_DEFAULT_LOCALE=en
58
+ NEXT_PUBLIC_BACKEND_URL=http://localhost:5000`,
59
+
60
+ '.env.local': `NEXT_PUBLIC_APP_URL=http://localhost:3000
61
+ NEXT_PUBLIC_SITE_NAME=Next.js Starter Template
62
+ NEXT_PUBLIC_DEFAULT_LOCALE=en
63
+ NEXT_PUBLIC_BACKEND_URL=http://localhost:5000`,
64
+
65
+ '.env.example': `NEXT_PUBLIC_APP_URL=http://localhost:3000
66
+ NEXT_PUBLIC_SITE_NAME=Next.js Starter Template
67
+ NEXT_PUBLIC_DEFAULT_LOCALE=en
68
+ NEXT_PUBLIC_BACKEND_URL=http://localhost:5000`,
69
+
70
+ 'api.yml': projectName => `openapi: 3.0.0
71
+ info:
72
+ title: ${projectName} API
73
+ version: 1.0.0
74
+ paths: {}`,
75
+
76
+ '.versionrc.json': `{
77
+ "types": [
78
+ {"type": "feat", "section": "Features"},
79
+ {"type": "fix", "section": "Bug Fixes"}
80
+ ]
81
+ }`,
82
+
83
+ 'components.json': `{
84
+ "$schema": "https://ui.shadcn.com/schema.json",
85
+ "style": "default",
86
+ "rsc": true,
87
+ "tsx": true,
88
+ "tailwind": {
89
+ "config": "tailwind.config.ts",
90
+ "css": "app/[locale]/globals.css",
91
+ "baseColor": "slate",
92
+ "cssVariables": true,
93
+ "prefix": ""
94
+ },
95
+ "aliases": {
96
+ "components": "@/components",
97
+ "utils": "@/lib/utils"
98
+ }
99
+ }`,
100
+
101
+ 'eslint.config.mjs': `export default [];`,
102
+
103
+ 'next.config.ts': `import type { NextConfig } from 'next';
104
+ import createNextIntlPlugin from 'next-intl/plugin';
105
+
106
+ const nextConfig: NextConfig = {
107
+ // Only use standalone output in production builds, not for dev server
108
+ ...(process.env.NODE_ENV === 'production' &&
109
+ !process.env.NEXT_PUBLIC_SKIP_STANDALONE
110
+ ? { output: 'standalone' }
111
+ : {}),
112
+ // Silence workspace root warning (useful for nested projects)
113
+ outputFileTracingRoot: process.cwd(),
114
+ /* config options here */
115
+ // Ignore ESLint errors during production builds
116
+ eslint: {
117
+ ignoreDuringBuilds: true,
118
+ },
119
+ // Ignore TypeScript errors during production builds
120
+ typescript: {
121
+ ignoreBuildErrors: false,
122
+ },
123
+ images: {
124
+ // Disable image optimization for external URLs to fix cloud deployment issues
125
+ unoptimized: true,
126
+ // Keep domains for backward compatibility
127
+ domains: [
128
+ 'cdn.sportmonks.com',
129
+ 'images.unsplash.com',
130
+ 'images.pexels.com',
131
+ 'images.pixabay.com',
132
+ 'imgur.com',
133
+ 'localhost',
134
+ '127.0.0.1',
135
+ ],
136
+ remotePatterns: [
137
+ {
138
+ protocol: 'https',
139
+ hostname: '**',
140
+ port: '',
141
+ pathname: '/**',
142
+ },
143
+ ],
144
+ dangerouslyAllowSVG: true,
145
+ contentDispositionType: 'attachment',
146
+ contentSecurityPolicy: "default-src 'self'; script-src 'none'; sandbox;",
147
+ // Add image optimization settings
148
+ formats: ['image/webp', 'image/avif'],
149
+ deviceSizes: [640, 750, 828, 1080, 1200, 1920, 2048, 3840],
150
+ imageSizes: [16, 32, 48, 64, 96, 128, 256, 384],
151
+ },
152
+ // Environment variables
153
+ env: {
154
+ NEXT_PUBLIC_BACKEND_URL:
155
+ process.env.NEXT_PUBLIC_BACKEND_URL || 'http://localhost:5000',
156
+ },
157
+ // Disable static optimization for pages that use Redux
158
+ experimental: {
159
+ // This will ensure the page is rendered on the server
160
+ serverActions: {
161
+ bodySizeLimit: '2mb',
162
+ },
163
+ },
164
+ // Configure which pages should be statically optimized
165
+ pageExtensions: ['tsx', 'ts', 'jsx', 'js'],
166
+ // Ensure proper handling of client-side navigation
167
+ reactStrictMode: true,
168
+ };
169
+
170
+ const withNextIntl = createNextIntlPlugin();
171
+ export default withNextIntl(nextConfig);`,
172
+
173
+ 'postcss.config.mjs': `const config = {
174
+ plugins: {
175
+ "@tailwindcss/postcss": {},
176
+ },
177
+ };
178
+
179
+ export default config;`,
180
+
181
+ 'tailwind.config.ts': `import type { Config } from "tailwindcss";
182
+
183
+ const config: Config = {
184
+ darkMode: "class",
185
+ content: [
186
+ "./pages/**/*.{js,ts,jsx,tsx,mdx}",
187
+ "./components/**/*.{js,ts,jsx,tsx,mdx}",
188
+ "./app/**/*.{js,ts,jsx,tsx,mdx}",
189
+ ],
190
+ theme: {
191
+ extend: {
192
+ colors: {
193
+ background: 'hsl(var(--background))',
194
+ foreground: 'hsl(var(--foreground))',
195
+ card: {
196
+ DEFAULT: 'hsl(var(--card))',
197
+ foreground: 'hsl(var(--card-foreground))'
198
+ },
199
+ popover: {
200
+ DEFAULT: 'hsl(var(--popover))',
201
+ foreground: 'hsl(var(--popover-foreground))'
202
+ },
203
+ primary: {
204
+ DEFAULT: 'hsl(var(--primary))',
205
+ foreground: 'hsl(var(--primary-foreground))'
206
+ },
207
+ secondary: {
208
+ DEFAULT: 'hsl(var(--secondary))',
209
+ foreground: 'hsl(var(--secondary-foreground))'
210
+ },
211
+ muted: {
212
+ DEFAULT: 'hsl(var(--muted))',
213
+ foreground: 'hsl(var(--muted-foreground))'
214
+ },
215
+ accent: {
216
+ DEFAULT: 'hsl(var(--accent))',
217
+ foreground: 'hsl(var(--accent-foreground))'
218
+ },
219
+ destructive: {
220
+ DEFAULT: 'hsl(var(--destructive))',
221
+ foreground: 'hsl(var(--destructive-foreground))'
222
+ },
223
+ border: 'hsl(var(--border))',
224
+ input: 'hsl(var(--input))',
225
+ ring: 'hsl(var(--ring))',
226
+ chart: {
227
+ '1': 'hsl(var(--chart-1))',
228
+ '2': 'hsl(var(--chart-2))',
229
+ '3': 'hsl(var(--chart-3))',
230
+ '4': 'hsl(var(--chart-4))',
231
+ '5': 'hsl(var(--chart-5))'
232
+ }
233
+ },
234
+ borderRadius: {
235
+ lg: 'var(--radius)',
236
+ md: 'calc(var(--radius) - 2px)',
237
+ sm: 'calc(var(--radius) - 4px)'
238
+ }
239
+ }
240
+ },
241
+ plugins: [require("tailwindcss-animate")],
242
+ };
243
+ export default config;`,
244
+
245
+ 'tsconfig.json': `{
246
+ "compilerOptions": {
247
+ "target": "ES2017",
248
+ "lib": ["dom", "dom.iterable", "esnext"],
249
+ "allowJs": true,
250
+ "skipLibCheck": true,
251
+ "strict": true,
252
+ "noEmit": true,
253
+ "esModuleInterop": true,
254
+ "module": "esnext",
255
+ "moduleResolution": "bundler",
256
+ "resolveJsonModule": true,
257
+ "isolatedModules": true,
258
+ "jsx": "preserve",
259
+ "incremental": true,
260
+ "plugins": [
261
+ {
262
+ "name": "next"
263
+ }
264
+ ],
265
+ "paths": {
266
+ "@/*": ["./*"]
267
+ }
268
+ },
269
+ "include": [
270
+ "next-env.d.ts",
271
+ "**/*.ts",
272
+ "**/*.tsx",
273
+ ".next/types/**/*.ts",
274
+ ".next/dev/types/**/*.ts",
275
+ "**/*.mts"
276
+ ],
277
+ "exclude": ["node_modules"]
278
+ }`,
279
+
280
+ 'next-intl.config.ts': `import { getRequestConfig } from 'next-intl/server';
281
+ import { notFound } from 'next/navigation';
282
+ import { routing } from './i18n/routing';
283
+
284
+ export default getRequestConfig(async ({ locale }) => {
285
+ if (!locale || !routing.locales.includes(locale as any)) {
286
+ notFound();
287
+ }
288
+
289
+ return {
290
+ locale,
291
+ messages: (await import(\`./messages/\${locale}.json\`)).default,
292
+ };
293
+ });`,
294
+ 'messages/en.json': `{
295
+ "Metadata": {
296
+ "title": "MetaBinaries",
297
+ "description": "We build innovative software solutions that transform your ideas into reality. Web development, mobile apps, and AI-powered applications.",
298
+ "keywords": "software development, web development, mobile apps, AI, MetaBinaries, digital solutions",
299
+ "siteName": "MetaBinaries"
300
+ },
301
+ "HomePage": {
302
+ "title": "Welcome to MetaBinaries",
303
+ "description": "Your Websites-style workspace is ready."
304
+ },
305
+ "aiChat": {
306
+ "offline": "Offline",
307
+ "online": "Online",
308
+ "confirmClear": "Clear conversation history?",
309
+ "confirmClearTitle": "Clear History",
310
+ "clear": "Clear History",
311
+ "cancel": "Cancel",
312
+ "micErrorTitle": "Microphone Error",
313
+ "micErrorDesc": "Microphone access denied or not supported.",
314
+ "ok": "OK"
315
+ },
316
+ "aiQuestions": {
317
+ "title": "AI Assistant",
318
+ "subtitle": "Ask me anything!",
319
+ "placeholder": "Ask me anything...",
320
+ "disclaimer": "AI may produce inaccurate information.",
321
+ "emptyStateTitle": "How can I help you today?",
322
+ "emptyStateSubtitle": "I can answer questions, summarize text, and more.",
323
+ "clickToAsk": "Click to ask",
324
+ "description": "Your AI questions are ready.",
325
+ "questions": {
326
+ "meta": "What is Meta?",
327
+ "initials": "What are Initials?",
328
+ "hottorun": "What is Hot to Run?"
329
+ }
330
+ },
331
+ "Questions": {
332
+ "title": "AI Assistant",
333
+ "subtitle": "Ask me anything!",
334
+ "placeholder": "Ask me anything...",
335
+ "disclaimer": "AI may produce inaccurate information.",
336
+ "emptyStateTitle": "How can I help you today?",
337
+ "emptyStateSubtitle": "I can answer questions, summarize text, and more.",
338
+ "clickToAsk": "Click to ask",
339
+ "description": "Your AI questions are ready."
340
+ }
341
+ }`,
342
+ 'messages/ar.json': `{
343
+ "Metadata": {
344
+ "title": "ميتابيناريز",
345
+ "description": "نبني حلول برمجية مبتكرة تحول أفكارك إلى واقع. تطوير المواقع، تطبيقات الموبايل، وتطبيقات الذكاء الاصطناعي.",
346
+ "keywords": "تطوير البرمجيات, تطوير المواقع, تطبيقات الموبايل, الذكاء الاصطناعي, ميتابيناريز, الحلول الرقمية",
347
+ "siteName": "ميتابيناريز"
348
+ },
349
+ "HomePage": {
350
+ "title": "مرحباً بك في ميتابيناريز",
351
+ "description": "بيئة عمل المواقع الخاصة بك جاهزة."
352
+ },
353
+ "aiChat": {
354
+ "offline": "غير متصل",
355
+ "online": "متصل",
356
+ "confirmClear": "هل أنت متأكد من مسح سجل المحادثة؟",
357
+ "confirmClearTitle": "مسح السجل",
358
+ "clear": "مسح السجل",
359
+ "cancel": "إلغاء",
360
+ "micErrorTitle": "خطأ في الميكروفون",
361
+ "micErrorDesc": "تم رفض الوصول إلى الميكروفون أو أنه غير مدعوم.",
362
+ "ok": "تم"
363
+ },
364
+ "aiQuestions": {
365
+ "title": "المساعد الذكي",
366
+ "subtitle": "اسألني أي شيء!",
367
+ "placeholder": "اسألني أي شيء...",
368
+ "disclaimer": "قد ينتج الذكاء الاصطناعي معلومات غير دقيقة.",
369
+ "emptyStateTitle": "كيف يمكنني مساعدتك اليوم؟",
370
+ "emptyStateSubtitle": "يمكنني الإجابة على الأسئلة وتلخيص النصوص والمزيد.",
371
+ "clickToAsk": "انقر للسؤال",
372
+ "description": "الأسئلة",
373
+ "questions": {
374
+ "meta": "ما هو ما تعرفه ب Meta؟",
375
+ "initials": "ما هي Initials؟",
376
+ "hottorun": "كيف يتم البداية"
377
+ }
378
+ },
379
+ "Questions": {
380
+ "title": "المساعد الذكي",
381
+ "subtitle": "اسألني أي شيء!",
382
+ "placeholder": "اسألني أي شيء...",
383
+ "disclaimer": "قد ينتج الذكاء الاصطناعي معلومات غير دقيقة.",
384
+ "emptyStateTitle": "كيف يمكنني مساعدتك اليوم؟",
385
+ "emptyStateSubtitle": "يمكنني الإجابة على الأسئلة وتلخيص النصوص والمزيد.",
386
+ "clickToAsk": "انقر للسؤال",
387
+ "description": "الأسئلة"
388
+ }
389
+ }`,
390
+ };
391
+
@@ -0,0 +1,269 @@
1
+ // Core templates (lib, utils, hooks, i18n, middleware)
2
+ // Conditionally exports i18n files based on options
3
+ export const coreTemplates = (projectName, options = {}) => {
4
+ const { i18n = true } = options;
5
+
6
+ const baseTemplates = {
7
+ 'lib/utils.ts': `import { clsx, type ClassValue } from 'clsx';
8
+ import { twMerge } from 'tailwind-merge';
9
+
10
+ export function cn(...inputs: ClassValue[]) {
11
+ return twMerge(clsx(inputs));
12
+ }`,
13
+
14
+ 'hooks/use-mobile.tsx': `import * as React from "react"
15
+
16
+ const MOBILE_BREAKPOINT = 768
17
+
18
+ export function useIsMobile() {
19
+ const [isMobile, setIsMobile] = React.useState<boolean | undefined>(undefined)
20
+
21
+ React.useEffect(() => {
22
+ const mql = window.matchMedia(\`(max-width: \${MOBILE_BREAKPOINT - 1}px)\`)
23
+ const onChange = () => {
24
+ setIsMobile(window.innerWidth < MOBILE_BREAKPOINT)
25
+ }
26
+ mql.addEventListener("change", onChange)
27
+ setIsMobile(window.innerWidth < MOBILE_BREAKPOINT)
28
+ return () => mql.removeEventListener("change", onChange)
29
+ }, [])
30
+
31
+ return !!isMobile
32
+ }`,
33
+
34
+ 'hooks/use-mobile.ts': `import * as React from "react"
35
+
36
+ const MOBILE_BREAKPOINT = 768
37
+
38
+ export function useIsMobile() {
39
+ const [isMobile, setIsMobile] = React.useState<boolean | undefined>(undefined)
40
+
41
+ React.useEffect(() => {
42
+ const mql = window.matchMedia(\`(max-width: \${MOBILE_BREAKPOINT - 1}px)\`)
43
+ const onChange = () => {
44
+ setIsMobile(window.innerWidth < MOBILE_BREAKPOINT)
45
+ }
46
+ mql.addEventListener("change", onChange)
47
+ setIsMobile(window.innerWidth < MOBILE_BREAKPOINT)
48
+ return () => mql.removeEventListener("change", onChange)
49
+ }, [])
50
+
51
+ return !!isMobile
52
+ }`,
53
+
54
+ 'types/menu-item.d.ts': `export type MenuItem = {
55
+ id?: number;
56
+ name: string;
57
+ url: string;
58
+ };`,
59
+
60
+ 'types/menu-item.ts': `export type MenuLink = {
61
+ name: string;
62
+ arabicName: string;
63
+ url: string;
64
+ };
65
+
66
+ export type SubCategory = {
67
+ _id: string;
68
+ name: string;
69
+ arabicName: string;
70
+ categoryId: string;
71
+ links: MenuLink[];
72
+ order: number;
73
+ createdAt?: string;
74
+ updatedAt?: string;
75
+ __v?: number;
76
+ };
77
+
78
+ export type Category = {
79
+ _id: string;
80
+ name: string;
81
+ arabicName: string;
82
+ order: number;
83
+ createdAt?: string;
84
+ updatedAt?: string;
85
+ __v?: number;
86
+ };
87
+
88
+ // For backward compatibility and admin interface
89
+ export type MenuItem = Category | SubCategory;`,
90
+
91
+ 'services/subcategory.ts': `// This service is currently not used as the application is in frontend-only mode.
92
+ export const subcategoryService = {
93
+ getMenuStructure: async () => [],
94
+ };`,
95
+ };
96
+
97
+ // Only include i18n files if i18n is enabled
98
+ if (i18n) {
99
+ baseTemplates['i18n/routing.ts'] = `import { defineRouting } from 'next-intl/routing';
100
+ import { createNavigation } from 'next-intl/navigation';
101
+
102
+ export const routing = defineRouting({
103
+ locales: ['en', 'ar'],
104
+ defaultLocale: 'en',
105
+ localePrefix: 'always',
106
+ });
107
+
108
+ export type Locale = (typeof routing.locales)[number];`;
109
+
110
+ baseTemplates['i18n/navigation.ts'] = `import { createNavigation } from 'next-intl/navigation';
111
+ import { routing } from './routing';
112
+
113
+ export const { Link, redirect, usePathname, useRouter } =
114
+ createNavigation(routing);`;
115
+
116
+ baseTemplates['i18n/request.ts'] = `import { getRequestConfig } from 'next-intl/server';
117
+ import { routing } from './routing';
118
+
119
+ export default getRequestConfig(async ({ requestLocale }) => {
120
+ let locale = await requestLocale;
121
+
122
+ if (!locale || !routing.locales.includes(locale as any)) {
123
+ locale = routing.defaultLocale;
124
+ }
125
+
126
+ return {
127
+ locale,
128
+ messages: (await import(\`../messages/\${locale}.json\`)).default,
129
+ };
130
+ });`;
131
+
132
+ baseTemplates['middleware.ts'] = `import { NextRequest, NextResponse } from 'next/server';
133
+ import createMiddleware from 'next-intl/middleware';
134
+ import { routing } from './i18n/routing';
135
+
136
+ const intlMiddleware = createMiddleware(routing);
137
+
138
+ // Supported locales
139
+ const locales = routing.locales; // ['en', 'ar']
140
+ const defaultLocale = routing.defaultLocale; // 'en'
141
+
142
+ // Helper function to check if a segment is a valid locale
143
+ function isLocale(segment: string): segment is 'en' | 'ar' {
144
+ return locales.includes(segment as 'en' | 'ar');
145
+ }
146
+
147
+ export default function middleware(request: NextRequest) {
148
+ const { pathname } = request.nextUrl;
149
+ const pathSegments = pathname.split('/').filter(Boolean);
150
+
151
+ // Check if the path is a static file (has file extension)
152
+ const lastSegment = pathSegments[pathSegments.length - 1] || '';
153
+ const isStaticFile =
154
+ lastSegment.includes('.') &&
155
+ /\\.(jpg|jpeg|png|gif|svg|ico|webp|pdf|css|js|json|xml|txt|woff|woff2|ttf|eot|mp4|mp3|wav)$/i.test(
156
+ lastSegment
157
+ );
158
+
159
+ // Skip processing for excluded paths
160
+ if (
161
+ pathname.startsWith('/api') ||
162
+ pathname.startsWith('/_next') ||
163
+ pathname.startsWith('/_vercel') ||
164
+ pathname.startsWith('/_image') ||
165
+ pathname.startsWith('/_static') ||
166
+ isStaticFile
167
+ ) {
168
+ return intlMiddleware(request);
169
+ }
170
+
171
+ // Case: Root path or no locale in path - add default locale
172
+ if (pathSegments.length === 0 || !isLocale(pathSegments[0])) {
173
+ const restOfPath = pathSegments.length === 0 ? '' : pathSegments.join('/');
174
+ const newUrl = request.nextUrl.clone();
175
+ newUrl.pathname = \`/\${defaultLocale}\${restOfPath ? \`/\${restOfPath}\` : ''}\`;
176
+ return NextResponse.redirect(newUrl);
177
+ }
178
+
179
+ // Case: Multiple locales detected - remove duplicates
180
+ const localeSegments = pathSegments.filter(segment => isLocale(segment));
181
+ if (localeSegments.length > 1) {
182
+ const firstLocale = localeSegments[0];
183
+ const nonLocaleSegments = pathSegments.filter(
184
+ segment => !isLocale(segment)
185
+ );
186
+ const restOfPath = nonLocaleSegments.join('/');
187
+ const newUrl = request.nextUrl.clone();
188
+ newUrl.pathname = \`/\${firstLocale}\${restOfPath ? \`/\${restOfPath}\` : ''}\`;
189
+ return NextResponse.redirect(newUrl);
190
+ }
191
+
192
+ return intlMiddleware(request);
193
+ }
194
+
195
+ export const config = {
196
+ matcher: ['/((?!api|_next|_vercel|.*\\..*).*)'],
197
+ };`;
198
+
199
+ baseTemplates['lib/axios.ts'] = `import axios from 'axios';
200
+
201
+ const getCurrentLocale = (): string => {
202
+ if (typeof window !== 'undefined') {
203
+ const pathname = window.location.pathname;
204
+ const localeMatch = pathname.match(/(en|ar)/);
205
+ return localeMatch ? localeMatch[1] : 'en';
206
+ }
207
+ return 'en';
208
+ };
209
+
210
+ export const api = axios.create({
211
+ baseURL:
212
+ process.env.NEXT_PUBLIC_API_URL ||
213
+ process.env.NEXT_PUBLIC_BACKEND_URL ||
214
+ 'http://localhost:5000',
215
+ headers: {
216
+ 'Content-Type': 'application/json',
217
+ },
218
+ withCredentials: true,
219
+ });
220
+
221
+ api.interceptors.request.use(config => {
222
+ const currentLocale = getCurrentLocale();
223
+ config.headers['Accept-Language'] = currentLocale;
224
+ return config;
225
+ });
226
+
227
+ export default api;`;
228
+
229
+ baseTemplates['lib/locale.ts'] = `/**
230
+ * Get current locale from the URL path
231
+ * Extracts locale from /[locale]/... pattern
232
+ * @returns 'en' or 'ar'
233
+ */
234
+ export const getCurrentLocale = (): string => {
235
+ if (typeof window !== 'undefined') {
236
+ const pathname = window.location.pathname;
237
+ const localeMatch = pathname.match(/(en|ar)/);
238
+ return localeMatch ? localeMatch[1] : 'en';
239
+ }
240
+ return 'en';
241
+ };
242
+
243
+ /**
244
+ * Check if current locale is RTL (Arabic)
245
+ * @returns true if Arabic, false if English
246
+ */
247
+ export const isRTL = (): boolean => {
248
+ return getCurrentLocale() === 'ar';
249
+ };`;
250
+ } else {
251
+ // Non-i18n version of axios (simpler, without locale)
252
+ baseTemplates['lib/axios.ts'] = `import axios from 'axios';
253
+
254
+ export const api = axios.create({
255
+ baseURL:
256
+ process.env.NEXT_PUBLIC_API_URL ||
257
+ process.env.NEXT_PUBLIC_BACKEND_URL ||
258
+ 'http://localhost:5000',
259
+ headers: {
260
+ 'Content-Type': 'application/json',
261
+ },
262
+ withCredentials: true,
263
+ });
264
+
265
+ export default api;`;
266
+ }
267
+
268
+ return baseTemplates;
269
+ };