@djangocfg/nextjs 2.1.5 → 2.1.7

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.
Files changed (40) hide show
  1. package/dist/ai/cli.d.mts +1 -0
  2. package/dist/ai/cli.mjs +171 -0
  3. package/dist/ai/cli.mjs.map +1 -0
  4. package/dist/ai/index.d.mts +81 -0
  5. package/dist/ai/index.mjs +139 -0
  6. package/dist/ai/index.mjs.map +1 -0
  7. package/dist/config/index.d.mts +360 -0
  8. package/dist/config/index.mjs +1363 -0
  9. package/dist/config/index.mjs.map +1 -0
  10. package/dist/constants-HezbftFb.d.mts +10 -0
  11. package/dist/contact/index.d.mts +47 -0
  12. package/dist/contact/index.mjs +98 -0
  13. package/dist/contact/index.mjs.map +1 -0
  14. package/dist/contact/route.d.mts +35 -0
  15. package/dist/contact/route.mjs +99 -0
  16. package/dist/contact/route.mjs.map +1 -0
  17. package/dist/health/index.d.mts +43 -0
  18. package/dist/health/index.mjs +38 -0
  19. package/dist/health/index.mjs.map +1 -0
  20. package/dist/index.d.mts +19 -0
  21. package/dist/index.mjs +2565 -0
  22. package/dist/index.mjs.map +1 -0
  23. package/dist/navigation/index.d.mts +89 -0
  24. package/dist/navigation/index.mjs +63 -0
  25. package/dist/navigation/index.mjs.map +1 -0
  26. package/dist/og-image/components/index.d.mts +59 -0
  27. package/dist/og-image/components/index.mjs +325 -0
  28. package/dist/og-image/components/index.mjs.map +1 -0
  29. package/dist/og-image/index.d.mts +112 -0
  30. package/dist/og-image/index.mjs +823 -0
  31. package/dist/og-image/index.mjs.map +1 -0
  32. package/dist/og-image/utils/index.d.mts +302 -0
  33. package/dist/og-image/utils/index.mjs +317 -0
  34. package/dist/og-image/utils/index.mjs.map +1 -0
  35. package/dist/sitemap/index.d.mts +66 -0
  36. package/dist/sitemap/index.mjs +76 -0
  37. package/dist/sitemap/index.mjs.map +1 -0
  38. package/dist/types-CwhXnEbK.d.mts +30 -0
  39. package/package.json +7 -6
  40. package/src/config/createNextConfig.ts +31 -2
@@ -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 };