astro-blog-kit 0.4.2 → 0.4.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/define-config.ts CHANGED
@@ -2,7 +2,16 @@
2
2
  // astro-blog-kit · define-config.ts
3
3
  // ─────────────────────────────────────────────────────────────
4
4
 
5
- import type { BlogKitConfig, BlogTheme, BlogHero, BlogUI } from "./types";
5
+ import type {
6
+ BlogKitConfig,
7
+ BlogTheme,
8
+ BlogHero,
9
+ BlogHeroLocale,
10
+ BlogUI,
11
+ BlogUILocale,
12
+ } from "./types";
13
+
14
+ // ── Interfaces públicas ───────────────────────────────────────
6
15
 
7
16
  export interface BlogConfig {
8
17
  /** URL de tu WordPress. Ej: https://cms.tudominio.com */
@@ -26,132 +35,170 @@ export interface BlogConfig {
26
35
  };
27
36
  }
28
37
 
29
- /**
30
- * Define la configuración del blog con tipado completo.
31
- *
32
- * @example
33
- * ```ts
34
- * // blog.config.ts
35
- * import { defineBlogConfig } from 'astro-blog-kit';
36
- *
37
- * export default defineBlogConfig({
38
- * wpUrl: 'https://cms.tudominio.com',
39
- * postsPerPage: 5,
40
- * defaultLayout: 'featured',
41
- * locale: 'en',
42
- * theme: {
43
- * accent: '#facc15',
44
- * background: '#0a1a0a',
45
- * text: '#ffffff',
46
- * },
47
- * hero: {
48
- * tagline: 'Technical Resources',
49
- * titleLine1: 'Building',
50
- * titleLine2: 'Insights',
51
- * description: 'Practical knowledge for architects and engineers.',
52
- * },
53
- * ui: {
54
- * readMoreLabel: 'Read more →',
55
- * btnPrev: 'Previous',
56
- * btnNext: 'Next',
57
- * commentButtonColor: '#facc15',
58
- * commentButtonTextColor: '#0a0a0a',
59
- * paginationStyle: 'minimal',
60
- * },
61
- * });
62
- * ```
63
- */
38
+ // ── defineBlogConfig ──────────────────────────────────────────
39
+
64
40
  export function defineBlogConfig(config: BlogConfig): BlogConfig {
65
41
  return {
66
- postsPerPage: 5,
42
+ postsPerPage: 5,
67
43
  defaultLayout: "magazine",
68
- locale: "en",
44
+ locale: "en",
69
45
  ...config,
70
46
  };
71
47
  }
72
48
 
73
- /**
74
- * Convierte BlogConfig a BlogKitConfig para usar en astro.config.mjs
75
- */
49
+ // ── toBlogKitConfig ───────────────────────────────────────────
50
+
76
51
  export function toBlogKitConfig(config: BlogConfig): BlogKitConfig {
77
52
  return {
78
- postsPerPage: config.postsPerPage,
53
+ postsPerPage: config.postsPerPage,
79
54
  defaultLayout: config.defaultLayout,
80
- theme: config.theme,
81
- hero: config.hero,
82
- ui: config.ui,
83
- i18n: config.i18n,
55
+ theme: config.theme,
56
+ hero: config.hero,
57
+ ui: config.ui,
58
+ i18n: config.i18n,
84
59
  };
85
60
  }
86
61
 
87
- /**
88
- * Resuelve los textos del hero con fallbacks según el locale.
89
- * Usado internamente por BlogList.astro
90
- */
62
+ // ── Helpers de detección de formato ──────────────────────────
63
+
64
+ function isHeroI18n(hero: BlogHero): hero is Record<string, BlogHeroLocale> {
65
+ return typeof hero === "object" &&
66
+ Object.values(hero).some((v) => typeof v === "object" && v !== null);
67
+ }
68
+
69
+ function isUIi18n(ui: BlogUI): ui is Record<string, BlogUILocale> {
70
+ return typeof ui === "object" &&
71
+ Object.values(ui).some((v) => typeof v === "object" && v !== null);
72
+ }
73
+
74
+ // ── Defaults internos ─────────────────────────────────────────
75
+
76
+ const HERO_DEFAULTS: Record<string, Required<BlogHeroLocale>> = {
77
+ en: {
78
+ tagline: "Our Blog",
79
+ titleLine1: "Latest",
80
+ titleLine2: "Articles",
81
+ description: "Welcome to our blog.",
82
+ },
83
+ es: {
84
+ tagline: "Nuestro Blog",
85
+ titleLine1: "Últimos",
86
+ titleLine2: "Artículos",
87
+ description: "Bienvenido a nuestro blog.",
88
+ },
89
+ };
90
+
91
+ const UI_DEFAULTS: Record<string, Required<BlogUILocale>> = {
92
+ en: {
93
+ readMoreLabel: "Read more →",
94
+ btnPrev: "Previous",
95
+ btnNext: "Next",
96
+ commentButtonColor: "var(--bk-accent)",
97
+ commentButtonTextColor: "var(--bk-black)",
98
+ paginationStyle: "minimal",
99
+ paginationBtnBg: "var(--bk-accent)",
100
+ paginationBtnText: "var(--bk-black)",
101
+ paginationBtnHoverBg: "var(--bk-text)",
102
+ paginationBtnHoverText: "var(--bk-white)",
103
+ paginationActiveBg: "var(--bk-accent)",
104
+ paginationActiveText: "var(--bk-black)",
105
+ },
106
+ es: {
107
+ readMoreLabel: "Leer más →",
108
+ btnPrev: "Anterior",
109
+ btnNext: "Siguiente",
110
+ commentButtonColor: "var(--bk-accent)",
111
+ commentButtonTextColor: "var(--bk-black)",
112
+ paginationStyle: "minimal",
113
+ paginationBtnBg: "var(--bk-accent)",
114
+ paginationBtnText: "var(--bk-black)",
115
+ paginationBtnHoverBg: "var(--bk-text)",
116
+ paginationBtnHoverText: "var(--bk-white)",
117
+ paginationActiveBg: "var(--bk-accent)",
118
+ paginationActiveText: "var(--bk-black)",
119
+ },
120
+ };
121
+
122
+ // ── resolveHero ───────────────────────────────────────────────
123
+
91
124
  export function resolveHero(
92
125
  hero: BlogHero | undefined,
93
126
  locale: string
94
- ): Required<BlogHero> {
95
- const defaults: Record<string, Required<BlogHero>> = {
96
- en: {
97
- tagline: "Our Blog",
98
- titleLine1: "Latest",
99
- titleLine2: "Articles",
100
- description: "Welcome to our blog.",
101
- },
102
- es: {
103
- tagline: "Nuestro Blog",
104
- titleLine1: "Últimos",
105
- titleLine2: "Artículos",
106
- description: "Bienvenido a nuestro blog.",
107
- },
108
- };
127
+ ): Required<BlogHeroLocale> {
128
+ const d = HERO_DEFAULTS[locale] ?? HERO_DEFAULTS["en"];
109
129
 
110
- const d = defaults[locale] ?? defaults["en"];
130
+ if (!hero) return d;
111
131
 
132
+ if (isHeroI18n(hero)) {
133
+ // Formato i18n: busca locale exacto → fallback 'en' → defaults internos
134
+ const src = hero[locale] ?? hero["en"] ?? {};
135
+ return {
136
+ tagline: src.tagline ?? d.tagline,
137
+ titleLine1: src.titleLine1 ?? d.titleLine1,
138
+ titleLine2: src.titleLine2 ?? d.titleLine2,
139
+ description: src.description ?? d.description,
140
+ };
141
+ }
142
+
143
+ // Formato plano legacy — backward compatible
144
+ const flat = hero as BlogHeroLocale;
112
145
  return {
113
- tagline: hero?.tagline ?? d.tagline,
114
- titleLine1: hero?.titleLine1 ?? d.titleLine1,
115
- titleLine2: hero?.titleLine2 ?? d.titleLine2,
116
- description: hero?.description ?? d.description,
146
+ tagline: flat.tagline ?? d.tagline,
147
+ titleLine1: flat.titleLine1 ?? d.titleLine1,
148
+ titleLine2: flat.titleLine2 ?? d.titleLine2,
149
+ description: flat.description ?? d.description,
117
150
  };
118
151
  }
119
152
 
120
- /**
121
- * Resuelve los labels de UI con fallbacks según el locale.
122
- * Usado internamente por BlogList.astro y Pagination.astro
123
- */
153
+ // ── resolveUI ─────────────────────────────────────────────────
154
+
124
155
  export function resolveUI(
125
156
  ui: BlogUI | undefined,
126
157
  locale: string
127
- ): Required<BlogUI> {
128
- const defaults: Record<string, Required<BlogUI>> = {
129
- en: {
130
- readMoreLabel: "Read more →",
131
- btnPrev: "Previous",
132
- btnNext: "Next",
133
- commentButtonColor: "var(--bk-accent)",
134
- commentButtonTextColor: "var(--bk-black)",
135
- paginationStyle: "minimal",
136
- },
137
- es: {
138
- readMoreLabel: "Leer más →",
139
- btnPrev: "Anterior",
140
- btnNext: "Siguiente",
141
- commentButtonColor: "var(--bk-accent)",
142
- commentButtonTextColor: "var(--bk-black)",
143
- paginationStyle: "minimal",
144
- },
145
- };
158
+ ): Required<BlogUILocale> {
159
+ const d = UI_DEFAULTS[locale] ?? UI_DEFAULTS["en"];
160
+
161
+ if (!ui) return d;
162
+
163
+ if (isUIi18n(ui)) {
164
+ // Campos visuales (colores) viven en cualquier locale; los buscamos
165
+ // en todos los locales disponibles para usarlos como fallback compartido.
166
+ const allLocales = Object.values(ui as Record<string, BlogUILocale>);
167
+ const visual = allLocales.find((v) => v.paginationBtnBg) ?? {};
168
+
169
+ const src = (ui as Record<string, BlogUILocale>)[locale] ??
170
+ (ui as Record<string, BlogUILocale>)["en"] ?? {};
146
171
 
147
- const d = defaults[locale] ?? defaults["en"];
172
+ return {
173
+ readMoreLabel: src.readMoreLabel ?? d.readMoreLabel,
174
+ btnPrev: src.btnPrev ?? d.btnPrev,
175
+ btnNext: src.btnNext ?? d.btnNext,
176
+ commentButtonColor: src.commentButtonColor ?? visual.commentButtonColor ?? d.commentButtonColor,
177
+ commentButtonTextColor: src.commentButtonTextColor ?? visual.commentButtonTextColor ?? d.commentButtonTextColor,
178
+ paginationStyle: src.paginationStyle ?? visual.paginationStyle ?? d.paginationStyle,
179
+ paginationBtnBg: src.paginationBtnBg ?? visual.paginationBtnBg ?? d.paginationBtnBg,
180
+ paginationBtnText: src.paginationBtnText ?? visual.paginationBtnText ?? d.paginationBtnText,
181
+ paginationBtnHoverBg: src.paginationBtnHoverBg ?? visual.paginationBtnHoverBg ?? d.paginationBtnHoverBg,
182
+ paginationBtnHoverText: src.paginationBtnHoverText ?? visual.paginationBtnHoverText ?? d.paginationBtnHoverText,
183
+ paginationActiveBg: src.paginationActiveBg ?? visual.paginationActiveBg ?? d.paginationActiveBg,
184
+ paginationActiveText: src.paginationActiveText ?? visual.paginationActiveText ?? d.paginationActiveText,
185
+ };
186
+ }
148
187
 
188
+ // Formato plano legacy — backward compatible
189
+ const flat = ui as BlogUILocale;
149
190
  return {
150
- readMoreLabel: ui?.readMoreLabel ?? d.readMoreLabel,
151
- btnPrev: ui?.btnPrev ?? d.btnPrev,
152
- btnNext: ui?.btnNext ?? d.btnNext,
153
- commentButtonColor: ui?.commentButtonColor ?? d.commentButtonColor,
154
- commentButtonTextColor: ui?.commentButtonTextColor ?? d.commentButtonTextColor,
155
- paginationStyle: ui?.paginationStyle ?? d.paginationStyle,
191
+ readMoreLabel: flat.readMoreLabel ?? d.readMoreLabel,
192
+ btnPrev: flat.btnPrev ?? d.btnPrev,
193
+ btnNext: flat.btnNext ?? d.btnNext,
194
+ commentButtonColor: flat.commentButtonColor ?? d.commentButtonColor,
195
+ commentButtonTextColor: flat.commentButtonTextColor ?? d.commentButtonTextColor,
196
+ paginationStyle: flat.paginationStyle ?? d.paginationStyle,
197
+ paginationBtnBg: flat.paginationBtnBg ?? d.paginationBtnBg,
198
+ paginationBtnText: flat.paginationBtnText ?? d.paginationBtnText,
199
+ paginationBtnHoverBg: flat.paginationBtnHoverBg ?? d.paginationBtnHoverBg,
200
+ paginationBtnHoverText: flat.paginationBtnHoverText ?? d.paginationBtnHoverText,
201
+ paginationActiveBg: flat.paginationActiveBg ?? d.paginationActiveBg,
202
+ paginationActiveText: flat.paginationActiveText ?? d.paginationActiveText,
156
203
  };
157
204
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "astro-blog-kit",
3
- "version": "0.4.2",
3
+ "version": "0.4.3",
4
4
  "description": "A ready-to-use blog system for Astro with WordPress headless support, optional i18n, multiple layouts, and a comment system.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -2,9 +2,9 @@ import { defineBlogConfig } from 'astro-blog-kit';
2
2
 
3
3
  export default defineBlogConfig({
4
4
  wpUrl: import.meta.env.WP_API_URL || '__WP_URL__',
5
- postsPerPage: __POSTS_PER_PAGE__,
5
+ postsPerPage: __POSTS_PER_PAGE__,
6
6
  defaultLayout: '__DEFAULT_LAYOUT__',
7
- locale: '__LOCALE__',
7
+ locale: '__LOCALE__',
8
8
 
9
9
  theme: {
10
10
  accent: '#facc15',
@@ -23,28 +23,49 @@ export default defineBlogConfig({
23
23
  containerMax: '1200px',
24
24
  },
25
25
 
26
+ // ── Hero ──────────────────────────────────────────────────────
27
+ // Sitio i18n: un objeto por locale.
28
+ // Sitio monolingüe: puedes usar el formato plano:
29
+ // hero: { tagline: 'Our Blog', titleLine1: 'Latest', ... }
26
30
  hero: {
27
- tagline: '__T_TAGLINE__',
28
- titleLine1: '__T_TITLE_LINE1__',
29
- titleLine2: '__T_TITLE_LINE2__',
30
- description: '__T_DESCRIPTION__',
31
+ en: {
32
+ tagline: 'Our Blog',
33
+ titleLine1: 'Latest',
34
+ titleLine2: 'Articles',
35
+ description: 'Welcome to our blog.',
36
+ },
37
+ es: {
38
+ tagline: 'Nuestro Blog',
39
+ titleLine1: 'Últimos',
40
+ titleLine2: 'Artículos',
41
+ description: 'Bienvenido a nuestro blog.',
42
+ },
31
43
  },
32
44
 
45
+ // ── UI labels ────────────────────────────────────────────────
46
+ // Los labels de texto van por locale.
47
+ // Los campos visuales (colores, paginationStyle) son compartidos:
48
+ // ponlos en cualquier locale y aplican a todos.
33
49
  ui: {
34
- readMoreLabel: '__T_BTNCTA__',
35
- btnPrev: '__T_BTN_PREV__',
36
- btnNext: '__T_BTN_NEXT__',
37
- commentButtonColor: 'var(--bk-accent)',
38
- commentButtonTextColor: 'var(--bk-black)',
39
- paginationStyle: 'minimal',
40
- // ── Personalización de paginación (opcional) ──────────────
41
- // Descomenta y cambia los valores según tu marca.
42
- // Por defecto usan el accent y black del tema.
43
- // paginationBtnBg: '#facc15', // fondo botón PREV/NEXT
44
- // paginationBtnText: '#0a0a0a', // texto botón PREV/NEXT
45
- // paginationBtnHoverBg: '#0a0a0a', // fondo hover
46
- // paginationBtnHoverText: '#ffffff', // texto hover
47
- // paginationActiveBg: '#facc15', // fondo página activa
48
- // paginationActiveText: '#0a0a0a', // texto página activa
50
+ en: {
51
+ readMoreLabel: 'Read more →',
52
+ btnPrev: 'Previous',
53
+ btnNext: 'Next',
54
+ // ── Colores compartidos (van aquí una sola vez) ──────────
55
+ commentButtonColor: 'var(--bk-accent)',
56
+ commentButtonTextColor: 'var(--bk-black)',
57
+ paginationStyle: 'minimal',
58
+ // paginationBtnBg: '#facc15',
59
+ // paginationBtnText: '#0a0a0a',
60
+ // paginationBtnHoverBg: '#0a0a0a',
61
+ // paginationBtnHoverText: '#ffffff',
62
+ // paginationActiveBg: '#facc15',
63
+ // paginationActiveText: '#0a0a0a',
64
+ },
65
+ es: {
66
+ readMoreLabel: 'Leer más →',
67
+ btnPrev: 'Anterior',
68
+ btnNext: 'Siguiente',
69
+ },
49
70
  },
50
- });
71
+ });
package/types.ts CHANGED
@@ -29,109 +29,144 @@ export interface I18nConfig {
29
29
 
30
30
  export interface BlogTranslations {
31
31
  blog: {
32
- tagline: string;
32
+ tagline: string;
33
33
  title_line1: string;
34
34
  title_line2: string;
35
35
  description: string;
36
- btncta: string;
37
- btn_prev: string;
38
- btn_next: string;
36
+ btncta: string;
37
+ btn_prev: string;
38
+ btn_next: string;
39
39
  };
40
40
  }
41
41
 
42
42
  export interface PaginationProps {
43
43
  currentPage: number;
44
- totalPages: number;
45
- basePath: string;
46
- blogBase: string;
47
- t: BlogTranslations;
44
+ totalPages: number;
45
+ basePath: string;
46
+ blogBase: string;
47
+ t: BlogTranslations;
48
48
  }
49
49
 
50
50
  export interface BlogListProps {
51
- posts: BlogPost[];
51
+ posts: BlogPost[];
52
52
  currentPage: number;
53
- totalPages: number;
54
- basePath: string;
55
- blogBase: string;
56
- dateLocale: string;
57
- t: BlogTranslations;
58
- locale: string;
59
- layout?: BlogListLayout;
53
+ totalPages: number;
54
+ basePath: string;
55
+ blogBase: string;
56
+ dateLocale: string;
57
+ t: BlogTranslations;
58
+ locale: string;
59
+ layout?: BlogListLayout;
60
60
  }
61
61
 
62
62
  export interface BlogPostProps {
63
63
  post: BlogPost;
64
- t: BlogTranslations;
64
+ t: BlogTranslations;
65
65
  lang: string;
66
66
  }
67
67
 
68
+ // ── Theme ─────────────────────────────────────────────────────
69
+
68
70
  export interface BlogTheme {
69
- accent?: string;
70
- background?: string;
71
- surface?: string;
72
- text?: string;
73
- muted?: string;
74
- mutedLight?: string;
75
- border?: string;
76
- black?: string;
77
- white?: string;
78
- fontHeading?: string;
79
- fontBody?: string;
80
- fontMono?: string;
81
- fontDisplay?: string;
71
+ accent?: string;
72
+ background?: string;
73
+ surface?: string;
74
+ text?: string;
75
+ muted?: string;
76
+ mutedLight?: string;
77
+ border?: string;
78
+ black?: string;
79
+ white?: string;
80
+ fontHeading?: string;
81
+ fontBody?: string;
82
+ fontMono?: string;
83
+ fontDisplay?: string;
82
84
  containerMax?: string;
83
85
  }
84
86
 
85
- export interface BlogHero {
86
- tagline?: string;
87
- titleLine1?: string;
88
- titleLine2?: string;
87
+ // ── Hero ──────────────────────────────────────────────────────
88
+
89
+ /** Textos del hero para un único locale */
90
+ export interface BlogHeroLocale {
91
+ tagline?: string;
92
+ titleLine1?: string;
93
+ titleLine2?: string;
89
94
  description?: string;
90
95
  }
91
96
 
92
- export interface BlogUI {
97
+ /**
98
+ * Acepta dos formatos:
99
+ * - Plano (sitio monolingüe): { tagline: 'Our Blog', ... }
100
+ * - Por locale (sitio i18n): { en: { tagline: 'Our Blog' }, es: { tagline: 'Nuestro Blog' } }
101
+ */
102
+ export type BlogHero = BlogHeroLocale | Record<string, BlogHeroLocale>;
103
+
104
+ // ── UI ────────────────────────────────────────────────────────
105
+
106
+ /** Labels y colores de UI para un único locale */
107
+ export interface BlogUILocale {
93
108
  /** Texto del botón "leer más". @default "Read more →" */
94
- readMoreLabel?: string;
109
+ readMoreLabel?: string;
95
110
  /** Texto del botón de página anterior. @default "Previous" */
96
- btnPrev?: string;
111
+ btnPrev?: string;
97
112
  /** Texto del botón de página siguiente. @default "Next" */
98
- btnNext?: string;
113
+ btnNext?: string;
99
114
  /** Color de fondo del botón de comentarios. @default var(--bk-accent) */
100
- commentButtonColor?: string;
115
+ commentButtonColor?: string;
101
116
  /** Color del texto del botón de comentarios. @default var(--bk-black) */
102
117
  commentButtonTextColor?: string;
103
118
  /** Estilo de paginación. @default "minimal" */
104
- paginationStyle?: "minimal" | "numbered";
105
- /** Fondo botones PREV/NEXT. @default accent */
106
- paginationBtnBg?: string;
107
- /** Texto botones PREV/NEXT. @default black */
108
- paginationBtnText?: string;
109
- /** Fondo botones PREV/NEXT en hover. @default text */
110
- paginationBtnHoverBg?: string;
111
- /** Texto botones PREV/NEXT en hover. @default white */
119
+ paginationStyle?: "minimal" | "numbered";
120
+ /** Fondo botones PREV/NEXT. @default var(--bk-accent) */
121
+ paginationBtnBg?: string;
122
+ /** Texto botones PREV/NEXT. @default var(--bk-black) */
123
+ paginationBtnText?: string;
124
+ /** Fondo botones PREV/NEXT en hover. @default var(--bk-text) */
125
+ paginationBtnHoverBg?: string;
126
+ /** Texto botones PREV/NEXT en hover. @default var(--bk-white) */
112
127
  paginationBtnHoverText?: string;
113
- /** Fondo página activa. @default accent */
114
- paginationActiveBg?: string;
115
- /** Texto página activa. @default black */
116
- paginationActiveText?: string;
128
+ /** Fondo página activa. @default var(--bk-accent) */
129
+ paginationActiveBg?: string;
130
+ /** Texto página activa. @default var(--bk-black) */
131
+ paginationActiveText?: string;
117
132
  }
118
133
 
134
+ /**
135
+ * Acepta dos formatos:
136
+ * - Plano (sitio monolingüe): { readMoreLabel: 'Read more', ... }
137
+ * - Por locale (sitio i18n): { en: { readMoreLabel: 'Read more' }, es: { readMoreLabel: 'Leer más' } }
138
+ *
139
+ * Nota: los campos visuales (colores, paginationStyle) son compartidos entre locales.
140
+ * Ponlos en cualquier locale (ej. `en`) — el resolver los tomará como fallback global.
141
+ */
142
+ export type BlogUI = BlogUILocale | Record<string, BlogUILocale>;
143
+
144
+ // ── Alias para backward compat ────────────────────────────────
145
+ /** @deprecated Usa BlogHeroLocale */
146
+ export type { BlogHeroLocale as BlogHeroFlat };
147
+ /** @deprecated Usa BlogUILocale */
148
+ export type { BlogUILocale as BlogUIFlat };
149
+
150
+ // ── BlogKitConfig (integration.ts) ───────────────────────────
151
+
119
152
  export interface BlogKitConfig {
120
- postsPerPage?: number;
121
- i18n?: I18nConfig;
122
- defaultLayout?: BlogListLayout;
153
+ postsPerPage?: number;
154
+ i18n?: I18nConfig;
155
+ defaultLayout?: BlogListLayout;
123
156
  collectionName?: string;
124
- theme?: BlogTheme;
125
- hero?: BlogHero;
126
- ui?: BlogUI;
157
+ theme?: BlogTheme;
158
+ hero?: BlogHero;
159
+ ui?: BlogUI;
127
160
  }
128
161
 
162
+ // ── Static paths ──────────────────────────────────────────────
163
+
129
164
  export interface PageStaticPath {
130
165
  params: { page: string };
131
- props: { posts: BlogPost[]; currentPage: number; totalPages: number };
166
+ props: { posts: BlogPost[]; currentPage: number; totalPages: number };
132
167
  }
133
168
 
134
169
  export interface PostStaticPath {
135
170
  params: { slug: string };
136
- props: { post: BlogPost };
171
+ props: { post: BlogPost };
137
172
  }