@promakeai/cli 0.1.1 → 0.1.2
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.
- package/dist/index.js +1 -1
- package/dist/registry/docs/google-adsense.md +38 -0
- package/dist/registry/docs/youtube-embed.md +42 -0
- package/dist/registry/google-adsense.json +43 -0
- package/dist/registry/header-mega.json +1 -1
- package/dist/registry/index.json +3 -1
- package/dist/registry/youtube-embed.json +40 -0
- package/package.json +2 -2
- package/template/.prettierrc +12 -1
- package/template/index.html +3 -3
- package/template/package.json +1 -1
- package/template/src/constants/constants.json +4 -4
- package/template/src/lib/env.ts +20 -0
- package/template/src/main.tsx +1 -0
package/dist/index.js
CHANGED
|
@@ -280,7 +280,7 @@ constants.json (full)`)),console.log(M.dim(JSON.stringify($.constantsConfig,null
|
|
|
280
280
|
`)),TR(),console.log(M.bold.cyan(`════════════════════════════════════════════════════════════════
|
|
281
281
|
`))});function TR(){console.log(M.bold.yellow(`
|
|
282
282
|
━━━ QUICK REFERENCE ━━━
|
|
283
|
-
`)),$1("promake create <name>","Create new project"),$1("promake add <modules...>","Add modules"),$1("promake add <page> --module-name <name>","Add page with custom name"),$1("promake remove <modules...>","Remove modules"),$1("promake sync","Sync from promake.json"),$1("promake theme --list","List theme options"),$1("promake theme --preset <name>","Apply color preset"),$1("promake list","List available modules"),$1("promake discover --json","Project info for AI"),$1("promake seo","Sync SEO meta tags"),$1("promake usage","Show this guide"),$1("promake <command> --help","Command-specific help"),console.log()}var PR=new E0("info").description("Show detailed information about a module").argument("<module>","Module name").option("--json","Output as JSON").action(async(D,F)=>{let X=H2(`Fetching ${D}...`).start();try{let J=await C1(D);if(X.stop(),!J)console.error(M.red(`Module not found: ${D}`)),process.exit(1);if(F.json){console.log(JSON.stringify(J,null,2));return}if(console.log(""),console.log(M.bold.cyan(J.name)),console.log(M.dim("─".repeat(J.name.length+4))),J.title)console.log(`${M.gray("Title:")} ${J.title}`);if(console.log(`${M.gray("Type:")} ${J.type.replace("registry:","")}`),J.description)console.log(`${M.gray("Description:")} ${J.description}`);if(J.registryDependencies?.length)console.log(""),console.log(M.gray("Dependencies:")),J.registryDependencies.forEach((Q)=>{let Y=!Q.includes("-")||["button","card","input","badge","dialog","sheet","dropdown-menu","accordion","tabs","select","checkbox","slider","separator","avatar","tooltip","popover","navigation-menu","scroll-area"].includes(Q)?M.dim("(shadcn)"):"";console.log(` ${M.yellow("•")} ${Q} ${Y}`)});if(J.dependencies?.length)console.log(""),console.log(M.gray("NPM Packages:")),J.dependencies.forEach((Q)=>{console.log(` ${M.blue("•")} ${Q}`)});if(J.files?.length)console.log(""),console.log(M.gray("Files:")),J.files.forEach((Q)=>{console.log(` ${M.dim("•")} ${Q.path}`)});if(J.exports){if(console.log(""),console.log(M.gray("Exports:")),J.exports.types?.length)console.log(` ${M.magenta("Types:")} ${J.exports.types.join(", ")}`);if(J.exports.variables?.length)console.log(` ${M.green("Components:")} ${J.exports.variables.join(", ")}`)}if(J.route)console.log(""),console.log(M.gray("Route:")),console.log(` ${M.cyan("Path:")} ${J.route.path}`),console.log(` ${M.cyan("Component:")} ${J.route.componentName}`);if(J.usage)console.log(""),console.log(M.gray("Usage:")),console.log(M.dim("─".repeat(40))),console.log(J.usage);console.log("")}catch(J){if(X.fail("Failed to fetch module info"),J instanceof Error)console.error(M.red(J.message));process.exit(1)}});var xR={name:"@promakeai/cli",version:"0.1.
|
|
283
|
+
`)),$1("promake create <name>","Create new project"),$1("promake add <modules...>","Add modules"),$1("promake add <page> --module-name <name>","Add page with custom name"),$1("promake remove <modules...>","Remove modules"),$1("promake sync","Sync from promake.json"),$1("promake theme --list","List theme options"),$1("promake theme --preset <name>","Apply color preset"),$1("promake list","List available modules"),$1("promake discover --json","Project info for AI"),$1("promake seo","Sync SEO meta tags"),$1("promake usage","Show this guide"),$1("promake <command> --help","Command-specific help"),console.log()}var PR=new E0("info").description("Show detailed information about a module").argument("<module>","Module name").option("--json","Output as JSON").action(async(D,F)=>{let X=H2(`Fetching ${D}...`).start();try{let J=await C1(D);if(X.stop(),!J)console.error(M.red(`Module not found: ${D}`)),process.exit(1);if(F.json){console.log(JSON.stringify(J,null,2));return}if(console.log(""),console.log(M.bold.cyan(J.name)),console.log(M.dim("─".repeat(J.name.length+4))),J.title)console.log(`${M.gray("Title:")} ${J.title}`);if(console.log(`${M.gray("Type:")} ${J.type.replace("registry:","")}`),J.description)console.log(`${M.gray("Description:")} ${J.description}`);if(J.registryDependencies?.length)console.log(""),console.log(M.gray("Dependencies:")),J.registryDependencies.forEach((Q)=>{let Y=!Q.includes("-")||["button","card","input","badge","dialog","sheet","dropdown-menu","accordion","tabs","select","checkbox","slider","separator","avatar","tooltip","popover","navigation-menu","scroll-area"].includes(Q)?M.dim("(shadcn)"):"";console.log(` ${M.yellow("•")} ${Q} ${Y}`)});if(J.dependencies?.length)console.log(""),console.log(M.gray("NPM Packages:")),J.dependencies.forEach((Q)=>{console.log(` ${M.blue("•")} ${Q}`)});if(J.files?.length)console.log(""),console.log(M.gray("Files:")),J.files.forEach((Q)=>{console.log(` ${M.dim("•")} ${Q.path}`)});if(J.exports){if(console.log(""),console.log(M.gray("Exports:")),J.exports.types?.length)console.log(` ${M.magenta("Types:")} ${J.exports.types.join(", ")}`);if(J.exports.variables?.length)console.log(` ${M.green("Components:")} ${J.exports.variables.join(", ")}`)}if(J.route)console.log(""),console.log(M.gray("Route:")),console.log(` ${M.cyan("Path:")} ${J.route.path}`),console.log(` ${M.cyan("Component:")} ${J.route.componentName}`);if(J.usage)console.log(""),console.log(M.gray("Usage:")),console.log(M.dim("─".repeat(40))),console.log(J.usage);console.log("")}catch(J){if(X.fail("Failed to fetch module info"),J instanceof Error)console.error(M.red(J.message));process.exit(1)}});var xR={name:"@promakeai/cli",version:"0.1.2",type:"module",bin:{promake:"dist/index.js"},files:["dist/index.js","dist/registry","template"],scripts:{dev:"bun run src/index.ts","dev:app":"cd dev && bun run dev","dev:populate":"bun run scripts/populate-dev.ts","dev:fresh":"bun run dev:populate && bun run dev:app","playground:create":"rm -rf playground && bun run dev -- create playground --template empty --pm bun","playground:reset":"bun run playground:create","playground:add":"cd playground && bun run ../src/index.ts add","playground:ecommerce":"rm -rf playground && bun run dev -- create playground --template ecommerce --pm bun",build:"bun run build:cli && bun run build:registry","build:cli":"bun build src/index.ts --outdir dist --target node --minify","build:registry":"bun run scripts/build-registry.ts",typecheck:"tsc --noEmit",prepublishOnly:"bun run build",test:"bun test","test:watch":"bun test --watch","test:coverage":"bun test --coverage",release:"bun run build && npm publish --access public"},dependencies:{"adm-zip":"^0.5.16",archiver:"^7.0.1",chalk:"^5.3.0",commander:"^12.1.0",culori:"^4.0.2","fs-extra":"^11.2.0",glob:"^11.0.0",ora:"^8.1.1",prompts:"^2.4.2"},devDependencies:{"@types/archiver":"^7.0.0","@types/bun":"^1.1.14","@types/culori":"^4.0.1","@types/fs-extra":"^11.0.4","@types/node":"^22.10.2","@types/prompts":"^2.4.9",typescript:"^5.7.2"}};var f2=new E0;f2.name("promake").description(`Modular React template CLI - Build React apps with pre-built components
|
|
284
284
|
|
|
285
285
|
Quick Start:
|
|
286
286
|
$ promake create my-app
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# Google AdSense
|
|
2
|
+
|
|
3
|
+
Google AdSense component for displaying responsive ads. Supports various ad formats including display, fluid, rectangle, vertical, and horizontal. Includes development placeholder and automatic script loading.
|
|
4
|
+
|
|
5
|
+
## Files
|
|
6
|
+
|
|
7
|
+
| Target | Type |
|
|
8
|
+
|--------|------|
|
|
9
|
+
| `$modules$/google-adsense/index.ts` | index |
|
|
10
|
+
| `$modules$/google-adsense/google-adsense.tsx` | component |
|
|
11
|
+
| `$modules$/google-adsense/lang/en.json` | lang |
|
|
12
|
+
| `$modules$/google-adsense/lang/tr.json` | lang |
|
|
13
|
+
|
|
14
|
+
## Exports
|
|
15
|
+
|
|
16
|
+
**Components/Functions:** `GoogleAdsense`
|
|
17
|
+
|
|
18
|
+
```typescript
|
|
19
|
+
import { GoogleAdsense } from '@/modules/google-adsense';
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## Usage
|
|
23
|
+
|
|
24
|
+
```
|
|
25
|
+
import { GoogleAdsense } from '@/modules/google-adsense';
|
|
26
|
+
|
|
27
|
+
// Basic usage
|
|
28
|
+
<GoogleAdsense slot="1234567890" />
|
|
29
|
+
|
|
30
|
+
// With all options
|
|
31
|
+
<GoogleAdsense
|
|
32
|
+
client="ca-pub-xxxxxxxx"
|
|
33
|
+
slot="1234567890"
|
|
34
|
+
format="auto"
|
|
35
|
+
responsive={true}
|
|
36
|
+
className="my-4"
|
|
37
|
+
/>
|
|
38
|
+
```
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# YouTube Embed
|
|
2
|
+
|
|
3
|
+
Responsive YouTube video embed component with customizable options. Supports autoplay, mute, loop, custom start time, and multiple aspect ratios. Includes loading state and error handling.
|
|
4
|
+
|
|
5
|
+
## Files
|
|
6
|
+
|
|
7
|
+
| Target | Type |
|
|
8
|
+
|--------|------|
|
|
9
|
+
| `$modules$/youtube-embed/index.ts` | index |
|
|
10
|
+
| `$modules$/youtube-embed/youtube-embed.tsx` | component |
|
|
11
|
+
| `$modules$/youtube-embed/lang/en.json` | lang |
|
|
12
|
+
| `$modules$/youtube-embed/lang/tr.json` | lang |
|
|
13
|
+
|
|
14
|
+
## Exports
|
|
15
|
+
|
|
16
|
+
**Components/Functions:** `YouTubeEmbed`
|
|
17
|
+
|
|
18
|
+
```typescript
|
|
19
|
+
import { YouTubeEmbed } from '@/modules/youtube-embed';
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## Usage
|
|
23
|
+
|
|
24
|
+
```
|
|
25
|
+
import { YouTubeEmbed } from '@/modules/youtube-embed';
|
|
26
|
+
|
|
27
|
+
// Basic usage
|
|
28
|
+
<YouTubeEmbed videoId="dQw4w9WgXcQ" />
|
|
29
|
+
|
|
30
|
+
// With all options
|
|
31
|
+
<YouTubeEmbed
|
|
32
|
+
videoId="dQw4w9WgXcQ"
|
|
33
|
+
title="Video Title"
|
|
34
|
+
autoplay={false}
|
|
35
|
+
mute={false}
|
|
36
|
+
loop={false}
|
|
37
|
+
controls={true}
|
|
38
|
+
start={30}
|
|
39
|
+
aspectRatio="16/9"
|
|
40
|
+
className="rounded-xl shadow-lg"
|
|
41
|
+
/>
|
|
42
|
+
```
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "google-adsense",
|
|
3
|
+
"type": "registry:component",
|
|
4
|
+
"title": "Google AdSense",
|
|
5
|
+
"description": "Google AdSense component for displaying responsive ads. Supports various ad formats including display, fluid, rectangle, vertical, and horizontal. Includes development placeholder and automatic script loading.",
|
|
6
|
+
"registryDependencies": [],
|
|
7
|
+
"usage": "import { GoogleAdsense } from '@/modules/google-adsense';\n\n// Basic usage\n<GoogleAdsense slot=\"1234567890\" />\n\n// With all options\n<GoogleAdsense\n client=\"ca-pub-xxxxxxxx\"\n slot=\"1234567890\"\n format=\"auto\"\n responsive={true}\n className=\"my-4\"\n/>",
|
|
8
|
+
"files": [
|
|
9
|
+
{
|
|
10
|
+
"path": "google-adsense/index.ts",
|
|
11
|
+
"type": "registry:index",
|
|
12
|
+
"target": "$modules$/google-adsense/index.ts",
|
|
13
|
+
"content": "export * from './google-adsense';\r\n"
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
"path": "google-adsense/google-adsense.tsx",
|
|
17
|
+
"type": "registry:component",
|
|
18
|
+
"target": "$modules$/google-adsense/google-adsense.tsx",
|
|
19
|
+
"content": "import { useEffect, useRef, useState } from \"react\";\r\nimport { useTranslation } from \"react-i18next\";\r\nimport { cn } from \"@/lib/utils\";\r\n\r\ndeclare global {\r\n interface Window {\r\n adsbygoogle: unknown[];\r\n }\r\n}\r\n\r\ninterface GoogleAdsenseProps {\r\n client?: string;\r\n slot: string;\r\n format?: \"auto\" | \"fluid\" | \"rectangle\" | \"vertical\" | \"horizontal\";\r\n responsive?: boolean;\r\n className?: string;\r\n style?: React.CSSProperties;\r\n testMode?: boolean;\r\n}\r\n\r\nconst ADSENSE_SCRIPT_ID = \"google-adsense-script\";\r\n\r\nexport function GoogleAdsense({\r\n client,\r\n slot,\r\n format = \"auto\",\r\n responsive = true,\r\n className,\r\n style,\r\n testMode = false,\r\n}: GoogleAdsenseProps) {\r\n const { t } = useTranslation(\"google-adsense\");\r\n const adRef = useRef<HTMLModElement>(null);\r\n const [isLoaded, setIsLoaded] = useState(false);\r\n const [hasError, setHasError] = useState(false);\r\n\r\n const adClient = client || import.meta.env.VITE_ADSENSE_CLIENT;\r\n const isDev = import.meta.env.DEV;\r\n\r\n // Load AdSense script once\r\n useEffect(() => {\r\n if (isDev && !testMode) return;\r\n if (!adClient) {\r\n setHasError(true);\r\n return;\r\n }\r\n\r\n const existingScript = document.getElementById(ADSENSE_SCRIPT_ID);\r\n if (existingScript) {\r\n setIsLoaded(true);\r\n return;\r\n }\r\n\r\n const script = document.createElement(\"script\");\r\n script.id = ADSENSE_SCRIPT_ID;\r\n script.src = `https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=${adClient}`;\r\n script.async = true;\r\n script.crossOrigin = \"anonymous\";\r\n\r\n script.onload = () => setIsLoaded(true);\r\n script.onerror = () => setHasError(true);\r\n\r\n document.head.appendChild(script);\r\n }, [adClient, isDev, testMode]);\r\n\r\n // Push ad when script is loaded\r\n useEffect(() => {\r\n if (!isLoaded || !adRef.current || (isDev && !testMode)) return;\r\n\r\n const adElement = adRef.current;\r\n if (adElement.dataset.adStatus) return; // Already initialized\r\n\r\n try {\r\n (window.adsbygoogle = window.adsbygoogle || []).push({});\r\n } catch (err) {\r\n console.error(\"AdSense error:\", err);\r\n setHasError(true);\r\n }\r\n }, [isLoaded, isDev, testMode]);\r\n\r\n // Development placeholder\r\n if (isDev && !testMode) {\r\n return (\r\n <div\r\n className={cn(\r\n \"flex items-center justify-center bg-muted/50 border-2 border-dashed border-muted-foreground/25 rounded-lg\",\r\n className\r\n )}\r\n style={{ minHeight: \"90px\", ...style }}\r\n >\r\n <div className=\"text-center p-4\">\r\n <p className=\"text-sm font-medium text-muted-foreground\">\r\n {t(\"devPlaceholder\", \"AdSense Placeholder\")}\r\n </p>\r\n <p className=\"text-xs text-muted-foreground/70 mt-1\">\r\n {t(\"devNote\", \"Ads will appear in production\")}\r\n </p>\r\n </div>\r\n </div>\r\n );\r\n }\r\n\r\n // Error state\r\n if (hasError || !adClient) {\r\n return (\r\n <div\r\n className={cn(\r\n \"flex items-center justify-center bg-muted/30 rounded-lg\",\r\n className\r\n )}\r\n style={{ minHeight: \"90px\", ...style }}\r\n >\r\n <p className=\"text-xs text-muted-foreground\">\r\n {t(\"error\", \"Ad could not be loaded\")}\r\n </p>\r\n </div>\r\n );\r\n }\r\n\r\n return (\r\n <ins\r\n ref={adRef}\r\n className={cn(\"adsbygoogle block\", className)}\r\n style={{ display: \"block\", minHeight: \"90px\", ...style }}\r\n data-ad-client={adClient}\r\n data-ad-slot={slot}\r\n data-ad-format={format}\r\n data-full-width-responsive={responsive ? \"true\" : \"false\"}\r\n {...(testMode && { \"data-adtest\": \"on\" })}\r\n />\r\n );\r\n}\r\n"
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
"path": "google-adsense/lang/en.json",
|
|
23
|
+
"type": "registry:lang",
|
|
24
|
+
"target": "$modules$/google-adsense/lang/en.json",
|
|
25
|
+
"content": "{\r\n \"devPlaceholder\": \"AdSense Placeholder\",\r\n \"devNote\": \"Ads will appear in production\",\r\n \"error\": \"Ad could not be loaded\"\r\n}\r\n"
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
"path": "google-adsense/lang/tr.json",
|
|
29
|
+
"type": "registry:lang",
|
|
30
|
+
"target": "$modules$/google-adsense/lang/tr.json",
|
|
31
|
+
"content": "{\r\n \"devPlaceholder\": \"AdSense Yer Tutucusu\",\r\n \"devNote\": \"Reklamlar production'da görünecek\",\r\n \"error\": \"Reklam yüklenemedi\"\r\n}\r\n"
|
|
32
|
+
}
|
|
33
|
+
],
|
|
34
|
+
"envVars": {
|
|
35
|
+
"VITE_ADSENSE_CLIENT": ""
|
|
36
|
+
},
|
|
37
|
+
"exports": {
|
|
38
|
+
"types": [],
|
|
39
|
+
"variables": [
|
|
40
|
+
"GoogleAdsense"
|
|
41
|
+
]
|
|
42
|
+
}
|
|
43
|
+
}
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
"path": "header-mega/header-mega.tsx",
|
|
24
24
|
"type": "registry:component",
|
|
25
25
|
"target": "$modules$/header-mega/header-mega.tsx",
|
|
26
|
-
"content": "import { Link } from \"react-router\";\r\nimport { Book, Menu, Sunset, Trees, Zap } from \"lucide-react\";\r\nimport { useTranslation } from \"react-i18next\";\r\nimport { cn } from \"@/lib/utils\";\r\nimport {\r\n Accordion,\r\n AccordionContent,\r\n AccordionItem,\r\n AccordionTrigger,\r\n} from \"@/components/ui/accordion\";\r\nimport { Button } from \"@/components/ui/button\";\r\nimport {\r\n NavigationMenu,\r\n NavigationMenuContent,\r\n NavigationMenuItem,\r\n NavigationMenuLink,\r\n NavigationMenuList,\r\n NavigationMenuTrigger,\r\n} from \"@/components/ui/navigation-menu\";\r\nimport {\r\n Sheet,\r\n SheetContent,\r\n SheetHeader,\r\n SheetTitle,\r\n SheetTrigger,\r\n} from \"@/components/ui/sheet\";\r\nimport constants from \"@/constants/constants.json\";\r\n\r\ninterface MenuItem {\r\n title: string;\r\n url: string;\r\n description?: string;\r\n icon?: React.ReactNode;\r\n items?: MenuItem[];\r\n}\r\n\r\ninterface HeaderMegaProps {\r\n className?: string;\r\n}\r\n\r\nexport function HeaderMega({ className }: HeaderMegaProps) {\r\n const { t } = useTranslation(\"header-mega\");\r\n\r\n const menu: MenuItem[] = [\r\n { title: t(\"home\", \"Home\"), url: \"/\" },\r\n {\r\n title: t(\"products\", \"Products\"),\r\n url: \"/products\",\r\n items: [\r\n {\r\n title: t(\"allProducts\", \"All Products\"),\r\n description: t(\r\n \"allProductsDesc\",\r\n \"Browse our complete product catalog\"\r\n ),\r\n icon: <Book className=\"h-5 w-5 shrink-0\" />,\r\n url: \"/products\",\r\n },\r\n {\r\n title: t(\"featured\", \"Featured\"),\r\n description: t(\r\n \"featuredDesc\",\r\n \"Our handpicked selection of top products\"\r\n ),\r\n icon: <Trees className=\"h-5 w-5 shrink-0\" />,\r\n url: \"/products?featured=true\",\r\n },\r\n {\r\n title: t(\"newArrivals\", \"New Arrivals\"),\r\n description: t(\"newArrivalsDesc\", \"Check out the latest additions\"),\r\n icon: <Sunset className=\"h-5 w-5 shrink-0\" />,\r\n url: \"/products?is_new=true\",\r\n },\r\n {\r\n title: t(\"onSale\", \"On Sale\"),\r\n description: t(\"onSaleDesc\", \"Great deals and special offers\"),\r\n icon: <Zap className=\"h-5 w-5 shrink-0\" />,\r\n url: \"/products?on_sale=true\",\r\n },\r\n ],\r\n },\r\n {\r\n title: t(\"company\", \"Company\"),\r\n url: \"#\",\r\n items: [\r\n {\r\n title: t(\"aboutUs\", \"About Us\"),\r\n description: t(\r\n \"aboutUsDesc\",\r\n \"Learn more about our story and mission\"\r\n ),\r\n icon: <Trees className=\"h-5 w-5 shrink-0\" />,\r\n url: \"/about\",\r\n },\r\n {\r\n title: t(\"contact\", \"Contact\"),\r\n description: t(\"contactDesc\", \"Get in touch with our team\"),\r\n icon: <Sunset className=\"h-5 w-5 shrink-0\" />,\r\n url: \"/contact\",\r\n },\r\n {\r\n title: t(\"blog\", \"Blog\"),\r\n description: t(\"blogDesc\", \"Read our latest articles and updates\"),\r\n icon: <Book className=\"h-5 w-5 shrink-0\" />,\r\n url: \"/blog\",\r\n },\r\n ],\r\n },\r\n ];\r\n\r\n const renderMenuItem = (item: MenuItem) => {\r\n if (item.items) {\r\n return (\r\n <NavigationMenuItem key={item.title}>\r\n <NavigationMenuTrigger>{item.title}</NavigationMenuTrigger>\r\n <NavigationMenuContent className=\"z-50 bg-popover text-popover-foreground\">\r\n {item.items.map((subItem) => (\r\n <NavigationMenuLink asChild key={subItem.title} className=\"w-80\">\r\n <SubMenuLink item={subItem} />\r\n </NavigationMenuLink>\r\n ))}\r\n </NavigationMenuContent>\r\n </NavigationMenuItem>\r\n );\r\n }\r\n\r\n return (\r\n <NavigationMenuItem key={item.title}>\r\n <NavigationMenuLink asChild>\r\n <Link\r\n to={item.url}\r\n className=\"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-muted hover:text-accent-foreground\"\r\n >\r\n {item.title}\r\n </Link>\r\n </NavigationMenuLink>\r\n </NavigationMenuItem>\r\n );\r\n };\r\n\r\n const renderMobileMenuItem = (item: MenuItem) => {\r\n if (item.items) {\r\n return (\r\n <AccordionItem\r\n key={item.title}\r\n value={item.title}\r\n className=\"border-b-0\"\r\n >\r\n <AccordionTrigger className=\"text-md py-0 font-semibold hover:no-underline\">\r\n {item.title}\r\n </AccordionTrigger>\r\n <AccordionContent className=\"mt-2\">\r\n {item.items.map((subItem) => (\r\n <SubMenuLink key={subItem.title} item={subItem} />\r\n ))}\r\n </AccordionContent>\r\n </AccordionItem>\r\n );\r\n }\r\n\r\n return (\r\n <Link key={item.title} to={item.url} className=\"text-md font-semibold\">\r\n {item.title}\r\n </Link>\r\n );\r\n };\r\n\r\n return (\r\n <header\r\n className={cn(\r\n \"relative z-50 py-4 border-b bg-background/95 backdrop-blur supports-[backdrop-filter]:bg-background/60\",\r\n className\r\n )}\r\n >\r\n <div className=\"container max-w-7xl mx-auto px-4\">\r\n {/* Desktop Menu */}\r\n <nav className=\"hidden items-center justify-between lg:flex\">\r\n <div className=\"flex items-center gap-6\">\r\n {/* Logo */}\r\n <Link to=\"/\" className=\"flex items-center gap-2\">\r\n <img\r\n src=\"/images/logo.png\"\r\n className=\"h-8 w-auto\"\r\n alt={constants.site.name}\r\n onError={(e) => {\r\n e.currentTarget.style.display = \"none\";\r\n }}\r\n />\r\n <span className=\"text-lg font-semibold tracking-tight\">\r\n {constants.site.name}\r\n </span>\r\n </Link>\r\n <div className=\"flex items-center\">\r\n <NavigationMenu>\r\n <NavigationMenuList>\r\n {menu.map((item) => renderMenuItem(item))}\r\n </NavigationMenuList>\r\n </NavigationMenu>\r\n </div>\r\n </div>\r\n <div className=\"flex gap-2\">\r\n <Button asChild variant=\"outline\" size=\"sm\">\r\n <Link to=\"/login\">{t(\"login\", \"Login\")}</Link>\r\n </Button>\r\n <Button asChild size=\"sm\">\r\n <Link to=\"/register\">{t(\"signup\", \"Sign up\")}</Link>\r\n </Button>\r\n </div>\r\n </nav>\r\n\r\n {/* Mobile Menu */}\r\n <div className=\"block lg:hidden\">\r\n <div className=\"flex items-center justify-between\">\r\n {/* Logo */}\r\n <Link to=\"/\" className=\"flex items-center gap-2\">\r\n <img\r\n src=\"/images/logo.png\"\r\n className=\"h-8 w-auto\"\r\n alt={constants.site.name}\r\n onError={(e) => {\r\n e.currentTarget.style.display = \"none\";\r\n }}\r\n />\r\n <span className=\"text-lg font-semibold tracking-tight\">\r\n {constants.site.name}\r\n </span>\r\n </Link>\r\n <Sheet>\r\n <SheetTrigger asChild>\r\n <Button variant=\"outline\" size=\"icon\">\r\n <Menu className=\"h-4 w-4\" />\r\n </Button>\r\n </SheetTrigger>\r\n <SheetContent className=\"z-50 overflow-y-auto px-6\">\r\n <SheetHeader>\r\n <SheetTitle>\r\n <Link to=\"/\" className=\"flex items-center gap-2\">\r\n <img\r\n src=\"/images/logo.png\"\r\n className=\"h-8 w-auto\"\r\n alt={constants.site.name}\r\n onError={(e) => {\r\n e.currentTarget.style.display = \"none\";\r\n }}\r\n />\r\n </Link>\r\n </SheetTitle>\r\n </SheetHeader>\r\n <div className=\"flex flex-col gap-6 p-4\">\r\n <Accordion\r\n type=\"single\"\r\n collapsible\r\n className=\"flex w-full flex-col gap-4\"\r\n >\r\n {menu.map((item) => renderMobileMenuItem(item))}\r\n </Accordion>\r\n\r\n <div className=\"flex flex-col gap-3\">\r\n <Button asChild variant=\"outline\">\r\n <Link to=\"/login\">{t(\"login\", \"Login\")}</Link>\r\n </Button>\r\n <Button asChild>\r\n <Link to=\"/register\">{t(\"signup\", \"Sign up\")}</Link>\r\n </Button>\r\n </div>\r\n </div>\r\n </SheetContent>\r\n </Sheet>\r\n </div>\r\n </div>\r\n </div>\r\n </header>\r\n );\r\n}\r\n\r\nfunction SubMenuLink({ item }: { item: MenuItem }) {\r\n return (\r\n <Link\r\n to={item.url}\r\n className=\"flex min-w-80 flex-row gap-4 rounded-md p-3 leading-none no-underline transition-colors outline-none select-none hover:bg-muted hover:text-accent-foreground\"\r\n >\r\n <div className=\"text-foreground\">{item.icon}</div>\r\n <div>\r\n <div className=\"text-sm font-semibold\">{item.title}</div>\r\n {item.description && (\r\n <p className=\"text-sm leading-snug text-muted-foreground\">\r\n {item.description}\r\n </p>\r\n )}\r\n </div>\r\n </Link>\r\n );\r\n}\r\n"
|
|
26
|
+
"content": "import { Link } from \"react-router\";\r\nimport { Book, Menu, Sunset, Trees, Zap } from \"lucide-react\";\r\nimport { useTranslation } from \"react-i18next\";\r\nimport { cn } from \"@/lib/utils\";\r\nimport {\r\n Accordion,\r\n AccordionContent,\r\n AccordionItem,\r\n AccordionTrigger,\r\n} from \"@/components/ui/accordion\";\r\nimport { Button } from \"@/components/ui/button\";\r\nimport {\r\n NavigationMenu,\r\n NavigationMenuContent,\r\n NavigationMenuItem,\r\n NavigationMenuLink,\r\n NavigationMenuList,\r\n NavigationMenuTrigger,\r\n} from \"@/components/ui/navigation-menu\";\r\nimport {\r\n Sheet,\r\n SheetContent,\r\n SheetHeader,\r\n SheetTitle,\r\n SheetTrigger,\r\n} from \"@/components/ui/sheet\";\r\nimport constants from \"@/constants/constants.json\";\r\n\r\ninterface MenuItem {\r\n title: string;\r\n url: string;\r\n description?: string;\r\n icon?: React.ReactNode;\r\n items?: MenuItem[];\r\n}\r\n\r\ninterface HeaderMegaProps {\r\n className?: string;\r\n}\r\n\r\nexport function HeaderMega({ className }: HeaderMegaProps) {\r\n const { t } = useTranslation(\"header-mega\");\r\n\r\n const menu: MenuItem[] = [\r\n { title: t(\"home\", \"Home\"), url: \"/\" },\r\n {\r\n title: t(\"products\", \"Products\"),\r\n url: \"/products\",\r\n items: [\r\n {\r\n title: t(\"allProducts\", \"All Products\"),\r\n description: t(\r\n \"allProductsDesc\",\r\n \"Browse our complete product catalog\"\r\n ),\r\n icon: <Book className=\"h-5 w-5 shrink-0\" />,\r\n url: \"/products\",\r\n },\r\n {\r\n title: t(\"featured\", \"Featured\"),\r\n description: t(\r\n \"featuredDesc\",\r\n \"Our handpicked selection of top products\"\r\n ),\r\n icon: <Trees className=\"h-5 w-5 shrink-0\" />,\r\n url: \"/products?featured=true\",\r\n },\r\n {\r\n title: t(\"newArrivals\", \"New Arrivals\"),\r\n description: t(\"newArrivalsDesc\", \"Check out the latest additions\"),\r\n icon: <Sunset className=\"h-5 w-5 shrink-0\" />,\r\n url: \"/products?is_new=true\",\r\n },\r\n {\r\n title: t(\"onSale\", \"On Sale\"),\r\n description: t(\"onSaleDesc\", \"Great deals and special offers\"),\r\n icon: <Zap className=\"h-5 w-5 shrink-0\" />,\r\n url: \"/products?on_sale=true\",\r\n },\r\n ],\r\n },\r\n {\r\n title: t(\"company\", \"Company\"),\r\n url: \"#\",\r\n items: [\r\n {\r\n title: t(\"aboutUs\", \"About Us\"),\r\n description: t(\r\n \"aboutUsDesc\",\r\n \"Learn more about our story and mission\"\r\n ),\r\n icon: <Trees className=\"h-5 w-5 shrink-0\" />,\r\n url: \"/about\",\r\n },\r\n {\r\n title: t(\"contact\", \"Contact\"),\r\n description: t(\"contactDesc\", \"Get in touch with our team\"),\r\n icon: <Sunset className=\"h-5 w-5 shrink-0\" />,\r\n url: \"/contact\",\r\n },\r\n {\r\n title: t(\"blog\", \"Blog\"),\r\n description: t(\"blogDesc\", \"Read our latest articles and updates\"),\r\n icon: <Book className=\"h-5 w-5 shrink-0\" />,\r\n url: \"/blog\",\r\n },\r\n ],\r\n },\r\n ];\r\n\r\n const renderMenuItem = (item: MenuItem) => {\r\n if (item.items) {\r\n return (\r\n <NavigationMenuItem key={item.title}>\r\n <NavigationMenuTrigger>{item.title}</NavigationMenuTrigger>\r\n <NavigationMenuContent className=\"z-50 bg-popover text-popover-foreground\">\r\n {item.items.map((subItem) => (\r\n <NavigationMenuLink asChild key={subItem.title} className=\"w-80\">\r\n <SubMenuLink item={subItem} />\r\n </NavigationMenuLink>\r\n ))}\r\n </NavigationMenuContent>\r\n </NavigationMenuItem>\r\n );\r\n }\r\n\r\n return (\r\n <NavigationMenuItem key={item.title}>\r\n <NavigationMenuLink asChild>\r\n <Link\r\n to={item.url}\r\n className=\"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-muted hover:text-accent-foreground\"\r\n >\r\n {item.title}\r\n </Link>\r\n </NavigationMenuLink>\r\n </NavigationMenuItem>\r\n );\r\n };\r\n\r\n const renderMobileMenuItem = (item: MenuItem) => {\r\n if (item.items) {\r\n return (\r\n <AccordionItem\r\n key={item.title}\r\n value={item.title}\r\n className=\"border-b-0\"\r\n >\r\n <AccordionTrigger className=\"text-md py-0 font-semibold hover:no-underline\">\r\n {item.title}\r\n </AccordionTrigger>\r\n <AccordionContent className=\"mt-2\">\r\n {item.items.map((subItem) => (\r\n <SubMenuLink key={subItem.title} item={subItem} />\r\n ))}\r\n </AccordionContent>\r\n </AccordionItem>\r\n );\r\n }\r\n\r\n return (\r\n <Link key={item.title} to={item.url} className=\"text-md font-semibold\">\r\n {item.title}\r\n </Link>\r\n );\r\n };\r\n\r\n return (\r\n <header\r\n className={cn(\r\n \"relative z-50 py-4 border-b bg-background/95 backdrop-blur supports-[backdrop-filter]:bg-background/60 overflow-visible\",\r\n className\r\n )}\r\n >\r\n <div className=\"container max-w-7xl mx-auto px-4\">\r\n {/* Desktop Menu */}\r\n <nav className=\"hidden items-center justify-between lg:flex overflow-visible\">\r\n <div className=\"flex items-center gap-6\">\r\n {/* Logo */}\r\n <Link to=\"/\" className=\"flex items-center gap-2\">\r\n <img\r\n src=\"/images/logo.png\"\r\n className=\"h-8 w-auto\"\r\n alt={constants.site.name}\r\n onError={(e) => {\r\n e.currentTarget.style.display = \"none\";\r\n }}\r\n />\r\n <span className=\"text-lg font-semibold tracking-tight\">\r\n {constants.site.name}\r\n </span>\r\n </Link>\r\n <div className=\"flex items-center\">\r\n <NavigationMenu>\r\n <NavigationMenuList>\r\n {menu.map((item) => renderMenuItem(item))}\r\n </NavigationMenuList>\r\n </NavigationMenu>\r\n </div>\r\n </div>\r\n <div className=\"flex gap-2\">\r\n <Button asChild variant=\"outline\" size=\"sm\">\r\n <Link to=\"/login\">{t(\"login\", \"Login\")}</Link>\r\n </Button>\r\n <Button asChild size=\"sm\">\r\n <Link to=\"/register\">{t(\"signup\", \"Sign up\")}</Link>\r\n </Button>\r\n </div>\r\n </nav>\r\n\r\n {/* Mobile Menu */}\r\n <div className=\"block lg:hidden\">\r\n <div className=\"flex items-center justify-between\">\r\n {/* Logo */}\r\n <Link to=\"/\" className=\"flex items-center gap-2\">\r\n <img\r\n src=\"/images/logo.png\"\r\n className=\"h-8 w-auto\"\r\n alt={constants.site.name}\r\n onError={(e) => {\r\n e.currentTarget.style.display = \"none\";\r\n }}\r\n />\r\n <span className=\"text-lg font-semibold tracking-tight\">\r\n {constants.site.name}\r\n </span>\r\n </Link>\r\n <Sheet>\r\n <SheetTrigger asChild>\r\n <Button variant=\"outline\" size=\"icon\">\r\n <Menu className=\"h-4 w-4\" />\r\n </Button>\r\n </SheetTrigger>\r\n <SheetContent className=\"z-50 overflow-y-auto px-6\">\r\n <SheetHeader>\r\n <SheetTitle>\r\n <Link to=\"/\" className=\"flex items-center gap-2\">\r\n <img\r\n src=\"/images/logo.png\"\r\n className=\"h-8 w-auto\"\r\n alt={constants.site.name}\r\n onError={(e) => {\r\n e.currentTarget.style.display = \"none\";\r\n }}\r\n />\r\n </Link>\r\n </SheetTitle>\r\n </SheetHeader>\r\n <div className=\"flex flex-col gap-6 p-4\">\r\n <Accordion\r\n type=\"single\"\r\n collapsible\r\n className=\"flex w-full flex-col gap-4\"\r\n >\r\n {menu.map((item) => renderMobileMenuItem(item))}\r\n </Accordion>\r\n\r\n <div className=\"flex flex-col gap-3\">\r\n <Button asChild variant=\"outline\">\r\n <Link to=\"/login\">{t(\"login\", \"Login\")}</Link>\r\n </Button>\r\n <Button asChild>\r\n <Link to=\"/register\">{t(\"signup\", \"Sign up\")}</Link>\r\n </Button>\r\n </div>\r\n </div>\r\n </SheetContent>\r\n </Sheet>\r\n </div>\r\n </div>\r\n </div>\r\n </header>\r\n );\r\n}\r\n\r\nfunction SubMenuLink({ item }: { item: MenuItem }) {\r\n return (\r\n <Link\r\n to={item.url}\r\n className=\"flex min-w-80 flex-row gap-4 rounded-md p-3 leading-none no-underline transition-colors outline-none select-none hover:bg-muted hover:text-accent-foreground\"\r\n >\r\n <div className=\"text-foreground\">{item.icon}</div>\r\n <div>\r\n <div className=\"text-sm font-semibold\">{item.title}</div>\r\n {item.description && (\r\n <p className=\"text-sm leading-snug text-muted-foreground\">\r\n {item.description}\r\n </p>\r\n )}\r\n </div>\r\n </Link>\r\n );\r\n}\r\n"
|
|
27
27
|
},
|
|
28
28
|
{
|
|
29
29
|
"path": "header-mega/lang/en.json",
|
package/dist/registry/index.json
CHANGED
|
@@ -40,6 +40,7 @@
|
|
|
40
40
|
"footer-detailed",
|
|
41
41
|
"footer-minimal",
|
|
42
42
|
"forgot-password-page",
|
|
43
|
+
"google-adsense",
|
|
43
44
|
"google-map",
|
|
44
45
|
"header-centered-pill",
|
|
45
46
|
"header-ecommerce",
|
|
@@ -89,5 +90,6 @@
|
|
|
89
90
|
"testimonials-carousel",
|
|
90
91
|
"testimonials-grid",
|
|
91
92
|
"timeline-section",
|
|
92
|
-
"video-hero"
|
|
93
|
+
"video-hero",
|
|
94
|
+
"youtube-embed"
|
|
93
95
|
]
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "youtube-embed",
|
|
3
|
+
"type": "registry:component",
|
|
4
|
+
"title": "YouTube Embed",
|
|
5
|
+
"description": "Responsive YouTube video embed component with customizable options. Supports autoplay, mute, loop, custom start time, and multiple aspect ratios. Includes loading state and error handling.",
|
|
6
|
+
"registryDependencies": [],
|
|
7
|
+
"usage": "import { YouTubeEmbed } from '@/modules/youtube-embed';\n\n// Basic usage\n<YouTubeEmbed videoId=\"dQw4w9WgXcQ\" />\n\n// With all options\n<YouTubeEmbed\n videoId=\"dQw4w9WgXcQ\"\n title=\"Video Title\"\n autoplay={false}\n mute={false}\n loop={false}\n controls={true}\n start={30}\n aspectRatio=\"16/9\"\n className=\"rounded-xl shadow-lg\"\n/>",
|
|
8
|
+
"files": [
|
|
9
|
+
{
|
|
10
|
+
"path": "youtube-embed/index.ts",
|
|
11
|
+
"type": "registry:index",
|
|
12
|
+
"target": "$modules$/youtube-embed/index.ts",
|
|
13
|
+
"content": "export * from './youtube-embed';\r\n"
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
"path": "youtube-embed/youtube-embed.tsx",
|
|
17
|
+
"type": "registry:component",
|
|
18
|
+
"target": "$modules$/youtube-embed/youtube-embed.tsx",
|
|
19
|
+
"content": "import { useState } from \"react\";\r\nimport { useTranslation } from \"react-i18next\";\r\nimport { cn } from \"@/lib/utils\";\r\nimport { Play } from \"lucide-react\";\r\n\r\ninterface YouTubeEmbedProps {\r\n videoId: string;\r\n title?: string;\r\n autoplay?: boolean;\r\n mute?: boolean;\r\n loop?: boolean;\r\n controls?: boolean;\r\n start?: number;\r\n aspectRatio?: \"16/9\" | \"4/3\" | \"1/1\";\r\n className?: string;\r\n}\r\n\r\nexport function YouTubeEmbed({\r\n videoId,\r\n title,\r\n autoplay = false,\r\n mute = false,\r\n loop = false,\r\n controls = true,\r\n start,\r\n aspectRatio = \"16/9\",\r\n className,\r\n}: YouTubeEmbedProps) {\r\n const { t } = useTranslation(\"youtube-embed\");\r\n const [isLoading, setIsLoading] = useState(true);\r\n const [hasError, setHasError] = useState(false);\r\n\r\n // Validate videoId\r\n const isValidVideoId = /^[a-zA-Z0-9_-]{11}$/.test(videoId);\r\n\r\n // Build embed URL with parameters\r\n const buildEmbedUrl = () => {\r\n const params = new URLSearchParams();\r\n\r\n if (autoplay) params.set(\"autoplay\", \"1\");\r\n if (mute) params.set(\"mute\", \"1\");\r\n if (loop) {\r\n params.set(\"loop\", \"1\");\r\n params.set(\"playlist\", videoId);\r\n }\r\n if (!controls) params.set(\"controls\", \"0\");\r\n if (start) params.set(\"start\", start.toString());\r\n\r\n params.set(\"rel\", \"0\");\r\n\r\n const queryString = params.toString();\r\n return `https://www.youtube.com/embed/${videoId}${queryString ? `?${queryString}` : \"\"}`;\r\n };\r\n\r\n const handleLoad = () => {\r\n setIsLoading(false);\r\n };\r\n\r\n const handleError = () => {\r\n setIsLoading(false);\r\n setHasError(true);\r\n };\r\n\r\n // Invalid videoId\r\n if (!isValidVideoId) {\r\n return (\r\n <div\r\n className={cn(\r\n \"flex flex-col items-center justify-center bg-muted rounded-lg border\",\r\n className\r\n )}\r\n style={{ aspectRatio }}\r\n >\r\n <Play className=\"h-12 w-12 text-muted-foreground mb-3\" />\r\n <p className=\"text-sm text-muted-foreground\">\r\n {t(\"invalidId\", \"Invalid video ID\")}\r\n </p>\r\n </div>\r\n );\r\n }\r\n\r\n // Error state\r\n if (hasError) {\r\n return (\r\n <div\r\n className={cn(\r\n \"flex flex-col items-center justify-center bg-muted rounded-lg border\",\r\n className\r\n )}\r\n style={{ aspectRatio }}\r\n >\r\n <Play className=\"h-12 w-12 text-muted-foreground mb-3\" />\r\n <p className=\"text-sm text-muted-foreground\">\r\n {t(\"error\", \"Failed to load video\")}\r\n </p>\r\n <a\r\n href={`https://www.youtube.com/watch?v=${videoId}`}\r\n target=\"_blank\"\r\n rel=\"noopener noreferrer\"\r\n className=\"text-sm text-primary hover:underline mt-2\"\r\n >\r\n {t(\"watchOnYouTube\", \"Watch on YouTube\")}\r\n </a>\r\n </div>\r\n );\r\n }\r\n\r\n return (\r\n <div\r\n className={cn(\"relative rounded-lg overflow-hidden\", className)}\r\n style={{ aspectRatio }}\r\n >\r\n {isLoading && (\r\n <div className=\"absolute inset-0 flex items-center justify-center bg-muted\">\r\n <div className=\"flex flex-col items-center gap-3\">\r\n <div className=\"h-8 w-8 animate-spin rounded-full border-4 border-primary border-t-transparent\" />\r\n <span className=\"text-sm text-muted-foreground\">\r\n {t(\"loading\", \"Loading video...\")}\r\n </span>\r\n </div>\r\n </div>\r\n )}\r\n <iframe\r\n src={buildEmbedUrl()}\r\n width=\"100%\"\r\n height=\"100%\"\r\n style={{ border: 0 }}\r\n allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share\"\r\n allowFullScreen\r\n loading=\"lazy\"\r\n title={title || t(\"defaultTitle\", \"YouTube video player\")}\r\n onLoad={handleLoad}\r\n onError={handleError}\r\n className={cn(\"absolute inset-0\", isLoading && \"invisible\")}\r\n />\r\n </div>\r\n );\r\n}\r\n"
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
"path": "youtube-embed/lang/en.json",
|
|
23
|
+
"type": "registry:lang",
|
|
24
|
+
"target": "$modules$/youtube-embed/lang/en.json",
|
|
25
|
+
"content": "{\r\n \"loading\": \"Loading video...\",\r\n \"error\": \"Failed to load video\",\r\n \"invalidId\": \"Invalid video ID\",\r\n \"watchOnYouTube\": \"Watch on YouTube\",\r\n \"defaultTitle\": \"YouTube video player\"\r\n}\r\n"
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
"path": "youtube-embed/lang/tr.json",
|
|
29
|
+
"type": "registry:lang",
|
|
30
|
+
"target": "$modules$/youtube-embed/lang/tr.json",
|
|
31
|
+
"content": "{\r\n \"loading\": \"Video yükleniyor...\",\r\n \"error\": \"Video yüklenemedi\",\r\n \"invalidId\": \"Geçersiz video ID\",\r\n \"watchOnYouTube\": \"YouTube'da izle\",\r\n \"defaultTitle\": \"YouTube video oynatıcı\"\r\n}\r\n"
|
|
32
|
+
}
|
|
33
|
+
],
|
|
34
|
+
"exports": {
|
|
35
|
+
"types": [],
|
|
36
|
+
"variables": [
|
|
37
|
+
"YouTubeEmbed"
|
|
38
|
+
]
|
|
39
|
+
}
|
|
40
|
+
}
|
package/package.json
CHANGED
package/template/.prettierrc
CHANGED
package/template/index.html
CHANGED
|
@@ -248,10 +248,10 @@
|
|
|
248
248
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
249
249
|
|
|
250
250
|
<!-- SEO -->
|
|
251
|
-
<title>Site Name
|
|
251
|
+
<title>Site Name</title>
|
|
252
252
|
<meta
|
|
253
253
|
name="description"
|
|
254
|
-
content="
|
|
254
|
+
content="Your site description"
|
|
255
255
|
/>
|
|
256
256
|
<meta name="author" content="Site Name" />
|
|
257
257
|
|
|
@@ -259,7 +259,7 @@
|
|
|
259
259
|
<meta property="og:title" content="Site Name" />
|
|
260
260
|
<meta
|
|
261
261
|
property="og:description"
|
|
262
|
-
content="
|
|
262
|
+
content="Your site description"
|
|
263
263
|
/>
|
|
264
264
|
<meta property="og:type" content="website" />
|
|
265
265
|
<meta property="og:image" content="" />
|
package/template/package.json
CHANGED
|
@@ -26,11 +26,11 @@
|
|
|
26
26
|
"timeout": 30000
|
|
27
27
|
},
|
|
28
28
|
"seo": {
|
|
29
|
-
"title": "Site Name
|
|
30
|
-
"description": "
|
|
29
|
+
"title": "Site Name",
|
|
30
|
+
"description": "Your site description",
|
|
31
31
|
"author": "Site Name",
|
|
32
32
|
"ogTitle": "Site Name",
|
|
33
|
-
"ogDescription": "
|
|
33
|
+
"ogDescription": "Your site description",
|
|
34
34
|
"ogImage": "",
|
|
35
35
|
"twitterSite": "",
|
|
36
36
|
"twitterImage": "",
|
|
@@ -61,4 +61,4 @@
|
|
|
61
61
|
"instagram": "",
|
|
62
62
|
"linkedin": ""
|
|
63
63
|
}
|
|
64
|
-
}
|
|
64
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
// Environment değişkenlerini window'a export et
|
|
2
|
+
declare global {
|
|
3
|
+
interface Window {
|
|
4
|
+
ENV: {
|
|
5
|
+
[key: string]: any;
|
|
6
|
+
};
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
window.ENV = {
|
|
11
|
+
...Object.keys(import.meta.env)
|
|
12
|
+
.filter((key) => key.startsWith('VITE_'))
|
|
13
|
+
.reduce(
|
|
14
|
+
(acc, key) => ({
|
|
15
|
+
...acc,
|
|
16
|
+
[key]: import.meta.env[key],
|
|
17
|
+
}),
|
|
18
|
+
{},
|
|
19
|
+
),
|
|
20
|
+
};
|