astro-blog-kit 0.1.0
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 +298 -0
- package/cli.ts +218 -0
- package/components/BlogCard.astro +248 -0
- package/components/BlogList/GridLayout.astro +42 -0
- package/components/BlogList/ListLayout.astro +30 -0
- package/components/BlogList/MagazineLayout.astro +60 -0
- package/components/BlogList/index.ts +7 -0
- package/components/BlogList.astro +136 -0
- package/components/BlogPost.astro +349 -0
- package/components/CommentForm.astro +331 -0
- package/components/Comments.astro +245 -0
- package/components/Pagination.astro +101 -0
- package/components/index.ts +14 -0
- package/define-config.ts +65 -0
- package/index.ts +41 -0
- package/integration.ts +137 -0
- package/package.json +41 -0
- package/templates/api-comments.ts.template +122 -0
- package/templates/blog-config.ts.template +24 -0
- package/templates/blog-index.astro.template +41 -0
- package/templates/blog-page.astro.template +46 -0
- package/templates/blog-slug.astro.template +41 -0
- package/types.ts +157 -0
- package/utils/collections.ts +87 -0
- package/utils/i18n.ts +129 -0
- package/utils/index.ts +41 -0
- package/utils/slug.ts +106 -0
- package/utils/wordpress.ts +162 -0
- package/virtual.d.ts +19 -0
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
---
|
|
2
|
+
import type { WPComment } from "../utils/wordpress";
|
|
3
|
+
|
|
4
|
+
interface Props {
|
|
5
|
+
comments: WPComment[];
|
|
6
|
+
postId: number;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
const { comments, postId } = Astro.props;
|
|
10
|
+
|
|
11
|
+
// Organiza comentarios en árbol (replies anidadas)
|
|
12
|
+
const topLevel = comments.filter((c: WPComment) => c.parent === 0);
|
|
13
|
+
const getReplies = (parentId: number) =>
|
|
14
|
+
comments.filter((c: WPComment) => c.parent === parentId);
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
<section class="comments" id="comments">
|
|
18
|
+
|
|
19
|
+
<h2 class="comments__title">
|
|
20
|
+
{comments.length === 0
|
|
21
|
+
? "No comments yet"
|
|
22
|
+
: `${comments.length} Comment${comments.length === 1 ? "" : "s"}`
|
|
23
|
+
}
|
|
24
|
+
</h2>
|
|
25
|
+
|
|
26
|
+
{comments.length === 0 && (
|
|
27
|
+
<p class="comments__empty">Be the first to leave a comment.</p>
|
|
28
|
+
)}
|
|
29
|
+
|
|
30
|
+
{topLevel.length > 0 && (
|
|
31
|
+
<ol class="comments__list">
|
|
32
|
+
{topLevel.map((comment: WPComment) => {
|
|
33
|
+
const replies = getReplies(comment.id);
|
|
34
|
+
const avatar = comment.author_avatar_urls?.["48"];
|
|
35
|
+
|
|
36
|
+
return (
|
|
37
|
+
<li class="comment" id={`comment-${comment.id}`}>
|
|
38
|
+
<div class="comment__header">
|
|
39
|
+
{avatar && (
|
|
40
|
+
<img
|
|
41
|
+
src={avatar}
|
|
42
|
+
alt={comment.author_name}
|
|
43
|
+
class="comment__avatar"
|
|
44
|
+
width="48"
|
|
45
|
+
height="48"
|
|
46
|
+
loading="lazy"
|
|
47
|
+
/>
|
|
48
|
+
)}
|
|
49
|
+
<div class="comment__meta">
|
|
50
|
+
<span class="comment__author">{comment.author_name}</span>
|
|
51
|
+
<time class="comment__date">
|
|
52
|
+
{new Date(comment.date).toLocaleDateString("en-US", {
|
|
53
|
+
year: "numeric",
|
|
54
|
+
month: "long",
|
|
55
|
+
day: "numeric",
|
|
56
|
+
})}
|
|
57
|
+
</time>
|
|
58
|
+
</div>
|
|
59
|
+
</div>
|
|
60
|
+
|
|
61
|
+
<div
|
|
62
|
+
class="comment__body"
|
|
63
|
+
set:html={comment.content.rendered}
|
|
64
|
+
/>
|
|
65
|
+
|
|
66
|
+
<button
|
|
67
|
+
class="comment__reply-btn"
|
|
68
|
+
data-reply-to={comment.id}
|
|
69
|
+
data-reply-name={comment.author_name}
|
|
70
|
+
>
|
|
71
|
+
↩ Reply
|
|
72
|
+
</button>
|
|
73
|
+
|
|
74
|
+
{replies.length > 0 && (
|
|
75
|
+
<ol class="comment__replies">
|
|
76
|
+
{replies.map((reply: WPComment) => {
|
|
77
|
+
const replyAvatar = reply.author_avatar_urls?.["48"];
|
|
78
|
+
return (
|
|
79
|
+
<li class="comment comment--reply" id={`comment-${reply.id}`}>
|
|
80
|
+
<div class="comment__header">
|
|
81
|
+
{replyAvatar && (
|
|
82
|
+
<img
|
|
83
|
+
src={replyAvatar}
|
|
84
|
+
alt={reply.author_name}
|
|
85
|
+
class="comment__avatar comment__avatar--small"
|
|
86
|
+
width="36"
|
|
87
|
+
height="36"
|
|
88
|
+
loading="lazy"
|
|
89
|
+
/>
|
|
90
|
+
)}
|
|
91
|
+
<div class="comment__meta">
|
|
92
|
+
<span class="comment__author">{reply.author_name}</span>
|
|
93
|
+
<time class="comment__date">
|
|
94
|
+
{new Date(reply.date).toLocaleDateString("en-US", {
|
|
95
|
+
year: "numeric",
|
|
96
|
+
month: "long",
|
|
97
|
+
day: "numeric",
|
|
98
|
+
})}
|
|
99
|
+
</time>
|
|
100
|
+
</div>
|
|
101
|
+
</div>
|
|
102
|
+
<div
|
|
103
|
+
class="comment__body"
|
|
104
|
+
set:html={reply.content.rendered}
|
|
105
|
+
/>
|
|
106
|
+
</li>
|
|
107
|
+
);
|
|
108
|
+
})}
|
|
109
|
+
</ol>
|
|
110
|
+
)}
|
|
111
|
+
</li>
|
|
112
|
+
);
|
|
113
|
+
})}
|
|
114
|
+
</ol>
|
|
115
|
+
)}
|
|
116
|
+
|
|
117
|
+
</section>
|
|
118
|
+
|
|
119
|
+
<style>
|
|
120
|
+
.comments {
|
|
121
|
+
max-width: 720px;
|
|
122
|
+
margin: 4rem auto 0;
|
|
123
|
+
padding: 0 2rem;
|
|
124
|
+
font-family: var(--font-body);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
.comments__title {
|
|
128
|
+
font-family: var(--font-display);
|
|
129
|
+
font-size: 1.5rem;
|
|
130
|
+
font-weight: 700;
|
|
131
|
+
color: var(--color-text);
|
|
132
|
+
margin: 0 0 2rem;
|
|
133
|
+
padding-bottom: 1rem;
|
|
134
|
+
border-bottom: 1px solid var(--color-border);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
.comments__empty {
|
|
138
|
+
color: var(--color-muted);
|
|
139
|
+
font-size: 0.95rem;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
.comments__list {
|
|
143
|
+
list-style: none;
|
|
144
|
+
padding: 0;
|
|
145
|
+
margin: 0;
|
|
146
|
+
display: flex;
|
|
147
|
+
flex-direction: column;
|
|
148
|
+
gap: 2rem;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/* Comment */
|
|
152
|
+
.comment {
|
|
153
|
+
display: flex;
|
|
154
|
+
flex-direction: column;
|
|
155
|
+
gap: 0.75rem;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
.comment--reply {
|
|
159
|
+
padding-left: 1.5rem;
|
|
160
|
+
border-left: 2px solid var(--color-border);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
.comment__header {
|
|
164
|
+
display: flex;
|
|
165
|
+
align-items: center;
|
|
166
|
+
gap: 0.75rem;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
.comment__avatar {
|
|
170
|
+
width: 48px;
|
|
171
|
+
height: 48px;
|
|
172
|
+
border-radius: 50%;
|
|
173
|
+
object-fit: cover;
|
|
174
|
+
border: 2px solid var(--color-border);
|
|
175
|
+
flex-shrink: 0;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
.comment__avatar--small {
|
|
179
|
+
width: 36px;
|
|
180
|
+
height: 36px;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
.comment__meta {
|
|
184
|
+
display: flex;
|
|
185
|
+
flex-direction: column;
|
|
186
|
+
gap: 0.15rem;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
.comment__author {
|
|
190
|
+
font-size: 0.9rem;
|
|
191
|
+
font-weight: 700;
|
|
192
|
+
color: var(--color-text);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
.comment__date {
|
|
196
|
+
font-size: 0.75rem;
|
|
197
|
+
color: var(--color-muted);
|
|
198
|
+
font-family: var(--font-mono);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
.comment__body {
|
|
202
|
+
font-size: 0.95rem;
|
|
203
|
+
color: var(--color-muted-light);
|
|
204
|
+
line-height: 1.75;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
.comment__body :global(p) {
|
|
208
|
+
margin: 0 0 0.75rem;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
.comment__body :global(p:last-child) {
|
|
212
|
+
margin: 0;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
.comment__reply-btn {
|
|
216
|
+
align-self: flex-start;
|
|
217
|
+
background: none;
|
|
218
|
+
border: none;
|
|
219
|
+
cursor: pointer;
|
|
220
|
+
font-size: 0.8rem;
|
|
221
|
+
font-family: var(--font-mono);
|
|
222
|
+
color: var(--color-muted);
|
|
223
|
+
padding: 0;
|
|
224
|
+
transition: color 0.15s;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
.comment__reply-btn:hover {
|
|
228
|
+
color: var(--color-accent);
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
.comment__replies {
|
|
232
|
+
list-style: none;
|
|
233
|
+
padding: 0;
|
|
234
|
+
margin: 0.5rem 0 0;
|
|
235
|
+
display: flex;
|
|
236
|
+
flex-direction: column;
|
|
237
|
+
gap: 1.5rem;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
@media (max-width: 768px) {
|
|
241
|
+
.comments {
|
|
242
|
+
padding: 0 1.25rem;
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
</style>
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
---
|
|
2
|
+
import type { PaginationProps } from "../types";
|
|
3
|
+
|
|
4
|
+
interface Props extends PaginationProps {}
|
|
5
|
+
|
|
6
|
+
const { currentPage, totalPages, basePath, blogBase, t } = Astro.props;
|
|
7
|
+
|
|
8
|
+
const pages = Array.from({ length: totalPages }, (_, i) => i + 1);
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
{totalPages > 1 && (
|
|
12
|
+
<nav class="pagination" aria-label="Blog pagination">
|
|
13
|
+
|
|
14
|
+
{currentPage > 1 && (
|
|
15
|
+
|
|
16
|
+
<a href={currentPage === 2 ? blogBase : `${basePath}${currentPage - 1}/`}
|
|
17
|
+
class="pagination__btn"
|
|
18
|
+
>
|
|
19
|
+
← {t.blog.btn_prev}
|
|
20
|
+
</a>
|
|
21
|
+
)}
|
|
22
|
+
|
|
23
|
+
<div class="pagination__pages">
|
|
24
|
+
{pages.map((pageNum) => (
|
|
25
|
+
|
|
26
|
+
<a href={pageNum === 1 ? blogBase : `${basePath}${pageNum}/`}
|
|
27
|
+
class={`pagination__page ${pageNum === currentPage ? "pagination__page--active" : ""}`}
|
|
28
|
+
aria-current={pageNum === currentPage ? "page" : undefined}
|
|
29
|
+
>
|
|
30
|
+
{pageNum}
|
|
31
|
+
</a>
|
|
32
|
+
))}
|
|
33
|
+
</div>
|
|
34
|
+
|
|
35
|
+
{currentPage < totalPages && (
|
|
36
|
+
<a href={`${basePath}${currentPage + 1}/`} class="pagination__btn">
|
|
37
|
+
{t.blog.btn_next} →
|
|
38
|
+
</a>
|
|
39
|
+
)}
|
|
40
|
+
|
|
41
|
+
</nav>
|
|
42
|
+
)}
|
|
43
|
+
|
|
44
|
+
<style>
|
|
45
|
+
.pagination {
|
|
46
|
+
display: flex;
|
|
47
|
+
align-items: center;
|
|
48
|
+
justify-content: center;
|
|
49
|
+
gap: 0.75rem;
|
|
50
|
+
flex-wrap: wrap;
|
|
51
|
+
padding-top: 3rem;
|
|
52
|
+
border-top: 2px solid var(--color-gray-200);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
.pagination__btn {
|
|
56
|
+
padding: 0.6rem 1.25rem;
|
|
57
|
+
background-color: var(--color-black);
|
|
58
|
+
color: var(--color-white);
|
|
59
|
+
font-family: var(--font-heading);
|
|
60
|
+
font-size: 0.9rem;
|
|
61
|
+
font-weight: 700;
|
|
62
|
+
text-transform: uppercase;
|
|
63
|
+
letter-spacing: 0.08em;
|
|
64
|
+
text-decoration: none;
|
|
65
|
+
transition: background-color 0.2s ease, color 0.2s ease;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
.pagination__btn:hover {
|
|
69
|
+
background-color: var(--color-yellow);
|
|
70
|
+
color: var(--color-black);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
.pagination__pages {
|
|
74
|
+
display: flex;
|
|
75
|
+
gap: 0.4rem;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
.pagination__page {
|
|
79
|
+
width: 2.5rem;
|
|
80
|
+
height: 2.5rem;
|
|
81
|
+
display: grid;
|
|
82
|
+
place-items: center;
|
|
83
|
+
font-weight: 700;
|
|
84
|
+
font-size: 0.9rem;
|
|
85
|
+
color: var(--color-gray-600);
|
|
86
|
+
border: 2px solid var(--color-gray-200);
|
|
87
|
+
text-decoration: none;
|
|
88
|
+
transition: border-color 0.2s ease, color 0.2s ease;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
.pagination__page:hover {
|
|
92
|
+
border-color: var(--color-black);
|
|
93
|
+
color: var(--color-black);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
.pagination__page--active {
|
|
97
|
+
background-color: var(--color-yellow);
|
|
98
|
+
border-color: var(--color-yellow);
|
|
99
|
+
color: var(--color-black);
|
|
100
|
+
}
|
|
101
|
+
</style>
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
// ─────────────────────────────────────────────────────────────
|
|
2
|
+
// astro-blog-kit · components/index.ts
|
|
3
|
+
// Punto de entrada de todos los componentes.
|
|
4
|
+
// Importa desde: 'astro-blog-kit/components'
|
|
5
|
+
// ─────────────────────────────────────────────────────────────
|
|
6
|
+
|
|
7
|
+
export { default as BlogCard } from "./BlogCard.astro";
|
|
8
|
+
export { default as BlogList } from "./BlogList.astro";
|
|
9
|
+
export { default as BlogPost } from "./BlogPost.astro";
|
|
10
|
+
export { default as Pagination } from "./Pagination.astro";
|
|
11
|
+
export { default as Comments } from "./Comments.astro";
|
|
12
|
+
export { default as CommentForm } from "./CommentForm.astro";
|
|
13
|
+
|
|
14
|
+
export { MagazineLayout, GridLayout, ListLayout } from "./BlogList/index.ts";
|
package/define-config.ts
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
// ─────────────────────────────────────────────────────────────
|
|
2
|
+
// astro-blog-kit · define-config.ts
|
|
3
|
+
// Función helper para tipar la config del blog.
|
|
4
|
+
// ─────────────────────────────────────────────────────────────
|
|
5
|
+
|
|
6
|
+
import type { BlogKitConfig, BlogTheme } from "./types";
|
|
7
|
+
|
|
8
|
+
export interface BlogConfig {
|
|
9
|
+
/** URL de tu WordPress. Ej: https://cms.tudominio.com */
|
|
10
|
+
wpUrl: string;
|
|
11
|
+
/** Posts por página. @default 5 */
|
|
12
|
+
postsPerPage?: number;
|
|
13
|
+
/** Layout por defecto. @default "magazine" */
|
|
14
|
+
defaultLayout?: "grid" | "list" | "magazine";
|
|
15
|
+
/** Locale por defecto. @default "en" */
|
|
16
|
+
locale?: string;
|
|
17
|
+
/** Tema visual */
|
|
18
|
+
theme?: BlogTheme;
|
|
19
|
+
/** Configuración de i18n */
|
|
20
|
+
i18n?: {
|
|
21
|
+
locales: string[];
|
|
22
|
+
defaultLocale: string;
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Define la configuración del blog con tipado completo.
|
|
28
|
+
* Genera blog.config.ts en la raíz del proyecto.
|
|
29
|
+
*
|
|
30
|
+
* @example
|
|
31
|
+
* ```ts
|
|
32
|
+
* // blog.config.ts
|
|
33
|
+
* import { defineBlogConfig } from 'astro-blog-kit';
|
|
34
|
+
*
|
|
35
|
+
* export default defineBlogConfig({
|
|
36
|
+
* wpUrl: 'https://cms.tudominio.com',
|
|
37
|
+
* postsPerPage: 5,
|
|
38
|
+
* defaultLayout: 'magazine',
|
|
39
|
+
* locale: 'en',
|
|
40
|
+
* theme: {
|
|
41
|
+
* accent: '#facc15',
|
|
42
|
+
* },
|
|
43
|
+
* });
|
|
44
|
+
* ```
|
|
45
|
+
*/
|
|
46
|
+
export function defineBlogConfig(config: BlogConfig): BlogConfig {
|
|
47
|
+
return {
|
|
48
|
+
postsPerPage: 5,
|
|
49
|
+
defaultLayout: "magazine",
|
|
50
|
+
locale: "en",
|
|
51
|
+
...config,
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Convierte BlogConfig a BlogKitConfig para usar en astro.config.mjs
|
|
57
|
+
*/
|
|
58
|
+
export function toBlogKitConfig(config: BlogConfig): BlogKitConfig {
|
|
59
|
+
return {
|
|
60
|
+
postsPerPage: config.postsPerPage,
|
|
61
|
+
defaultLayout: config.defaultLayout,
|
|
62
|
+
theme: config.theme,
|
|
63
|
+
i18n: config.i18n,
|
|
64
|
+
};
|
|
65
|
+
}
|
package/index.ts
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
// ─────────────────────────────────────────────────────────────
|
|
2
|
+
// astro-blog-kit · index.ts
|
|
3
|
+
// Punto de entrada principal del paquete.
|
|
4
|
+
// Importa desde: 'astro-blog-kit'
|
|
5
|
+
// ─────────────────────────────────────────────────────────────
|
|
6
|
+
|
|
7
|
+
// Tipos
|
|
8
|
+
export type {
|
|
9
|
+
BlogPost,
|
|
10
|
+
BlogListLayout,
|
|
11
|
+
BlogListProps,
|
|
12
|
+
BlogPostProps,
|
|
13
|
+
BlogTranslations,
|
|
14
|
+
BlogKitConfig,
|
|
15
|
+
I18nConfig,
|
|
16
|
+
PaginationProps,
|
|
17
|
+
PageStaticPath,
|
|
18
|
+
PostStaticPath,
|
|
19
|
+
} from "./types";
|
|
20
|
+
|
|
21
|
+
// Utils
|
|
22
|
+
export {
|
|
23
|
+
// i18n
|
|
24
|
+
isI18nEnabled,
|
|
25
|
+
getLang,
|
|
26
|
+
useTranslations,
|
|
27
|
+
getBlogBase,
|
|
28
|
+
getPageBase,
|
|
29
|
+
getDateLocale,
|
|
30
|
+
// slug
|
|
31
|
+
getStaticPathsForPosts,
|
|
32
|
+
getStaticPathsForPages,
|
|
33
|
+
toSlug,
|
|
34
|
+
estimateReadingTime,
|
|
35
|
+
getFeaturedImageUrl,
|
|
36
|
+
// collections
|
|
37
|
+
normalizeWPPost,
|
|
38
|
+
sortPostsByDate,
|
|
39
|
+
filterPostsByLang,
|
|
40
|
+
preparePosts,
|
|
41
|
+
} from "./utils/index.ts";
|
package/integration.ts
ADDED
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
// ─────────────────────────────────────────────────────────────
|
|
2
|
+
// astro-blog-kit · integration.ts
|
|
3
|
+
// ─────────────────────────────────────────────────────────────
|
|
4
|
+
|
|
5
|
+
import type { AstroIntegration } from "astro";
|
|
6
|
+
import type { BlogKitConfig, BlogTheme } from "./types";
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Genera el bloque de CSS variables a partir del tema.
|
|
10
|
+
*/
|
|
11
|
+
function generateThemeCSS(theme: BlogTheme = {}): string {
|
|
12
|
+
const t = {
|
|
13
|
+
accent: theme.accent ?? "#facc15",
|
|
14
|
+
background: theme.background ?? "#ffffff",
|
|
15
|
+
surface: theme.surface ?? "#f8f8f8",
|
|
16
|
+
text: theme.text ?? "#0a0a0a",
|
|
17
|
+
muted: theme.muted ?? "#6b7280",
|
|
18
|
+
mutedLight: theme.mutedLight ?? "#9ca3af",
|
|
19
|
+
border: theme.border ?? "#e5e7eb",
|
|
20
|
+
black: theme.black ?? "#0a0a0a",
|
|
21
|
+
white: theme.white ?? "#ffffff",
|
|
22
|
+
fontHeading: theme.fontHeading ?? "Georgia, serif",
|
|
23
|
+
fontBody: theme.fontBody ?? "system-ui, sans-serif",
|
|
24
|
+
fontMono: theme.fontMono ?? "monospace",
|
|
25
|
+
fontDisplay: theme.fontDisplay ?? "Georgia, serif",
|
|
26
|
+
containerMax: theme.containerMax ?? "1200px",
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
return `
|
|
30
|
+
:root {
|
|
31
|
+
--color-accent: ${t.accent};
|
|
32
|
+
--color-bg: ${t.background};
|
|
33
|
+
--color-surface: ${t.surface};
|
|
34
|
+
--color-text: ${t.text};
|
|
35
|
+
--color-muted: ${t.muted};
|
|
36
|
+
--color-muted-light: ${t.mutedLight};
|
|
37
|
+
--color-border: ${t.border};
|
|
38
|
+
--color-black: ${t.black};
|
|
39
|
+
--color-white: ${t.white};
|
|
40
|
+
--color-yellow: ${t.accent};
|
|
41
|
+
--color-gray-100: #f3f4f6;
|
|
42
|
+
--color-gray-200: #e5e7eb;
|
|
43
|
+
--color-gray-300: #d1d5db;
|
|
44
|
+
--color-gray-400: #9ca3af;
|
|
45
|
+
--color-gray-600: #4b5563;
|
|
46
|
+
--font-heading: ${t.fontHeading};
|
|
47
|
+
--font-body: ${t.fontBody};
|
|
48
|
+
--font-mono: ${t.fontMono};
|
|
49
|
+
--font-display: ${t.fontDisplay};
|
|
50
|
+
--container-max: ${t.containerMax};
|
|
51
|
+
--transition: all 0.2s ease;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
*, *::before, *::after {
|
|
55
|
+
box-sizing: border-box;
|
|
56
|
+
margin: 0;
|
|
57
|
+
padding: 0;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
body {
|
|
61
|
+
font-family: var(--font-body);
|
|
62
|
+
background-color: var(--color-bg);
|
|
63
|
+
color: var(--color-text);
|
|
64
|
+
line-height: 1.6;
|
|
65
|
+
}
|
|
66
|
+
`;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Integración principal de astro-blog-kit.
|
|
71
|
+
*
|
|
72
|
+
* @example
|
|
73
|
+
* ```js
|
|
74
|
+
* // astro.config.mjs
|
|
75
|
+
* import { defineConfig } from 'astro/config';
|
|
76
|
+
* import { blogKit } from 'astro-blog-kit/integration';
|
|
77
|
+
*
|
|
78
|
+
* export default defineConfig({
|
|
79
|
+
* integrations: [
|
|
80
|
+
* blogKit({
|
|
81
|
+
* postsPerPage: 6,
|
|
82
|
+
* defaultLayout: 'magazine',
|
|
83
|
+
* theme: {
|
|
84
|
+
* accent: '#facc15',
|
|
85
|
+
* fontHeading: 'Inter, sans-serif',
|
|
86
|
+
* },
|
|
87
|
+
* }),
|
|
88
|
+
* ],
|
|
89
|
+
* });
|
|
90
|
+
* ```
|
|
91
|
+
*/
|
|
92
|
+
export function blogKit(config: BlogKitConfig = {}): AstroIntegration {
|
|
93
|
+
const resolvedConfig: Required<BlogKitConfig> = {
|
|
94
|
+
postsPerPage: config.postsPerPage ?? 5,
|
|
95
|
+
defaultLayout: config.defaultLayout ?? "magazine",
|
|
96
|
+
collectionName: config.collectionName ?? "blog",
|
|
97
|
+
i18n: config.i18n ?? { locales: [], defaultLocale: "en" },
|
|
98
|
+
theme: config.theme ?? {},
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
return {
|
|
102
|
+
name: "astro-blog-kit",
|
|
103
|
+
|
|
104
|
+
hooks: {
|
|
105
|
+
"astro:config:setup": ({ injectScript, logger }) => {
|
|
106
|
+
logger.info(
|
|
107
|
+
`astro-blog-kit initialized — layout: ${resolvedConfig.defaultLayout}, postsPerPage: ${resolvedConfig.postsPerPage}`
|
|
108
|
+
);
|
|
109
|
+
|
|
110
|
+
// Inyecta CSS variables del tema globalmente
|
|
111
|
+
const themeCSS = generateThemeCSS(resolvedConfig.theme);
|
|
112
|
+
injectScript("head-inline", `
|
|
113
|
+
(() => {
|
|
114
|
+
const style = document.createElement('style');
|
|
115
|
+
style.id = 'astro-blog-kit-theme';
|
|
116
|
+
style.textContent = \`${themeCSS.replace(/`/g, "\\`")}\`;
|
|
117
|
+
document.head.appendChild(style);
|
|
118
|
+
})();
|
|
119
|
+
`);
|
|
120
|
+
|
|
121
|
+
// Inyecta config global
|
|
122
|
+
injectScript(
|
|
123
|
+
"page-ssr",
|
|
124
|
+
`globalThis.__BLOG_KIT_CONFIG__ = ${JSON.stringify(resolvedConfig)};`
|
|
125
|
+
);
|
|
126
|
+
},
|
|
127
|
+
|
|
128
|
+
"astro:config:done": ({ logger }) => {
|
|
129
|
+
if (resolvedConfig.i18n.locales.length > 0) {
|
|
130
|
+
logger.info(
|
|
131
|
+
`i18n enabled — locales: [${resolvedConfig.i18n.locales.join(", ")}], default: ${resolvedConfig.i18n.defaultLocale}`
|
|
132
|
+
);
|
|
133
|
+
}
|
|
134
|
+
},
|
|
135
|
+
},
|
|
136
|
+
};
|
|
137
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "astro-blog-kit",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "A ready-to-use blog system for Astro with WordPress headless support, optional i18n, multiple layouts, and a comment system.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": "./index.ts",
|
|
9
|
+
"./integration": "./integration.ts",
|
|
10
|
+
"./components": "./components/index.ts",
|
|
11
|
+
"./utils": "./utils/index.ts"
|
|
12
|
+
},
|
|
13
|
+
"files": [
|
|
14
|
+
"index.ts",
|
|
15
|
+
"integration.ts",
|
|
16
|
+
"types.ts",
|
|
17
|
+
"virtual.d.ts",
|
|
18
|
+
"cli.ts",
|
|
19
|
+
"define-config.ts",
|
|
20
|
+
"templates",
|
|
21
|
+
"components",
|
|
22
|
+
"utils"
|
|
23
|
+
],
|
|
24
|
+
"keywords": [
|
|
25
|
+
"astro",
|
|
26
|
+
"blog",
|
|
27
|
+
"wordpress",
|
|
28
|
+
"i18n",
|
|
29
|
+
"astro-integration",
|
|
30
|
+
"astro-component",
|
|
31
|
+
"headless-cms"
|
|
32
|
+
],
|
|
33
|
+
"peerDependencies": {
|
|
34
|
+
"astro": "^4.0.0 || ^5.0.0"
|
|
35
|
+
},
|
|
36
|
+
"devDependencies": {
|
|
37
|
+
"@clack/prompts": "latest",
|
|
38
|
+
"@types/node": "latest",
|
|
39
|
+
"astro": "^6.3.1"
|
|
40
|
+
}
|
|
41
|
+
}
|