@djangocfg/nextjs 2.1.6 → 2.1.8
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/README.md +3 -3
- package/dist/ai/cli.d.mts +1 -0
- package/dist/ai/cli.mjs +173 -0
- package/dist/ai/cli.mjs.map +1 -0
- package/dist/ai/index.d.mts +81 -0
- package/dist/ai/index.mjs +139 -0
- package/dist/ai/index.mjs.map +1 -0
- package/dist/config/index.d.mts +360 -0
- package/dist/config/index.mjs +1363 -0
- package/dist/config/index.mjs.map +1 -0
- package/dist/constants-HezbftFb.d.mts +10 -0
- package/dist/contact/index.d.mts +47 -0
- package/dist/contact/index.mjs +98 -0
- package/dist/contact/index.mjs.map +1 -0
- package/dist/contact/route.d.mts +35 -0
- package/dist/contact/route.mjs +99 -0
- package/dist/contact/route.mjs.map +1 -0
- package/dist/health/index.d.mts +43 -0
- package/dist/health/index.mjs +38 -0
- package/dist/health/index.mjs.map +1 -0
- package/dist/index.d.mts +19 -0
- package/dist/index.mjs +2565 -0
- package/dist/index.mjs.map +1 -0
- package/dist/navigation/index.d.mts +89 -0
- package/dist/navigation/index.mjs +63 -0
- package/dist/navigation/index.mjs.map +1 -0
- package/dist/og-image/components/index.d.mts +59 -0
- package/dist/og-image/components/index.mjs +325 -0
- package/dist/og-image/components/index.mjs.map +1 -0
- package/dist/og-image/index.d.mts +112 -0
- package/dist/og-image/index.mjs +823 -0
- package/dist/og-image/index.mjs.map +1 -0
- package/dist/og-image/utils/index.d.mts +302 -0
- package/dist/og-image/utils/index.mjs +317 -0
- package/dist/og-image/utils/index.mjs.map +1 -0
- package/dist/sitemap/index.d.mts +66 -0
- package/dist/sitemap/index.mjs +76 -0
- package/dist/sitemap/index.mjs.map +1 -0
- package/dist/types-CwhXnEbK.d.mts +30 -0
- package/package.json +7 -6
- package/src/ai/cli.ts +29 -29
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/navigation/utils.ts"],"sourcesContent":["/**\n * Navigation Utilities\n *\n * Common utilities for route definitions and navigation\n */\n\nimport type { RouteDefinition, RouteMetadata, MenuItem } from './types';\n\n// ─────────────────────────────────────────────────────────────────────────\n// Route Definition Helper\n// ─────────────────────────────────────────────────────────────────────────\n\n/**\n * Define a route with metadata\n *\n * IMPORTANT: Next.js automatically handles basePath for <Link> components when\n * basePath is set in next.config.ts. We should NOT add basePath manually here,\n * as it would cause double-prefixing in static builds.\n *\n * @param path - Route path (e.g., '/dashboard', '/admin/users')\n * @param metadata - Route metadata (label, icon, etc.)\n */\nexport function defineRoute(\n path: string,\n metadata: RouteMetadata,\n): RouteDefinition {\n // Always return path as-is. Next.js will handle basePath automatically\n // for <Link> components when basePath is set in next.config.ts\n return {\n path,\n metadata,\n };\n}\n\n// ─────────────────────────────────────────────────────────────────────────\n// Route Guards\n// ─────────────────────────────────────────────────────────────────────────\n\n/**\n * Get redirect path for unauthenticated users\n */\nexport function getUnauthenticatedRedirect(\n path: string,\n authPath: string = '/auth',\n): string | null {\n if (path.startsWith('/private') || path.startsWith('/admin')) {\n // Return path as-is. Next.js will handle basePath automatically for router.push()\n // when basePath is set in next.config.ts\n return authPath;\n }\n return null;\n}\n\n/**\n * Get redirect path to auth page\n */\nexport function redirectToAuth(\n authPath: string = '/auth',\n): string {\n // Return path as-is. Next.js will handle basePath automatically for router.push()\n // when basePath is set in next.config.ts\n return authPath;\n}\n\n// ─────────────────────────────────────────────────────────────────────────\n// Route Lookup\n// ─────────────────────────────────────────────────────────────────────────\n\nexport function findRoute(\n routes: RouteDefinition[],\n path: string\n): RouteDefinition | undefined {\n return routes.find((r) => r.path === path);\n}\n\nexport function findRouteByPattern(\n routes: RouteDefinition[],\n path: string\n): RouteDefinition | undefined {\n const exact = findRoute(routes, path);\n if (exact) return exact;\n\n const segments = path.split('/').filter(Boolean);\n for (let i = segments.length; i > 0; i--) {\n const parentPath = '/' + segments.slice(0, i).join('/');\n const parent = findRoute(routes, parentPath);\n if (parent) return parent;\n }\n\n return undefined;\n}\n\n// ─────────────────────────────────────────────────────────────────────────\n// Page Title\n// ─────────────────────────────────────────────────────────────────────────\n\nexport function getPageTitle(\n routes: RouteDefinition[],\n path: string,\n fallback = 'Dashboard'\n): string {\n const route = findRouteByPattern(routes, path);\n return route?.metadata.label || fallback;\n}\n\n// ─────────────────────────────────────────────────────────────────────────\n// Active Route\n// ─────────────────────────────────────────────────────────────────────────\n\n/**\n * Check if a route is active\n *\n * @param current - Current pathname\n * @param target - Target route path to check\n * @param allRoutes - Optional array of all routes to prevent parent paths from being active when child paths are active\n * @returns true if the route is active\n */\nexport function isActive(\n current: string,\n target: string,\n allRoutes?: RouteDefinition[]\n): boolean {\n const matches =\n current === target || (target !== '/' && current.startsWith(target + '/'));\n\n // If allRoutes is provided, check for more specific paths\n if (matches && allRoutes) {\n return !allRoutes.some(\n (otherRoute) =>\n otherRoute.path !== target &&\n otherRoute.path.startsWith(target + '/') &&\n (current === otherRoute.path ||\n current.startsWith(otherRoute.path + '/'))\n );\n }\n\n return matches;\n}\n\n// ─────────────────────────────────────────────────────────────────────────\n// Menu Generation Helper\n// ─────────────────────────────────────────────────────────────────────────\n\n/**\n * Filter and convert routes to menu items\n */\nexport function routesToMenuItems(\n routes: RouteDefinition[],\n groupName: string\n): MenuItem[] {\n return routes\n .filter(\n (r) =>\n r.metadata.group === groupName &&\n r.metadata.icon &&\n (r.metadata.show === undefined || r.metadata.show === true)\n )\n .sort((a, b) => (a.metadata.order || 0) - (b.metadata.order || 0))\n .map((r) => ({\n path: r.path,\n label: r.metadata.label,\n icon: r.metadata.icon!,\n }));\n}\n\n"],"mappings":";AAsBO,SAAS,YACd,MACA,UACiB;AAGjB,SAAO;AAAA,IACL;AAAA,IACA;AAAA,EACF;AACF;AASO,SAAS,2BACd,MACA,WAAmB,SACJ;AACf,MAAI,KAAK,WAAW,UAAU,KAAK,KAAK,WAAW,QAAQ,GAAG;AAG5D,WAAO;AAAA,EACT;AACA,SAAO;AACT;AAKO,SAAS,eACd,WAAmB,SACX;AAGR,SAAO;AACT;AAMO,SAAS,UACd,QACA,MAC6B;AAC7B,SAAO,OAAO,KAAK,CAAC,MAAM,EAAE,SAAS,IAAI;AAC3C;AAEO,SAAS,mBACd,QACA,MAC6B;AAC7B,QAAM,QAAQ,UAAU,QAAQ,IAAI;AACpC,MAAI,MAAO,QAAO;AAElB,QAAM,WAAW,KAAK,MAAM,GAAG,EAAE,OAAO,OAAO;AAC/C,WAAS,IAAI,SAAS,QAAQ,IAAI,GAAG,KAAK;AACxC,UAAM,aAAa,MAAM,SAAS,MAAM,GAAG,CAAC,EAAE,KAAK,GAAG;AACtD,UAAM,SAAS,UAAU,QAAQ,UAAU;AAC3C,QAAI,OAAQ,QAAO;AAAA,EACrB;AAEA,SAAO;AACT;AAMO,SAAS,aACd,QACA,MACA,WAAW,aACH;AACR,QAAM,QAAQ,mBAAmB,QAAQ,IAAI;AAC7C,SAAO,OAAO,SAAS,SAAS;AAClC;AAcO,SAAS,SACd,SACA,QACA,WACS;AACT,QAAM,UACJ,YAAY,UAAW,WAAW,OAAO,QAAQ,WAAW,SAAS,GAAG;AAG1E,MAAI,WAAW,WAAW;AACxB,WAAO,CAAC,UAAU;AAAA,MAChB,CAAC,eACC,WAAW,SAAS,UACpB,WAAW,KAAK,WAAW,SAAS,GAAG,MACtC,YAAY,WAAW,QACtB,QAAQ,WAAW,WAAW,OAAO,GAAG;AAAA,IAC9C;AAAA,EACF;AAEA,SAAO;AACT;AASO,SAAS,kBACd,QACA,WACY;AACZ,SAAO,OACJ;AAAA,IACC,CAAC,MACC,EAAE,SAAS,UAAU,aACrB,EAAE,SAAS,SACV,EAAE,SAAS,SAAS,UAAa,EAAE,SAAS,SAAS;AAAA,EAC1D,EACC,KAAK,CAAC,GAAG,OAAO,EAAE,SAAS,SAAS,MAAM,EAAE,SAAS,SAAS,EAAE,EAChE,IAAI,CAAC,OAAO;AAAA,IACX,MAAM,EAAE;AAAA,IACR,OAAO,EAAE,SAAS;AAAA,IAClB,MAAM,EAAE,SAAS;AAAA,EACnB,EAAE;AACN;","names":[]}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { ReactElement } from 'react';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* OG Image Types
|
|
5
|
+
*
|
|
6
|
+
* Shared types for OG image templates and handlers
|
|
7
|
+
* This file is safe to import in client components
|
|
8
|
+
*/
|
|
9
|
+
interface OgImageTemplateProps {
|
|
10
|
+
title: string;
|
|
11
|
+
description?: string;
|
|
12
|
+
subtitle?: string;
|
|
13
|
+
siteName?: string;
|
|
14
|
+
logo?: string;
|
|
15
|
+
showLogo?: boolean;
|
|
16
|
+
showSiteName?: boolean;
|
|
17
|
+
backgroundType?: 'gradient' | 'solid';
|
|
18
|
+
gradientStart?: string;
|
|
19
|
+
gradientEnd?: string;
|
|
20
|
+
backgroundColor?: string;
|
|
21
|
+
titleSize?: number;
|
|
22
|
+
titleWeight?: number;
|
|
23
|
+
titleColor?: string;
|
|
24
|
+
descriptionSize?: number;
|
|
25
|
+
descriptionColor?: string;
|
|
26
|
+
siteNameSize?: number;
|
|
27
|
+
siteNameColor?: string;
|
|
28
|
+
padding?: number;
|
|
29
|
+
logoSize?: number;
|
|
30
|
+
devMode?: boolean;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Default OG Image Template
|
|
35
|
+
*
|
|
36
|
+
* A modern, gradient-based template for OG images
|
|
37
|
+
*/
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Default OG Image Template Component
|
|
41
|
+
*
|
|
42
|
+
* Features:
|
|
43
|
+
* - Modern gradient background
|
|
44
|
+
* - Responsive text sizing
|
|
45
|
+
* - Optional logo and site name
|
|
46
|
+
* - Clean typography
|
|
47
|
+
* - Full customization support
|
|
48
|
+
*
|
|
49
|
+
* @param props - Template props with title, description, siteName, logo and optional customization
|
|
50
|
+
*/
|
|
51
|
+
declare function DefaultTemplate({ title, description, siteName, logo, showLogo, showSiteName, backgroundType, gradientStart, gradientEnd, backgroundColor, titleSize, titleWeight, titleColor, descriptionSize, descriptionColor, siteNameSize, siteNameColor, padding, logoSize, devMode, }: OgImageTemplateProps): ReactElement;
|
|
52
|
+
/**
|
|
53
|
+
* Simple light template variant
|
|
54
|
+
*
|
|
55
|
+
* Light background variant with dark text
|
|
56
|
+
*/
|
|
57
|
+
declare function LightTemplate({ title, description, siteName, logo, showLogo, showSiteName, backgroundType, gradientStart, gradientEnd, backgroundColor, titleSize, titleWeight, titleColor, descriptionSize, descriptionColor, siteNameSize, siteNameColor, padding, logoSize, devMode, }: OgImageTemplateProps): ReactElement;
|
|
58
|
+
|
|
59
|
+
export { DefaultTemplate, LightTemplate, type OgImageTemplateProps };
|
|
@@ -0,0 +1,325 @@
|
|
|
1
|
+
// src/og-image/components/DefaultTemplate.tsx
|
|
2
|
+
function DefaultTemplate({
|
|
3
|
+
title,
|
|
4
|
+
description,
|
|
5
|
+
siteName,
|
|
6
|
+
logo,
|
|
7
|
+
// Visibility flags
|
|
8
|
+
showLogo = true,
|
|
9
|
+
showSiteName = true,
|
|
10
|
+
// Background customization
|
|
11
|
+
backgroundType = "gradient",
|
|
12
|
+
gradientStart = "#667eea",
|
|
13
|
+
gradientEnd = "#764ba2",
|
|
14
|
+
backgroundColor = "#ffffff",
|
|
15
|
+
// Typography - Title
|
|
16
|
+
titleSize,
|
|
17
|
+
titleWeight = 800,
|
|
18
|
+
titleColor = "white",
|
|
19
|
+
// Typography - Description
|
|
20
|
+
descriptionSize = 32,
|
|
21
|
+
descriptionColor = "rgba(255, 255, 255, 0.85)",
|
|
22
|
+
// Typography - Site Name
|
|
23
|
+
siteNameSize = 28,
|
|
24
|
+
siteNameColor = "rgba(255, 255, 255, 0.95)",
|
|
25
|
+
// Layout
|
|
26
|
+
padding = 80,
|
|
27
|
+
logoSize = 48,
|
|
28
|
+
// Dev mode
|
|
29
|
+
devMode = false
|
|
30
|
+
}) {
|
|
31
|
+
const calculatedTitleSize = titleSize || (title.length > 60 ? 56 : 72);
|
|
32
|
+
const backgroundStyle = backgroundType === "gradient" ? `linear-gradient(135deg, ${gradientStart} 0%, ${gradientEnd} 100%)` : backgroundColor;
|
|
33
|
+
const gridOverlay = devMode ? /* @__PURE__ */ React.createElement(
|
|
34
|
+
"div",
|
|
35
|
+
{
|
|
36
|
+
style: {
|
|
37
|
+
position: "absolute",
|
|
38
|
+
top: 0,
|
|
39
|
+
left: 0,
|
|
40
|
+
right: 0,
|
|
41
|
+
bottom: 0,
|
|
42
|
+
backgroundImage: `
|
|
43
|
+
linear-gradient(rgba(0, 0, 0, 0.1) 1px, transparent 1px),
|
|
44
|
+
linear-gradient(90deg, rgba(0, 0, 0, 0.1) 1px, transparent 1px)
|
|
45
|
+
`,
|
|
46
|
+
backgroundSize: "20px 20px",
|
|
47
|
+
pointerEvents: "none",
|
|
48
|
+
zIndex: 10
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
) : null;
|
|
52
|
+
return /* @__PURE__ */ React.createElement(
|
|
53
|
+
"div",
|
|
54
|
+
{
|
|
55
|
+
style: {
|
|
56
|
+
height: "100%",
|
|
57
|
+
width: "100%",
|
|
58
|
+
display: "flex",
|
|
59
|
+
flexDirection: "column",
|
|
60
|
+
alignItems: "flex-start",
|
|
61
|
+
justifyContent: "space-between",
|
|
62
|
+
background: backgroundStyle,
|
|
63
|
+
padding: `${padding}px`,
|
|
64
|
+
fontFamily: "system-ui, -apple-system, sans-serif",
|
|
65
|
+
position: "relative"
|
|
66
|
+
}
|
|
67
|
+
},
|
|
68
|
+
gridOverlay,
|
|
69
|
+
(showLogo && logo || showSiteName && siteName) && /* @__PURE__ */ React.createElement(
|
|
70
|
+
"div",
|
|
71
|
+
{
|
|
72
|
+
style: {
|
|
73
|
+
display: "flex",
|
|
74
|
+
alignItems: "center",
|
|
75
|
+
gap: "16px"
|
|
76
|
+
}
|
|
77
|
+
},
|
|
78
|
+
showLogo && logo && // eslint-disable-next-line @next/next/no-img-element
|
|
79
|
+
/* @__PURE__ */ React.createElement(
|
|
80
|
+
"img",
|
|
81
|
+
{
|
|
82
|
+
src: logo,
|
|
83
|
+
alt: "Logo",
|
|
84
|
+
width: logoSize,
|
|
85
|
+
height: logoSize,
|
|
86
|
+
style: {
|
|
87
|
+
borderRadius: "8px"
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
),
|
|
91
|
+
showSiteName && siteName && /* @__PURE__ */ React.createElement(
|
|
92
|
+
"div",
|
|
93
|
+
{
|
|
94
|
+
style: {
|
|
95
|
+
fontSize: siteNameSize,
|
|
96
|
+
fontWeight: 600,
|
|
97
|
+
color: siteNameColor,
|
|
98
|
+
letterSpacing: "-0.02em"
|
|
99
|
+
}
|
|
100
|
+
},
|
|
101
|
+
siteName
|
|
102
|
+
)
|
|
103
|
+
),
|
|
104
|
+
/* @__PURE__ */ React.createElement(
|
|
105
|
+
"div",
|
|
106
|
+
{
|
|
107
|
+
style: {
|
|
108
|
+
display: "flex",
|
|
109
|
+
flexDirection: "column",
|
|
110
|
+
gap: "24px",
|
|
111
|
+
flex: 1,
|
|
112
|
+
justifyContent: "center"
|
|
113
|
+
}
|
|
114
|
+
},
|
|
115
|
+
/* @__PURE__ */ React.createElement(
|
|
116
|
+
"div",
|
|
117
|
+
{
|
|
118
|
+
style: {
|
|
119
|
+
fontSize: calculatedTitleSize,
|
|
120
|
+
fontWeight: titleWeight,
|
|
121
|
+
color: titleColor,
|
|
122
|
+
lineHeight: 1.1,
|
|
123
|
+
letterSpacing: "-0.03em",
|
|
124
|
+
textShadow: backgroundType === "gradient" ? "0 2px 20px rgba(0, 0, 0, 0.2)" : "none",
|
|
125
|
+
maxWidth: "100%",
|
|
126
|
+
wordWrap: "break-word"
|
|
127
|
+
}
|
|
128
|
+
},
|
|
129
|
+
title
|
|
130
|
+
),
|
|
131
|
+
description && /* @__PURE__ */ React.createElement(
|
|
132
|
+
"div",
|
|
133
|
+
{
|
|
134
|
+
style: {
|
|
135
|
+
fontSize: descriptionSize,
|
|
136
|
+
fontWeight: 400,
|
|
137
|
+
color: descriptionColor,
|
|
138
|
+
lineHeight: 1.5,
|
|
139
|
+
letterSpacing: "-0.01em",
|
|
140
|
+
maxWidth: "90%",
|
|
141
|
+
display: "-webkit-box",
|
|
142
|
+
WebkitLineClamp: 2,
|
|
143
|
+
WebkitBoxOrient: "vertical",
|
|
144
|
+
overflow: "hidden"
|
|
145
|
+
}
|
|
146
|
+
},
|
|
147
|
+
description
|
|
148
|
+
)
|
|
149
|
+
),
|
|
150
|
+
/* @__PURE__ */ React.createElement(
|
|
151
|
+
"div",
|
|
152
|
+
{
|
|
153
|
+
style: {
|
|
154
|
+
display: "flex",
|
|
155
|
+
width: "100%",
|
|
156
|
+
height: "4px",
|
|
157
|
+
background: backgroundType === "gradient" ? `linear-gradient(90deg, ${gradientStart} 0%, ${gradientEnd} 100%)` : gradientStart,
|
|
158
|
+
borderRadius: "2px"
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
)
|
|
162
|
+
);
|
|
163
|
+
}
|
|
164
|
+
function LightTemplate({
|
|
165
|
+
title,
|
|
166
|
+
description,
|
|
167
|
+
siteName,
|
|
168
|
+
logo,
|
|
169
|
+
// Visibility flags
|
|
170
|
+
showLogo = true,
|
|
171
|
+
showSiteName = true,
|
|
172
|
+
// Background customization (defaults to light theme)
|
|
173
|
+
backgroundType = "solid",
|
|
174
|
+
gradientStart = "#667eea",
|
|
175
|
+
gradientEnd = "#764ba2",
|
|
176
|
+
backgroundColor = "#ffffff",
|
|
177
|
+
// Typography - Title
|
|
178
|
+
titleSize,
|
|
179
|
+
titleWeight = 800,
|
|
180
|
+
titleColor = "#111",
|
|
181
|
+
// Typography - Description
|
|
182
|
+
descriptionSize = 32,
|
|
183
|
+
descriptionColor = "#666",
|
|
184
|
+
// Typography - Site Name
|
|
185
|
+
siteNameSize = 28,
|
|
186
|
+
siteNameColor = "#111",
|
|
187
|
+
// Layout
|
|
188
|
+
padding = 80,
|
|
189
|
+
logoSize = 48,
|
|
190
|
+
// Dev mode
|
|
191
|
+
devMode = false
|
|
192
|
+
}) {
|
|
193
|
+
const calculatedTitleSize = titleSize || (title.length > 60 ? 56 : 72);
|
|
194
|
+
const backgroundStyle = backgroundType === "gradient" ? `linear-gradient(135deg, ${gradientStart} 0%, ${gradientEnd} 100%)` : backgroundColor;
|
|
195
|
+
const gridOverlay = devMode ? /* @__PURE__ */ React.createElement(
|
|
196
|
+
"div",
|
|
197
|
+
{
|
|
198
|
+
style: {
|
|
199
|
+
position: "absolute",
|
|
200
|
+
top: 0,
|
|
201
|
+
left: 0,
|
|
202
|
+
right: 0,
|
|
203
|
+
bottom: 0,
|
|
204
|
+
backgroundImage: `
|
|
205
|
+
linear-gradient(rgba(0, 0, 0, 0.1) 1px, transparent 1px),
|
|
206
|
+
linear-gradient(90deg, rgba(0, 0, 0, 0.1) 1px, transparent 1px)
|
|
207
|
+
`,
|
|
208
|
+
backgroundSize: "20px 20px",
|
|
209
|
+
pointerEvents: "none",
|
|
210
|
+
zIndex: 10
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
) : null;
|
|
214
|
+
return /* @__PURE__ */ React.createElement(
|
|
215
|
+
"div",
|
|
216
|
+
{
|
|
217
|
+
style: {
|
|
218
|
+
height: "100%",
|
|
219
|
+
width: "100%",
|
|
220
|
+
display: "flex",
|
|
221
|
+
flexDirection: "column",
|
|
222
|
+
alignItems: "flex-start",
|
|
223
|
+
justifyContent: "space-between",
|
|
224
|
+
background: backgroundStyle,
|
|
225
|
+
padding: `${padding}px`,
|
|
226
|
+
fontFamily: "system-ui, -apple-system, sans-serif",
|
|
227
|
+
position: "relative"
|
|
228
|
+
}
|
|
229
|
+
},
|
|
230
|
+
gridOverlay,
|
|
231
|
+
(showLogo && logo || showSiteName && siteName) && /* @__PURE__ */ React.createElement(
|
|
232
|
+
"div",
|
|
233
|
+
{
|
|
234
|
+
style: {
|
|
235
|
+
display: "flex",
|
|
236
|
+
alignItems: "center",
|
|
237
|
+
gap: "16px"
|
|
238
|
+
}
|
|
239
|
+
},
|
|
240
|
+
showLogo && logo && // eslint-disable-next-line @next/next/no-img-element
|
|
241
|
+
/* @__PURE__ */ React.createElement(
|
|
242
|
+
"img",
|
|
243
|
+
{
|
|
244
|
+
src: logo,
|
|
245
|
+
alt: "Logo",
|
|
246
|
+
width: logoSize,
|
|
247
|
+
height: logoSize,
|
|
248
|
+
style: {
|
|
249
|
+
borderRadius: "8px"
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
),
|
|
253
|
+
showSiteName && siteName && /* @__PURE__ */ React.createElement(
|
|
254
|
+
"div",
|
|
255
|
+
{
|
|
256
|
+
style: {
|
|
257
|
+
fontSize: siteNameSize,
|
|
258
|
+
fontWeight: 600,
|
|
259
|
+
color: siteNameColor,
|
|
260
|
+
letterSpacing: "-0.02em"
|
|
261
|
+
}
|
|
262
|
+
},
|
|
263
|
+
siteName
|
|
264
|
+
)
|
|
265
|
+
),
|
|
266
|
+
/* @__PURE__ */ React.createElement(
|
|
267
|
+
"div",
|
|
268
|
+
{
|
|
269
|
+
style: {
|
|
270
|
+
display: "flex",
|
|
271
|
+
flexDirection: "column",
|
|
272
|
+
gap: "24px",
|
|
273
|
+
flex: 1,
|
|
274
|
+
justifyContent: "center"
|
|
275
|
+
}
|
|
276
|
+
},
|
|
277
|
+
/* @__PURE__ */ React.createElement(
|
|
278
|
+
"div",
|
|
279
|
+
{
|
|
280
|
+
style: {
|
|
281
|
+
fontSize: calculatedTitleSize,
|
|
282
|
+
fontWeight: titleWeight,
|
|
283
|
+
color: titleColor,
|
|
284
|
+
lineHeight: 1.1,
|
|
285
|
+
letterSpacing: "-0.03em",
|
|
286
|
+
maxWidth: "100%",
|
|
287
|
+
wordWrap: "break-word"
|
|
288
|
+
}
|
|
289
|
+
},
|
|
290
|
+
title
|
|
291
|
+
),
|
|
292
|
+
description && /* @__PURE__ */ React.createElement(
|
|
293
|
+
"div",
|
|
294
|
+
{
|
|
295
|
+
style: {
|
|
296
|
+
fontSize: descriptionSize,
|
|
297
|
+
fontWeight: 400,
|
|
298
|
+
color: descriptionColor,
|
|
299
|
+
lineHeight: 1.5,
|
|
300
|
+
letterSpacing: "-0.01em",
|
|
301
|
+
maxWidth: "90%"
|
|
302
|
+
}
|
|
303
|
+
},
|
|
304
|
+
description
|
|
305
|
+
)
|
|
306
|
+
),
|
|
307
|
+
/* @__PURE__ */ React.createElement(
|
|
308
|
+
"div",
|
|
309
|
+
{
|
|
310
|
+
style: {
|
|
311
|
+
display: "flex",
|
|
312
|
+
width: "100%",
|
|
313
|
+
height: "4px",
|
|
314
|
+
background: backgroundType === "gradient" ? `linear-gradient(90deg, ${gradientStart} 0%, ${gradientEnd} 100%)` : gradientStart,
|
|
315
|
+
borderRadius: "2px"
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
)
|
|
319
|
+
);
|
|
320
|
+
}
|
|
321
|
+
export {
|
|
322
|
+
DefaultTemplate,
|
|
323
|
+
LightTemplate
|
|
324
|
+
};
|
|
325
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/og-image/components/DefaultTemplate.tsx"],"sourcesContent":["/**\n * Default OG Image Template\n *\n * A modern, gradient-based template for OG images\n */\n\nimport type { ReactElement } from 'react';\nimport type { OgImageTemplateProps } from '../types';\n\n/**\n * Default OG Image Template Component\n *\n * Features:\n * - Modern gradient background\n * - Responsive text sizing\n * - Optional logo and site name\n * - Clean typography\n * - Full customization support\n *\n * @param props - Template props with title, description, siteName, logo and optional customization\n */\nexport function DefaultTemplate({\n title,\n description,\n siteName,\n logo,\n // Visibility flags\n showLogo = true,\n showSiteName = true,\n // Background customization\n backgroundType = 'gradient',\n gradientStart = '#667eea',\n gradientEnd = '#764ba2',\n backgroundColor = '#ffffff',\n // Typography - Title\n titleSize,\n titleWeight = 800,\n titleColor = 'white',\n // Typography - Description\n descriptionSize = 32,\n descriptionColor = 'rgba(255, 255, 255, 0.85)',\n // Typography - Site Name\n siteNameSize = 28,\n siteNameColor = 'rgba(255, 255, 255, 0.95)',\n // Layout\n padding = 80,\n logoSize = 48,\n // Dev mode\n devMode = false,\n}: OgImageTemplateProps): ReactElement {\n // Calculate title size if not provided (responsive based on title length)\n const calculatedTitleSize = titleSize || (title.length > 60 ? 56 : 72);\n \n // Determine background style\n const backgroundStyle =\n backgroundType === 'gradient'\n ? `linear-gradient(135deg, ${gradientStart} 0%, ${gradientEnd} 100%)`\n : backgroundColor;\n\n // Grid overlay for dev mode\n const gridOverlay = devMode ? (\n <div\n style={{\n position: 'absolute',\n top: 0,\n left: 0,\n right: 0,\n bottom: 0,\n backgroundImage: `\n linear-gradient(rgba(0, 0, 0, 0.1) 1px, transparent 1px),\n linear-gradient(90deg, rgba(0, 0, 0, 0.1) 1px, transparent 1px)\n `,\n backgroundSize: '20px 20px',\n pointerEvents: 'none',\n zIndex: 10,\n }}\n />\n ) : null;\n\n return (\n <div\n style={{\n height: '100%',\n width: '100%',\n display: 'flex',\n flexDirection: 'column',\n alignItems: 'flex-start',\n justifyContent: 'space-between',\n background: backgroundStyle,\n padding: `${padding}px`,\n fontFamily: 'system-ui, -apple-system, sans-serif',\n position: 'relative',\n }}\n >\n {gridOverlay}\n \n {/* Header with logo and site name */}\n {((showLogo && logo) || (showSiteName && siteName)) && (\n <div\n style={{\n display: 'flex',\n alignItems: 'center',\n gap: '16px',\n }}\n >\n {showLogo && logo && (\n // eslint-disable-next-line @next/next/no-img-element\n <img\n src={logo}\n alt=\"Logo\"\n width={logoSize}\n height={logoSize}\n style={{\n borderRadius: '8px',\n }}\n />\n )}\n {showSiteName && siteName && (\n <div\n style={{\n fontSize: siteNameSize,\n fontWeight: 600,\n color: siteNameColor,\n letterSpacing: '-0.02em',\n }}\n >\n {siteName}\n </div>\n )}\n </div>\n )}\n\n {/* Main content */}\n <div\n style={{\n display: 'flex',\n flexDirection: 'column',\n gap: '24px',\n flex: 1,\n justifyContent: 'center',\n }}\n >\n {/* Title */}\n <div\n style={{\n fontSize: calculatedTitleSize,\n fontWeight: titleWeight,\n color: titleColor,\n lineHeight: 1.1,\n letterSpacing: '-0.03em',\n textShadow: backgroundType === 'gradient' ? '0 2px 20px rgba(0, 0, 0, 0.2)' : 'none',\n maxWidth: '100%',\n wordWrap: 'break-word',\n }}\n >\n {title}\n </div>\n\n {/* Description */}\n {description && (\n <div\n style={{\n fontSize: descriptionSize,\n fontWeight: 400,\n color: descriptionColor,\n lineHeight: 1.5,\n letterSpacing: '-0.01em',\n maxWidth: '90%',\n display: '-webkit-box',\n WebkitLineClamp: 2,\n WebkitBoxOrient: 'vertical',\n overflow: 'hidden',\n }}\n >\n {description}\n </div>\n )}\n </div>\n\n {/* Footer decoration */}\n <div\n style={{\n display: 'flex',\n width: '100%',\n height: '4px',\n background: backgroundType === 'gradient' \n ? `linear-gradient(90deg, ${gradientStart} 0%, ${gradientEnd} 100%)`\n : gradientStart,\n borderRadius: '2px',\n }}\n />\n </div>\n );\n}\n\n/**\n * Simple light template variant\n * \n * Light background variant with dark text\n */\nexport function LightTemplate({\n title,\n description,\n siteName,\n logo,\n // Visibility flags\n showLogo = true,\n showSiteName = true,\n // Background customization (defaults to light theme)\n backgroundType = 'solid',\n gradientStart = '#667eea',\n gradientEnd = '#764ba2',\n backgroundColor = '#ffffff',\n // Typography - Title\n titleSize,\n titleWeight = 800,\n titleColor = '#111',\n // Typography - Description\n descriptionSize = 32,\n descriptionColor = '#666',\n // Typography - Site Name\n siteNameSize = 28,\n siteNameColor = '#111',\n // Layout\n padding = 80,\n logoSize = 48,\n // Dev mode\n devMode = false,\n}: OgImageTemplateProps): ReactElement {\n // Calculate title size if not provided (responsive based on title length)\n const calculatedTitleSize = titleSize || (title.length > 60 ? 56 : 72);\n \n // Determine background style\n const backgroundStyle =\n backgroundType === 'gradient'\n ? `linear-gradient(135deg, ${gradientStart} 0%, ${gradientEnd} 100%)`\n : backgroundColor;\n\n // Grid overlay for dev mode\n const gridOverlay = devMode ? (\n <div\n style={{\n position: 'absolute',\n top: 0,\n left: 0,\n right: 0,\n bottom: 0,\n backgroundImage: `\n linear-gradient(rgba(0, 0, 0, 0.1) 1px, transparent 1px),\n linear-gradient(90deg, rgba(0, 0, 0, 0.1) 1px, transparent 1px)\n `,\n backgroundSize: '20px 20px',\n pointerEvents: 'none',\n zIndex: 10,\n }}\n />\n ) : null;\n\n return (\n <div\n style={{\n height: '100%',\n width: '100%',\n display: 'flex',\n flexDirection: 'column',\n alignItems: 'flex-start',\n justifyContent: 'space-between',\n background: backgroundStyle,\n padding: `${padding}px`,\n fontFamily: 'system-ui, -apple-system, sans-serif',\n position: 'relative',\n }}\n >\n {gridOverlay}\n \n {/* Header with logo and site name */}\n {((showLogo && logo) || (showSiteName && siteName)) && (\n <div\n style={{\n display: 'flex',\n alignItems: 'center',\n gap: '16px',\n }}\n >\n {showLogo && logo && (\n // eslint-disable-next-line @next/next/no-img-element\n <img\n src={logo}\n alt=\"Logo\"\n width={logoSize}\n height={logoSize}\n style={{\n borderRadius: '8px',\n }}\n />\n )}\n {showSiteName && siteName && (\n <div\n style={{\n fontSize: siteNameSize,\n fontWeight: 600,\n color: siteNameColor,\n letterSpacing: '-0.02em',\n }}\n >\n {siteName}\n </div>\n )}\n </div>\n )}\n\n {/* Main content */}\n <div\n style={{\n display: 'flex',\n flexDirection: 'column',\n gap: '24px',\n flex: 1,\n justifyContent: 'center',\n }}\n >\n {/* Title */}\n <div\n style={{\n fontSize: calculatedTitleSize,\n fontWeight: titleWeight,\n color: titleColor,\n lineHeight: 1.1,\n letterSpacing: '-0.03em',\n maxWidth: '100%',\n wordWrap: 'break-word',\n }}\n >\n {title}\n </div>\n\n {/* Description */}\n {description && (\n <div\n style={{\n fontSize: descriptionSize,\n fontWeight: 400,\n color: descriptionColor,\n lineHeight: 1.5,\n letterSpacing: '-0.01em',\n maxWidth: '90%',\n }}\n >\n {description}\n </div>\n )}\n </div>\n\n {/* Footer decoration */}\n <div\n style={{\n display: 'flex',\n width: '100%',\n height: '4px',\n background: backgroundType === 'gradient' \n ? `linear-gradient(90deg, ${gradientStart} 0%, ${gradientEnd} 100%)`\n : gradientStart,\n borderRadius: '2px',\n }}\n />\n </div>\n );\n}\n\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import { ImageResponse } from 'next/og';
|
|
2
|
+
import { NextRequest } from 'next/server';
|
|
3
|
+
import { ReactElement } from 'react';
|
|
4
|
+
import { OgImageTemplateProps } from './components/index.mjs';
|
|
5
|
+
export { DefaultTemplate, LightTemplate } from './components/index.mjs';
|
|
6
|
+
export { FontConfig, OgImageMetadataOptions, OgImageUrlParams, createFontLoader, createOgImageMetadataGenerator, createOgImageUrlBuilder, decodeBase64, encodeBase64, generateOgImageMetadata, generateOgImageUrl, getAbsoluteOgImageUrl, loadGoogleFont, loadGoogleFonts, parseOgImageData, parseOgImageUrl } from './utils/index.mjs';
|
|
7
|
+
import 'next';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* OG Image Route Handler
|
|
11
|
+
*
|
|
12
|
+
* Factory function to create OG Image route handler for Next.js App Router
|
|
13
|
+
*
|
|
14
|
+
* Usage:
|
|
15
|
+
* ```tsx
|
|
16
|
+
* // app/api/og/route.tsx
|
|
17
|
+
* import { createOgImageHandler } from '@djangocfg/nextjs/og-image';
|
|
18
|
+
* import { MyTemplate } from './templates';
|
|
19
|
+
*
|
|
20
|
+
* export const { GET, runtime } = createOgImageHandler({
|
|
21
|
+
* template: MyTemplate,
|
|
22
|
+
* defaultProps: {
|
|
23
|
+
* siteName: 'My Site',
|
|
24
|
+
* },
|
|
25
|
+
* fonts: [{ family: 'Manrope', weight: 700 }],
|
|
26
|
+
* });
|
|
27
|
+
* ```
|
|
28
|
+
*/
|
|
29
|
+
|
|
30
|
+
interface OgImageHandlerConfig {
|
|
31
|
+
/** Custom template component (optional, defaults to DefaultTemplate) */
|
|
32
|
+
template?: (props: OgImageTemplateProps) => ReactElement;
|
|
33
|
+
/** Default props to merge with query params */
|
|
34
|
+
defaultProps?: Partial<OgImageTemplateProps>;
|
|
35
|
+
/** Google Fonts to load */
|
|
36
|
+
fonts?: Array<{
|
|
37
|
+
family: string;
|
|
38
|
+
weight: 400 | 500 | 600 | 700 | 800 | 900;
|
|
39
|
+
}>;
|
|
40
|
+
/** Image size */
|
|
41
|
+
size?: {
|
|
42
|
+
width: number;
|
|
43
|
+
height: number;
|
|
44
|
+
};
|
|
45
|
+
/** Enable debug mode */
|
|
46
|
+
debug?: boolean;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Factory function to create OG Image route handler
|
|
50
|
+
*
|
|
51
|
+
* @example
|
|
52
|
+
* ```tsx
|
|
53
|
+
* // app/api/og/route.tsx
|
|
54
|
+
* import { createOgImageHandler } from '@djangocfg/nextjs/og-image';
|
|
55
|
+
* import { MyTemplate } from '@/components/MyTemplate';
|
|
56
|
+
*
|
|
57
|
+
* export const { GET, runtime } = createOgImageHandler({
|
|
58
|
+
* template: MyTemplate,
|
|
59
|
+
* defaultProps: { siteName: 'My Site' },
|
|
60
|
+
* fonts: [{ family: 'Inter', weight: 700 }],
|
|
61
|
+
* });
|
|
62
|
+
* ```
|
|
63
|
+
*/
|
|
64
|
+
declare function createOgImageHandler(config: OgImageHandlerConfig): {
|
|
65
|
+
GET: (req: NextRequest) => Promise<ImageResponse>;
|
|
66
|
+
runtime: "edge";
|
|
67
|
+
};
|
|
68
|
+
/**
|
|
69
|
+
* Create OG Image route handler for dynamic route with path parameter
|
|
70
|
+
*
|
|
71
|
+
* This is a convenience wrapper for Next.js dynamic routes like `/api/og/[data]/route.tsx`.
|
|
72
|
+
* It extracts the `data` parameter from the path and passes it to the handler as a query parameter.
|
|
73
|
+
* Also handles static export mode automatically.
|
|
74
|
+
*
|
|
75
|
+
* @example
|
|
76
|
+
* ```tsx
|
|
77
|
+
* // app/api/og/[data]/route.tsx
|
|
78
|
+
* import { createOgImageDynamicRoute } from '@djangocfg/nextjs/og-image';
|
|
79
|
+
* import { OgImageTemplate } from '@/components/OgImageTemplate';
|
|
80
|
+
*
|
|
81
|
+
* export const runtime = 'nodejs';
|
|
82
|
+
* export const dynamic = 'force-static';
|
|
83
|
+
* export const revalidate = false;
|
|
84
|
+
*
|
|
85
|
+
* const handler = createOgImageDynamicRoute({
|
|
86
|
+
* template: OgImageTemplate,
|
|
87
|
+
* defaultProps: {
|
|
88
|
+
* siteName: 'My App',
|
|
89
|
+
* logo: '/logo.svg',
|
|
90
|
+
* },
|
|
91
|
+
* });
|
|
92
|
+
*
|
|
93
|
+
* export async function GET(
|
|
94
|
+
* request: NextRequest,
|
|
95
|
+
* { params }: { params: { data: string } }
|
|
96
|
+
) {
|
|
97
|
+
* return handler(request, params);
|
|
98
|
+
* }
|
|
99
|
+
* ```
|
|
100
|
+
*/
|
|
101
|
+
declare function createOgImageDynamicRoute(config: OgImageHandlerConfig): {
|
|
102
|
+
GET: (request: NextRequest, context: {
|
|
103
|
+
params: Promise<{
|
|
104
|
+
data: string;
|
|
105
|
+
}>;
|
|
106
|
+
}) => Promise<Response>;
|
|
107
|
+
generateStaticParams: () => Promise<Array<{
|
|
108
|
+
data: string;
|
|
109
|
+
}>>;
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
export { type OgImageHandlerConfig, OgImageTemplateProps, createOgImageDynamicRoute, createOgImageHandler };
|