@fullstackdatasolutions/articles 0.9.0 → 0.11.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/CHANGELOG.md +243 -0
- package/README.md +226 -29
- package/dist/index.cjs +635 -274
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +165 -56
- package/dist/index.d.ts +165 -56
- package/dist/index.js +614 -250
- package/dist/index.js.map +1 -1
- package/dist/nextjs.cjs +40 -5
- package/dist/nextjs.cjs.map +1 -1
- package/dist/nextjs.d.cts +72 -0
- package/dist/nextjs.d.ts +72 -0
- package/dist/nextjs.js +40 -5
- package/dist/nextjs.js.map +1 -1
- package/dist/server.cjs +280 -16
- package/dist/server.cjs.map +1 -1
- package/dist/server.d.cts +92 -3
- package/dist/server.d.ts +92 -3
- package/dist/server.js +269 -16
- package/dist/server.js.map +1 -1
- package/package.json +8 -5
- package/src/ArticleDetailHero.tsx +27 -2
- package/src/ArticleSchemas.tsx +27 -27
- package/src/AuthorArticlesPage.tsx +60 -0
- package/src/AuthorCard.tsx +112 -0
- package/src/AuthorDetailHero.tsx +56 -0
- package/src/Breadcrumb.tsx +78 -0
- package/src/CategoryArticlesPage.tsx +62 -11
- package/src/__tests__/ArticleDetailHero.test.tsx +21 -1
- package/src/__tests__/ArticleSchemas.test.tsx +47 -2
- package/src/__tests__/AuthorArticlesPage.test.tsx +74 -0
- package/src/__tests__/AuthorCard.test.tsx +98 -0
- package/src/__tests__/AuthorDetailHero.test.tsx +51 -0
- package/src/__tests__/CategoryArticlesPage.test.tsx +31 -5
- package/src/__tests__/articlesConfig.test.ts +20 -1
- package/src/__tests__/authorUtils.test.ts +89 -0
- package/src/__tests__/renderMdx.test.tsx +113 -0
- package/src/__tests__/seoUtils-authors.test.ts +160 -0
- package/src/__tests__/seoUtils.test.ts +4 -0
- package/src/__tests__/server-articles.test.ts +159 -2
- package/src/articleTypes.ts +33 -0
- package/src/articlesConfig.ts +68 -0
- package/src/authorUtils.ts +95 -0
- package/src/index.ts +32 -9
- package/src/renderMdx.tsx +8 -2
- package/src/seoUtils.ts +226 -7
- package/src/server-articles.ts +98 -10
- package/src/server.ts +19 -1
package/dist/index.cjs
CHANGED
|
@@ -81,6 +81,11 @@ __export(src_exports, {
|
|
|
81
81
|
ArticleTOC: () => ArticleTOC,
|
|
82
82
|
ArticlesHero: () => ArticlesHero,
|
|
83
83
|
ArticlesPage: () => ArticlesPage,
|
|
84
|
+
AuthorArticlesPage: () => AuthorArticlesPage,
|
|
85
|
+
AuthorCard: () => AuthorCard,
|
|
86
|
+
AuthorDetailHero: () => AuthorDetailHero,
|
|
87
|
+
AuthorSocialLinks: () => AuthorSocialLinks,
|
|
88
|
+
Breadcrumb: () => Breadcrumb,
|
|
84
89
|
BreadcrumbSchema: () => BreadcrumbSchema,
|
|
85
90
|
CategoryArticlesPage: () => CategoryArticlesPage,
|
|
86
91
|
CollectionPageSchema: () => CollectionPageSchema,
|
|
@@ -96,6 +101,8 @@ __export(src_exports, {
|
|
|
96
101
|
LatestArticles: () => LatestArticles,
|
|
97
102
|
LatestArticlesSection: () => LatestArticlesSection,
|
|
98
103
|
ScrollToTop: () => ScrollToTop,
|
|
104
|
+
breadcrumbsAreEnabled: () => breadcrumbsAreEnabled,
|
|
105
|
+
getBreadcrumbsConfig: () => getBreadcrumbsConfig,
|
|
99
106
|
useArticles: () => useArticles
|
|
100
107
|
});
|
|
101
108
|
module.exports = __toCommonJS(src_exports);
|
|
@@ -150,44 +157,177 @@ function ArticleCategoryGrid({ categories, pageSize = 8 }) {
|
|
|
150
157
|
] }) });
|
|
151
158
|
}
|
|
152
159
|
|
|
153
|
-
// src/
|
|
160
|
+
// src/authorUtils.ts
|
|
161
|
+
function getAuthorUrl(author) {
|
|
162
|
+
var _a;
|
|
163
|
+
return (_a = author.url) != null ? _a : `/articles/authors/${author.slug}`;
|
|
164
|
+
}
|
|
165
|
+
function getAuthorAvatar(author, config) {
|
|
166
|
+
if (!author.avatar) return void 0;
|
|
167
|
+
if (author.avatar.startsWith("http://") || author.avatar.startsWith("https://")) {
|
|
168
|
+
return author.avatar;
|
|
169
|
+
}
|
|
170
|
+
const path = `/articles/authors/${author.slug}/${author.avatar.replace(/^\/+/, "")}`;
|
|
171
|
+
if (!config) return path;
|
|
172
|
+
return `${config.siteUrl.replace(/\/$/, "")}${path}`;
|
|
173
|
+
}
|
|
174
|
+
function getAuthorSameAs(author) {
|
|
175
|
+
return getAuthorSocialLinks(author).map((link) => link.href);
|
|
176
|
+
}
|
|
177
|
+
function normalizeHandle(value) {
|
|
178
|
+
return value.replace(/^@/, "");
|
|
179
|
+
}
|
|
180
|
+
function normalizeUrl(value, baseUrl) {
|
|
181
|
+
if (value.startsWith("http://") || value.startsWith("https://")) return value;
|
|
182
|
+
if (!baseUrl) return value;
|
|
183
|
+
return `${baseUrl}${normalizeHandle(value)}`;
|
|
184
|
+
}
|
|
185
|
+
function getConfiguredSocialLinks(social) {
|
|
186
|
+
var _a, _b;
|
|
187
|
+
return [
|
|
188
|
+
{ label: "Website", href: (_a = social.website) != null ? _a : "" },
|
|
189
|
+
{
|
|
190
|
+
label: "Facebook",
|
|
191
|
+
href: social.facebook ? normalizeUrl(social.facebook, "https://www.facebook.com/") : ""
|
|
192
|
+
},
|
|
193
|
+
{
|
|
194
|
+
label: "Twitter",
|
|
195
|
+
href: social.twitter ? normalizeUrl(social.twitter, "https://twitter.com/") : ""
|
|
196
|
+
},
|
|
197
|
+
{ label: "X", href: social.x ? normalizeUrl(social.x, "https://x.com/") : "" },
|
|
198
|
+
{
|
|
199
|
+
label: "LinkedIn",
|
|
200
|
+
href: social.linkedin ? normalizeUrl(social.linkedin, "https://www.linkedin.com/in/") : ""
|
|
201
|
+
},
|
|
202
|
+
{
|
|
203
|
+
label: "Instagram",
|
|
204
|
+
href: social.instagram ? normalizeUrl(social.instagram, "https://www.instagram.com/") : ""
|
|
205
|
+
},
|
|
206
|
+
{
|
|
207
|
+
label: "YouTube",
|
|
208
|
+
href: social.youtube ? normalizeUrl(social.youtube, "https://www.youtube.com/") : ""
|
|
209
|
+
},
|
|
210
|
+
{
|
|
211
|
+
label: "TikTok",
|
|
212
|
+
href: social.tiktok ? normalizeUrl(social.tiktok, "https://www.tiktok.com/@") : ""
|
|
213
|
+
},
|
|
214
|
+
{
|
|
215
|
+
label: "GitHub",
|
|
216
|
+
href: social.github ? normalizeUrl(social.github, "https://github.com/") : ""
|
|
217
|
+
},
|
|
218
|
+
{
|
|
219
|
+
label: "Bluesky",
|
|
220
|
+
href: social.bluesky ? normalizeUrl(social.bluesky, "https://bsky.app/profile/") : ""
|
|
221
|
+
},
|
|
222
|
+
{
|
|
223
|
+
label: "Threads",
|
|
224
|
+
href: social.threads ? normalizeUrl(social.threads, "https://www.threads.net/@") : ""
|
|
225
|
+
},
|
|
226
|
+
{ label: "Mastodon", href: social.mastodon ? normalizeUrl(social.mastodon) : "" },
|
|
227
|
+
{
|
|
228
|
+
label: "Medium",
|
|
229
|
+
href: social.medium ? normalizeUrl(social.medium, "https://medium.com/@") : ""
|
|
230
|
+
},
|
|
231
|
+
{ label: "Newsletter", href: (_b = social.newsletter) != null ? _b : "" }
|
|
232
|
+
];
|
|
233
|
+
}
|
|
234
|
+
function getAuthorSocialLinks(author) {
|
|
235
|
+
var _a;
|
|
236
|
+
const social = author.social;
|
|
237
|
+
if (!social) return [];
|
|
238
|
+
const configuredLinks = getConfiguredSocialLinks(social);
|
|
239
|
+
const otherLinks = Object.entries((_a = social.other) != null ? _a : {}).map(([label, href]) => ({ label, href }));
|
|
240
|
+
return [...configuredLinks, ...otherLinks].filter((link) => link.href.trim().length > 0);
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
// src/Breadcrumb.tsx
|
|
244
|
+
var import_link2 = __toESM(require("next/link"), 1);
|
|
154
245
|
var import_jsx_runtime2 = require("react/jsx-runtime");
|
|
246
|
+
function buildBreadcrumbSchema(items) {
|
|
247
|
+
return {
|
|
248
|
+
"@context": "https://schema.org",
|
|
249
|
+
"@type": "BreadcrumbList",
|
|
250
|
+
itemListElement: items.map((item, index) => __spreadValues({
|
|
251
|
+
"@type": "ListItem",
|
|
252
|
+
position: index + 1,
|
|
253
|
+
name: item.name
|
|
254
|
+
}, item.url && { item: item.url }))
|
|
255
|
+
};
|
|
256
|
+
}
|
|
257
|
+
function getDisplayItems(items) {
|
|
258
|
+
if (items.length <= 4) return [...items];
|
|
259
|
+
return [items[0], { name: "..." }, ...items.slice(-2)];
|
|
260
|
+
}
|
|
261
|
+
function Breadcrumb({
|
|
262
|
+
items,
|
|
263
|
+
className = "",
|
|
264
|
+
showSchema = true,
|
|
265
|
+
separator = ">"
|
|
266
|
+
}) {
|
|
267
|
+
if (items.length === 0) return null;
|
|
268
|
+
const displayItems = getDisplayItems(items);
|
|
269
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_jsx_runtime2.Fragment, { children: [
|
|
270
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
271
|
+
"nav",
|
|
272
|
+
{
|
|
273
|
+
"aria-label": "Breadcrumb",
|
|
274
|
+
className: `border-b border-border bg-background/80 px-4 py-3 text-sm text-muted-foreground ${className}`,
|
|
275
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("ol", { className: "mx-auto flex max-w-7xl items-center gap-2 overflow-hidden", children: displayItems.map((item, index) => {
|
|
276
|
+
const isLast = index === displayItems.length - 1;
|
|
277
|
+
const key = `${item.name}-${index}`;
|
|
278
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("li", { className: "flex min-w-0 items-center gap-2", children: [
|
|
279
|
+
index > 0 && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { "aria-hidden": "true", children: separator }),
|
|
280
|
+
item.url && !isLast ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_link2.default, { href: item.url, className: "truncate hover:text-foreground", children: item.name }) : /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "truncate", "aria-current": isLast ? "page" : void 0, children: item.name })
|
|
281
|
+
] }, key);
|
|
282
|
+
}) })
|
|
283
|
+
}
|
|
284
|
+
),
|
|
285
|
+
showSchema && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
286
|
+
"script",
|
|
287
|
+
{
|
|
288
|
+
type: "application/ld+json",
|
|
289
|
+
dangerouslySetInnerHTML: { __html: JSON.stringify(buildBreadcrumbSchema(items)) }
|
|
290
|
+
}
|
|
291
|
+
)
|
|
292
|
+
] });
|
|
293
|
+
}
|
|
294
|
+
function BreadcrumbSchema({ items }) {
|
|
295
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Breadcrumb, { items, showSchema: true, className: "sr-only" });
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
// src/ArticleSchemas.tsx
|
|
299
|
+
var import_jsx_runtime3 = require("react/jsx-runtime");
|
|
300
|
+
function getPersonSchemas(article, authors) {
|
|
301
|
+
let resolvedAuthors = [];
|
|
302
|
+
if (authors == null ? void 0 : authors.length) {
|
|
303
|
+
resolvedAuthors = [...authors];
|
|
304
|
+
} else if (article.author) {
|
|
305
|
+
resolvedAuthors = [{ name: article.author, slug: "", bio: "" }];
|
|
306
|
+
}
|
|
307
|
+
return resolvedAuthors.map((author) => __spreadValues(__spreadValues({
|
|
308
|
+
"@type": "Person",
|
|
309
|
+
name: author.name
|
|
310
|
+
}, author.url && { url: author.url }), getAuthorSameAs(author).length > 0 && { sameAs: getAuthorSameAs(author) }));
|
|
311
|
+
}
|
|
155
312
|
function ArticleSchema({
|
|
156
313
|
article,
|
|
157
314
|
articleUrl,
|
|
158
315
|
siteName,
|
|
159
|
-
showAuthor = true
|
|
316
|
+
showAuthor = true,
|
|
317
|
+
authors
|
|
160
318
|
}) {
|
|
319
|
+
const personSchemas = getPersonSchemas(article, authors);
|
|
161
320
|
const schema = __spreadProps(__spreadValues(__spreadValues({
|
|
162
321
|
"@context": "https://schema.org",
|
|
163
322
|
"@type": "Article",
|
|
164
323
|
headline: article.title,
|
|
165
324
|
description: article.excerpt,
|
|
166
325
|
image: article.featuredImage
|
|
167
|
-
}, article.date && { datePublished: new Date(article.date).toISOString() }), showAuthor &&
|
|
326
|
+
}, article.date && { datePublished: new Date(article.date).toISOString() }), showAuthor && personSchemas.length > 0 && { author: personSchemas }), {
|
|
168
327
|
publisher: { "@type": "Organization", name: siteName },
|
|
169
328
|
mainEntityOfPage: { "@type": "WebPage", "@id": articleUrl }
|
|
170
329
|
});
|
|
171
|
-
return /* @__PURE__ */ (0,
|
|
172
|
-
"script",
|
|
173
|
-
{
|
|
174
|
-
type: "application/ld+json",
|
|
175
|
-
dangerouslySetInnerHTML: { __html: JSON.stringify(schema) }
|
|
176
|
-
}
|
|
177
|
-
);
|
|
178
|
-
}
|
|
179
|
-
function BreadcrumbSchema({ items }) {
|
|
180
|
-
const schema = {
|
|
181
|
-
"@context": "https://schema.org",
|
|
182
|
-
"@type": "BreadcrumbList",
|
|
183
|
-
itemListElement: items.map((item, index) => ({
|
|
184
|
-
"@type": "ListItem",
|
|
185
|
-
position: index + 1,
|
|
186
|
-
name: item.name,
|
|
187
|
-
item: item.url
|
|
188
|
-
}))
|
|
189
|
-
};
|
|
190
|
-
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
330
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
191
331
|
"script",
|
|
192
332
|
{
|
|
193
333
|
type: "application/ld+json",
|
|
@@ -200,32 +340,34 @@ function ArticleSEO({
|
|
|
200
340
|
articleUrl,
|
|
201
341
|
siteName,
|
|
202
342
|
siteLogo,
|
|
203
|
-
showAuthor = true
|
|
343
|
+
showAuthor = true,
|
|
344
|
+
authors
|
|
204
345
|
}) {
|
|
205
346
|
const publishedAt = article.date ? new Date(article.date).toISOString() : void 0;
|
|
206
347
|
const modifiedAt = article.lastmod ? new Date(article.lastmod).toISOString() : publishedAt;
|
|
348
|
+
const personSchemas = getPersonSchemas(article, authors);
|
|
207
349
|
const structuredData = __spreadValues(__spreadProps(__spreadValues(__spreadValues(__spreadValues({
|
|
208
350
|
"@context": "https://schema.org",
|
|
209
351
|
"@type": article.articleType || "Article",
|
|
210
352
|
mainEntityOfPage: { "@type": "WebPage", "@id": articleUrl },
|
|
211
353
|
headline: article.title,
|
|
212
354
|
image: article.featuredImage ? { "@type": "ImageObject", url: article.featuredImage } : void 0
|
|
213
|
-
}, publishedAt && { datePublished: publishedAt }), modifiedAt && { dateModified: modifiedAt }), showAuthor &&
|
|
355
|
+
}, publishedAt && { datePublished: publishedAt }), modifiedAt && { dateModified: modifiedAt }), showAuthor && personSchemas.length > 0 && { author: personSchemas }), {
|
|
214
356
|
publisher: __spreadValues({
|
|
215
357
|
"@type": "Organization",
|
|
216
358
|
name: siteName
|
|
217
359
|
}, siteLogo && { logo: { "@type": "ImageObject", url: siteLogo } }),
|
|
218
360
|
description: article.excerpt
|
|
219
361
|
}), article.series && { isPartOf: { "@type": "Blog", name: article.series } });
|
|
220
|
-
return /* @__PURE__ */ (0,
|
|
221
|
-
/* @__PURE__ */ (0,
|
|
362
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
|
|
363
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
222
364
|
"script",
|
|
223
365
|
{
|
|
224
366
|
type: "application/ld+json",
|
|
225
367
|
dangerouslySetInnerHTML: { __html: JSON.stringify(structuredData) }
|
|
226
368
|
}
|
|
227
369
|
),
|
|
228
|
-
article.faq && article.faq.length > 0 && /* @__PURE__ */ (0,
|
|
370
|
+
article.faq && article.faq.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
229
371
|
"script",
|
|
230
372
|
{
|
|
231
373
|
type: "application/ld+json",
|
|
@@ -242,7 +384,7 @@ function ArticleSEO({
|
|
|
242
384
|
}
|
|
243
385
|
}
|
|
244
386
|
),
|
|
245
|
-
article.howTo && article.howTo.length > 0 && /* @__PURE__ */ (0,
|
|
387
|
+
article.howTo && article.howTo.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
246
388
|
"script",
|
|
247
389
|
{
|
|
248
390
|
type: "application/ld+json",
|
|
@@ -272,7 +414,7 @@ function FAQPageSchema({ items }) {
|
|
|
272
414
|
acceptedAnswer: { "@type": "Answer", text: answer }
|
|
273
415
|
}))
|
|
274
416
|
};
|
|
275
|
-
return /* @__PURE__ */ (0,
|
|
417
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
276
418
|
"script",
|
|
277
419
|
{
|
|
278
420
|
type: "application/ld+json",
|
|
@@ -294,7 +436,7 @@ function CollectionPageSchema({
|
|
|
294
436
|
url,
|
|
295
437
|
numberOfItems: articleCount
|
|
296
438
|
};
|
|
297
|
-
return /* @__PURE__ */ (0,
|
|
439
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
298
440
|
"script",
|
|
299
441
|
{
|
|
300
442
|
type: "application/ld+json",
|
|
@@ -305,17 +447,17 @@ function CollectionPageSchema({
|
|
|
305
447
|
|
|
306
448
|
// src/ArticleSearchBar.tsx
|
|
307
449
|
var import_lucide_react = require("lucide-react");
|
|
308
|
-
var
|
|
450
|
+
var import_jsx_runtime4 = require("react/jsx-runtime");
|
|
309
451
|
function ArticleSearchBar({
|
|
310
452
|
value,
|
|
311
453
|
onChange,
|
|
312
454
|
disabled,
|
|
313
455
|
resultCount
|
|
314
456
|
}) {
|
|
315
|
-
return /* @__PURE__ */ (0,
|
|
316
|
-
/* @__PURE__ */ (0,
|
|
317
|
-
/* @__PURE__ */ (0,
|
|
318
|
-
/* @__PURE__ */ (0,
|
|
457
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("section", { className: "py-8 bg-background border-b border-border", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "max-w-4xl mx-auto px-4 sm:px-6 lg:px-8", children: [
|
|
458
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "relative", children: [
|
|
459
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_lucide_react.Search, { className: "absolute left-3 top-1/2 transform -translate-y-1/2 text-muted-foreground h-5 w-5" }),
|
|
460
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
319
461
|
"input",
|
|
320
462
|
{
|
|
321
463
|
type: "text",
|
|
@@ -326,44 +468,44 @@ function ArticleSearchBar({
|
|
|
326
468
|
className: "flex w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 pl-10 pr-10 py-3 text-lg"
|
|
327
469
|
}
|
|
328
470
|
),
|
|
329
|
-
value && /* @__PURE__ */ (0,
|
|
471
|
+
value && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
330
472
|
"button",
|
|
331
473
|
{
|
|
332
474
|
type: "button",
|
|
333
475
|
onClick: () => onChange(""),
|
|
334
476
|
"aria-label": "Clear search",
|
|
335
477
|
className: "absolute right-3 top-1/2 transform -translate-y-1/2 text-muted-foreground hover:text-foreground",
|
|
336
|
-
children: /* @__PURE__ */ (0,
|
|
478
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_lucide_react.X, { className: "h-5 w-5" })
|
|
337
479
|
}
|
|
338
480
|
)
|
|
339
481
|
] }),
|
|
340
|
-
value && /* @__PURE__ */ (0,
|
|
482
|
+
value && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "mt-2 text-sm text-muted-foreground", children: resultCount === 0 ? "No articles found matching your search." : `Found ${resultCount} article${resultCount === 1 ? "" : "s"} matching "${value}"` })
|
|
341
483
|
] }) });
|
|
342
484
|
}
|
|
343
485
|
|
|
344
486
|
// src/ArticlesHero.tsx
|
|
345
|
-
var
|
|
346
|
-
var
|
|
487
|
+
var import_link3 = __toESM(require("next/link"), 1);
|
|
488
|
+
var import_jsx_runtime5 = require("react/jsx-runtime");
|
|
347
489
|
var DEFAULT_TITLE = "Vox Populus Insights";
|
|
348
490
|
var DEFAULT_DESCRIPTION = "Expert analysis, campaign strategies, and voter engagement insights from the frontlines of democracy.";
|
|
349
491
|
function ArticlesHero({
|
|
350
492
|
title = DEFAULT_TITLE,
|
|
351
493
|
description = DEFAULT_DESCRIPTION
|
|
352
494
|
}) {
|
|
353
|
-
return /* @__PURE__ */ (0,
|
|
354
|
-
/* @__PURE__ */ (0,
|
|
355
|
-
/* @__PURE__ */ (0,
|
|
356
|
-
/* @__PURE__ */ (0,
|
|
357
|
-
/* @__PURE__ */ (0,
|
|
358
|
-
|
|
495
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("section", { className: "bg-linear-to-r from-gray-50 to-gray-100 py-16 md:py-24", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "max-w-7xl mx-auto px-4 sm:px-6 lg:px-8", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "max-w-3xl mx-auto text-center", children: [
|
|
496
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("h1", { className: "text-4xl md:text-5xl font-bold text-foreground mb-6", children: title }),
|
|
497
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("p", { className: "text-xl text-muted-foreground mb-8", children: description }),
|
|
498
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "flex flex-col sm:flex-row gap-4 justify-center", children: [
|
|
499
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
500
|
+
import_link3.default,
|
|
359
501
|
{
|
|
360
502
|
href: "#latest",
|
|
361
503
|
className: "inline-flex items-center justify-center px-4 py-2 bg-primary hover:bg-primary/90 text-primary-foreground rounded-md font-medium text-sm transition-colors",
|
|
362
504
|
children: "Browse Latest Articles"
|
|
363
505
|
}
|
|
364
506
|
),
|
|
365
|
-
/* @__PURE__ */ (0,
|
|
366
|
-
|
|
507
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
508
|
+
import_link3.default,
|
|
367
509
|
{
|
|
368
510
|
href: "/contact",
|
|
369
511
|
className: "inline-flex items-center justify-center px-4 py-2 border border-primary text-primary hover:bg-primary/10 rounded-md font-medium text-sm transition-colors",
|
|
@@ -375,44 +517,44 @@ function ArticlesHero({
|
|
|
375
517
|
}
|
|
376
518
|
|
|
377
519
|
// src/FeaturedArticle.tsx
|
|
378
|
-
var
|
|
520
|
+
var import_link4 = __toESM(require("next/link"), 1);
|
|
379
521
|
var import_image2 = __toESM(require("next/image"), 1);
|
|
380
522
|
var import_lucide_react2 = require("lucide-react");
|
|
381
|
-
var
|
|
523
|
+
var import_jsx_runtime6 = require("react/jsx-runtime");
|
|
382
524
|
function FeaturedArticle({ article, showAuthor = true }) {
|
|
383
|
-
return /* @__PURE__ */ (0,
|
|
384
|
-
/* @__PURE__ */ (0,
|
|
385
|
-
/* @__PURE__ */ (0,
|
|
386
|
-
/* @__PURE__ */ (0,
|
|
387
|
-
/* @__PURE__ */ (0,
|
|
388
|
-
/* @__PURE__ */ (0,
|
|
389
|
-
article.date && /* @__PURE__ */ (0,
|
|
390
|
-
/* @__PURE__ */ (0,
|
|
391
|
-
/* @__PURE__ */ (0,
|
|
525
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("section", { className: "py-16 bg-background", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "max-w-7xl mx-auto px-4 sm:px-6 lg:px-8", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "grid lg:grid-cols-2 gap-12 items-center", children: [
|
|
526
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { children: [
|
|
527
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "inline-block bg-primary/10 text-primary text-sm font-medium px-3 py-1 rounded-full mb-4", children: "FEATURED ARTICLE" }),
|
|
528
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("h2", { className: "text-3xl font-bold text-foreground mb-4", children: article.title }),
|
|
529
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { className: "text-lg text-muted-foreground mb-6", children: article.excerpt }),
|
|
530
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "flex items-center gap-6 text-sm text-muted-foreground mb-6", children: [
|
|
531
|
+
article.date && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "flex items-center gap-2", children: [
|
|
532
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_lucide_react2.Calendar, { className: "h-4 w-4" }),
|
|
533
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { children: article.date })
|
|
392
534
|
] }),
|
|
393
|
-
/* @__PURE__ */ (0,
|
|
394
|
-
/* @__PURE__ */ (0,
|
|
395
|
-
/* @__PURE__ */ (0,
|
|
535
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "flex items-center gap-2", children: [
|
|
536
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_lucide_react2.Clock, { className: "h-4 w-4" }),
|
|
537
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { children: article.readTime })
|
|
396
538
|
] }),
|
|
397
|
-
showAuthor && /* @__PURE__ */ (0,
|
|
398
|
-
/* @__PURE__ */ (0,
|
|
399
|
-
/* @__PURE__ */ (0,
|
|
539
|
+
showAuthor && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "flex items-center gap-2", children: [
|
|
540
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_lucide_react2.User, { className: "h-4 w-4" }),
|
|
541
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { children: article.author })
|
|
400
542
|
] })
|
|
401
543
|
] }),
|
|
402
|
-
/* @__PURE__ */ (0,
|
|
403
|
-
|
|
544
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
|
|
545
|
+
import_link4.default,
|
|
404
546
|
{
|
|
405
547
|
href: `/articles/${article.slug}`,
|
|
406
548
|
className: "inline-flex items-center justify-center px-4 py-2 bg-primary hover:bg-primary/90 text-primary-foreground rounded-md font-medium text-sm transition-colors",
|
|
407
549
|
children: [
|
|
408
550
|
"Read Full Article",
|
|
409
|
-
/* @__PURE__ */ (0,
|
|
551
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_lucide_react2.ArrowRight, { className: "ml-2 h-4 w-4" })
|
|
410
552
|
]
|
|
411
553
|
}
|
|
412
554
|
)
|
|
413
555
|
] }),
|
|
414
|
-
/* @__PURE__ */ (0,
|
|
415
|
-
/* @__PURE__ */ (0,
|
|
556
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "bg-card rounded-lg p-8", children: [
|
|
557
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
416
558
|
import_image2.default,
|
|
417
559
|
{
|
|
418
560
|
src: article.featuredImage,
|
|
@@ -422,7 +564,7 @@ function FeaturedArticle({ article, showAuthor = true }) {
|
|
|
422
564
|
className: "w-full h-64 object-cover rounded-lg mb-4"
|
|
423
565
|
}
|
|
424
566
|
),
|
|
425
|
-
/* @__PURE__ */ (0,
|
|
567
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "flex gap-2", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "bg-primary/10 text-primary text-xs font-medium px-2 py-1 rounded", children: article.category }) })
|
|
426
568
|
] })
|
|
427
569
|
] }) }) });
|
|
428
570
|
}
|
|
@@ -435,12 +577,12 @@ var import_react2 = require("react");
|
|
|
435
577
|
|
|
436
578
|
// src/ArticleCard.tsx
|
|
437
579
|
var import_image3 = __toESM(require("next/image"), 1);
|
|
438
|
-
var
|
|
580
|
+
var import_link5 = __toESM(require("next/link"), 1);
|
|
439
581
|
var import_lucide_react3 = require("lucide-react");
|
|
440
|
-
var
|
|
582
|
+
var import_jsx_runtime7 = require("react/jsx-runtime");
|
|
441
583
|
function ArticleCard({ article }) {
|
|
442
|
-
return /* @__PURE__ */ (0,
|
|
443
|
-
/* @__PURE__ */ (0,
|
|
584
|
+
return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "rounded-lg border border-border bg-card text-card-foreground shadow-sm hover:shadow-lg transition-shadow duration-300", children: [
|
|
585
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "p-0", children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
444
586
|
import_image3.default,
|
|
445
587
|
{
|
|
446
588
|
src: article.featuredImage,
|
|
@@ -450,35 +592,35 @@ function ArticleCard({ article }) {
|
|
|
450
592
|
height: 200
|
|
451
593
|
}
|
|
452
594
|
) }),
|
|
453
|
-
/* @__PURE__ */ (0,
|
|
454
|
-
/* @__PURE__ */ (0,
|
|
455
|
-
/* @__PURE__ */ (0,
|
|
456
|
-
|
|
595
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "p-6", children: [
|
|
596
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "flex gap-2 mb-3", children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "bg-primary/10 text-primary text-xs font-medium px-2 py-1 rounded", children: article.category }) }),
|
|
597
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("h3", { className: "text-lg font-semibold leading-none tracking-tight mb-2", children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
598
|
+
import_link5.default,
|
|
457
599
|
{
|
|
458
600
|
href: `/articles/${article.slug}`,
|
|
459
601
|
className: "hover:text-indigo-600 transition-colors",
|
|
460
602
|
children: article.title
|
|
461
603
|
}
|
|
462
604
|
) }),
|
|
463
|
-
/* @__PURE__ */ (0,
|
|
464
|
-
/* @__PURE__ */ (0,
|
|
465
|
-
/* @__PURE__ */ (0,
|
|
466
|
-
article.date && /* @__PURE__ */ (0,
|
|
467
|
-
/* @__PURE__ */ (0,
|
|
468
|
-
/* @__PURE__ */ (0,
|
|
605
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("p", { className: "text-muted-foreground mb-4 line-clamp-3", children: article.excerpt }),
|
|
606
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "flex items-center justify-between text-sm text-muted-foreground border-t pt-4", children: [
|
|
607
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "flex items-center gap-3", children: [
|
|
608
|
+
article.date && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "flex items-center gap-2", children: [
|
|
609
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_lucide_react3.Calendar, { className: "h-3 w-3" }),
|
|
610
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { children: article.date })
|
|
469
611
|
] }),
|
|
470
|
-
/* @__PURE__ */ (0,
|
|
471
|
-
/* @__PURE__ */ (0,
|
|
472
|
-
/* @__PURE__ */ (0,
|
|
612
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "flex items-center gap-2", children: [
|
|
613
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_lucide_react3.Clock, { className: "h-3 w-3" }),
|
|
614
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { children: article.readTime })
|
|
473
615
|
] })
|
|
474
616
|
] }),
|
|
475
|
-
/* @__PURE__ */ (0,
|
|
476
|
-
|
|
617
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
618
|
+
import_link5.default,
|
|
477
619
|
{
|
|
478
620
|
href: `/articles/${article.slug}`,
|
|
479
621
|
className: "inline-flex items-center justify-center h-8 w-8 p-0 rounded-md hover:bg-accent transition-colors",
|
|
480
622
|
"aria-label": `Read ${article.title}`,
|
|
481
|
-
children: /* @__PURE__ */ (0,
|
|
623
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_lucide_react3.ArrowRight, { className: "h-4 w-4" })
|
|
482
624
|
}
|
|
483
625
|
)
|
|
484
626
|
] })
|
|
@@ -487,7 +629,7 @@ function ArticleCard({ article }) {
|
|
|
487
629
|
}
|
|
488
630
|
|
|
489
631
|
// src/LatestArticles.tsx
|
|
490
|
-
var
|
|
632
|
+
var import_jsx_runtime8 = require("react/jsx-runtime");
|
|
491
633
|
function LatestArticles({ articles, pageSize }) {
|
|
492
634
|
const [visibleCount, setVisibleCount] = (0, import_react2.useState)(pageSize);
|
|
493
635
|
(0, import_react2.useEffect)(() => {
|
|
@@ -495,9 +637,9 @@ function LatestArticles({ articles, pageSize }) {
|
|
|
495
637
|
}, [articles, pageSize]);
|
|
496
638
|
const visible = articles.slice(0, visibleCount);
|
|
497
639
|
const hasMore = visibleCount < articles.length;
|
|
498
|
-
return /* @__PURE__ */ (0,
|
|
499
|
-
/* @__PURE__ */ (0,
|
|
500
|
-
hasMore && /* @__PURE__ */ (0,
|
|
640
|
+
return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { children: [
|
|
641
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "grid md:grid-cols-2 lg:grid-cols-3 gap-8", children: visible.map((article) => /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(ArticleCard, { article }, article.slug)) }),
|
|
642
|
+
hasMore && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "mt-10 text-center", children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
501
643
|
"button",
|
|
502
644
|
{
|
|
503
645
|
type: "button",
|
|
@@ -510,14 +652,14 @@ function LatestArticles({ articles, pageSize }) {
|
|
|
510
652
|
}
|
|
511
653
|
|
|
512
654
|
// src/LatestArticlesSection.tsx
|
|
513
|
-
var
|
|
655
|
+
var import_jsx_runtime9 = require("react/jsx-runtime");
|
|
514
656
|
function DescriptionText({
|
|
515
657
|
searchQuery,
|
|
516
658
|
count
|
|
517
659
|
}) {
|
|
518
660
|
const pluralSuffix = count === 1 ? "" : "s";
|
|
519
661
|
const text = searchQuery ? `Found ${count} article${pluralSuffix} matching your search.` : "Stay informed with our latest insights on voter engagement, campaign strategies, and civic technology.";
|
|
520
|
-
return /* @__PURE__ */ (0,
|
|
662
|
+
return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("p", { className: "text-lg text-muted-foreground", children: text });
|
|
521
663
|
}
|
|
522
664
|
function LatestArticlesSection({
|
|
523
665
|
articles,
|
|
@@ -525,20 +667,20 @@ function LatestArticlesSection({
|
|
|
525
667
|
onClearSearch,
|
|
526
668
|
pageSize = 6
|
|
527
669
|
}) {
|
|
528
|
-
return /* @__PURE__ */ (0,
|
|
529
|
-
/* @__PURE__ */ (0,
|
|
530
|
-
/* @__PURE__ */ (0,
|
|
531
|
-
/* @__PURE__ */ (0,
|
|
670
|
+
return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("section", { id: "latest", className: "py-16 bg-muted/50", children: /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "max-w-7xl mx-auto px-4 sm:px-6 lg:px-8", children: [
|
|
671
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "text-center mb-12", children: [
|
|
672
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("h2", { className: "text-3xl font-bold text-foreground mb-4", children: searchQuery ? "Search Results" : "Latest Articles" }),
|
|
673
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(DescriptionText, { searchQuery, count: articles.length })
|
|
532
674
|
] }),
|
|
533
|
-
articles.length === 0 && searchQuery ? /* @__PURE__ */ (0,
|
|
534
|
-
/* @__PURE__ */ (0,
|
|
535
|
-
/* @__PURE__ */ (0,
|
|
536
|
-
/* @__PURE__ */ (0,
|
|
675
|
+
articles.length === 0 && searchQuery ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "text-center py-12", children: /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "max-w-md mx-auto", children: [
|
|
676
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_lucide_react4.Search, { className: "h-12 w-12 text-muted-foreground mx-auto mb-4" }),
|
|
677
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("h3", { className: "text-lg font-semibold text-foreground mb-2", children: "No articles found" }),
|
|
678
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("p", { className: "text-muted-foreground mb-4", children: [
|
|
537
679
|
`We couldn't find any articles matching "`,
|
|
538
680
|
searchQuery,
|
|
539
681
|
'". Try adjusting your search terms.'
|
|
540
682
|
] }),
|
|
541
|
-
/* @__PURE__ */ (0,
|
|
683
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
542
684
|
"button",
|
|
543
685
|
{
|
|
544
686
|
type: "button",
|
|
@@ -547,7 +689,7 @@ function LatestArticlesSection({
|
|
|
547
689
|
children: "Clear search"
|
|
548
690
|
}
|
|
549
691
|
)
|
|
550
|
-
] }) }) : /* @__PURE__ */ (0,
|
|
692
|
+
] }) }) : /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(LatestArticles, { articles, pageSize }, searchQuery)
|
|
551
693
|
] }) });
|
|
552
694
|
}
|
|
553
695
|
|
|
@@ -561,6 +703,15 @@ var DEFAULT_LAYOUT = [
|
|
|
561
703
|
"latest",
|
|
562
704
|
"categories"
|
|
563
705
|
];
|
|
706
|
+
function breadcrumbsAreEnabled(config) {
|
|
707
|
+
var _a;
|
|
708
|
+
return config.breadcrumbs !== false && ((_a = config.breadcrumbs) == null ? void 0 : _a.show) !== false;
|
|
709
|
+
}
|
|
710
|
+
function getBreadcrumbsConfig(config) {
|
|
711
|
+
var _a;
|
|
712
|
+
if (config.breadcrumbs === false) return {};
|
|
713
|
+
return (_a = config.breadcrumbs) != null ? _a : {};
|
|
714
|
+
}
|
|
564
715
|
|
|
565
716
|
// src/useArticles.ts
|
|
566
717
|
var import_react3 = require("react");
|
|
@@ -631,7 +782,7 @@ function useArticles() {
|
|
|
631
782
|
}
|
|
632
783
|
|
|
633
784
|
// src/ArticlesPage.tsx
|
|
634
|
-
var
|
|
785
|
+
var import_jsx_runtime10 = require("react/jsx-runtime");
|
|
635
786
|
function buildThemeVars(theme) {
|
|
636
787
|
if (!theme) return {};
|
|
637
788
|
const vars = {};
|
|
@@ -664,7 +815,7 @@ function renderSection(section, ctx, config) {
|
|
|
664
815
|
} = ctx;
|
|
665
816
|
switch (section) {
|
|
666
817
|
case "hero":
|
|
667
|
-
return /* @__PURE__ */ (0,
|
|
818
|
+
return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
668
819
|
ArticlesHero,
|
|
669
820
|
{
|
|
670
821
|
title: (_a = config.hero) == null ? void 0 : _a.title,
|
|
@@ -673,7 +824,7 @@ function renderSection(section, ctx, config) {
|
|
|
673
824
|
"hero"
|
|
674
825
|
);
|
|
675
826
|
case "search":
|
|
676
|
-
return /* @__PURE__ */ (0,
|
|
827
|
+
return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
677
828
|
ArticleSearchBar,
|
|
678
829
|
{
|
|
679
830
|
value: searchQuery,
|
|
@@ -684,7 +835,7 @@ function renderSection(section, ctx, config) {
|
|
|
684
835
|
"search"
|
|
685
836
|
);
|
|
686
837
|
case "featured":
|
|
687
|
-
return articles.length > 0 && !searchQuery ? /* @__PURE__ */ (0,
|
|
838
|
+
return articles.length > 0 && !searchQuery ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
688
839
|
FeaturedArticle,
|
|
689
840
|
{
|
|
690
841
|
article: articles[0],
|
|
@@ -694,16 +845,16 @@ function renderSection(section, ctx, config) {
|
|
|
694
845
|
) : null;
|
|
695
846
|
case "latest":
|
|
696
847
|
if (loading && articles.length === 0) {
|
|
697
|
-
return /* @__PURE__ */ (0,
|
|
698
|
-
/* @__PURE__ */ (0,
|
|
699
|
-
/* @__PURE__ */ (0,
|
|
848
|
+
return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("section", { id: "latest", className: "py-16 bg-muted/50 flex-1", children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 flex items-center justify-center py-24", children: /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "text-center", children: [
|
|
849
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "animate-spin rounded-full h-12 w-12 border-b-2 border-primary mx-auto mb-4" }),
|
|
850
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("p", { className: "text-muted-foreground", children: "Loading articles..." })
|
|
700
851
|
] }) }) }, "latest");
|
|
701
852
|
}
|
|
702
853
|
if (error) {
|
|
703
|
-
return /* @__PURE__ */ (0,
|
|
704
|
-
/* @__PURE__ */ (0,
|
|
705
|
-
/* @__PURE__ */ (0,
|
|
706
|
-
/* @__PURE__ */ (0,
|
|
854
|
+
return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("section", { id: "latest", className: "py-16 bg-muted/50 flex-1", children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 flex items-center justify-center", children: /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "bg-red-50 border border-red-200 rounded-lg p-6 max-w-md text-center", children: [
|
|
855
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("h3", { className: "text-lg font-semibold text-red-800 mb-2", children: "Error Loading Articles" }),
|
|
856
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("p", { className: "text-red-600 mb-4", children: error }),
|
|
857
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
707
858
|
"button",
|
|
708
859
|
{
|
|
709
860
|
type: "button",
|
|
@@ -714,7 +865,7 @@ function renderSection(section, ctx, config) {
|
|
|
714
865
|
)
|
|
715
866
|
] }) }) }, "latest");
|
|
716
867
|
}
|
|
717
|
-
return /* @__PURE__ */ (0,
|
|
868
|
+
return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
718
869
|
LatestArticlesSection,
|
|
719
870
|
{
|
|
720
871
|
articles: displayedArticles,
|
|
@@ -725,7 +876,7 @@ function renderSection(section, ctx, config) {
|
|
|
725
876
|
"latest"
|
|
726
877
|
);
|
|
727
878
|
case "categories":
|
|
728
|
-
return searchQuery ? null : /* @__PURE__ */ (0,
|
|
879
|
+
return searchQuery ? null : /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
729
880
|
ArticleCategoryGrid,
|
|
730
881
|
{
|
|
731
882
|
categories,
|
|
@@ -749,9 +900,9 @@ function ArticlesPage({ config }) {
|
|
|
749
900
|
const articlesWithoutFeatured = hasFeatured ? state.articles.slice(1) : state.articles;
|
|
750
901
|
const displayedArticles = state.searchQuery ? state.articles : articlesWithoutFeatured;
|
|
751
902
|
const ctx = __spreadProps(__spreadValues({}, state), { displayedArticles, pageSize, categoriesPageSize });
|
|
752
|
-
return /* @__PURE__ */ (0,
|
|
903
|
+
return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { style: themeVars, children: [
|
|
753
904
|
layout.map((section) => renderSection(section, ctx, config)),
|
|
754
|
-
state.articles.length > 0 && /* @__PURE__ */ (0,
|
|
905
|
+
state.articles.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
755
906
|
CollectionPageSchema,
|
|
756
907
|
{
|
|
757
908
|
title: `Articles | ${config.siteName}`,
|
|
@@ -764,10 +915,11 @@ function ArticlesPage({ config }) {
|
|
|
764
915
|
}
|
|
765
916
|
|
|
766
917
|
// src/ArticleDetailHero.tsx
|
|
918
|
+
var import_react4 = require("react");
|
|
767
919
|
var import_image4 = __toESM(require("next/image"), 1);
|
|
768
|
-
var
|
|
920
|
+
var import_link6 = __toESM(require("next/link"), 1);
|
|
769
921
|
var import_lucide_react5 = require("lucide-react");
|
|
770
|
-
var
|
|
922
|
+
var import_jsx_runtime11 = require("react/jsx-runtime");
|
|
771
923
|
function toSlug(cat) {
|
|
772
924
|
return cat.toLowerCase().replaceAll(/\s+/g, "-").replaceAll(/[^a-z0-9-]/g, "");
|
|
773
925
|
}
|
|
@@ -775,9 +927,12 @@ function ArticleDetailHero({
|
|
|
775
927
|
article,
|
|
776
928
|
categoryBasePath = "/articles/category",
|
|
777
929
|
showDate = false,
|
|
778
|
-
showAuthor = true
|
|
930
|
+
showAuthor = true,
|
|
931
|
+
authors = []
|
|
779
932
|
}) {
|
|
780
|
-
|
|
933
|
+
const showConfiguredAuthors = showAuthor && authors.length > 0;
|
|
934
|
+
const showLegacyAuthor = showAuthor && authors.length === 0 && article.author.trim().length > 0;
|
|
935
|
+
return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
|
|
781
936
|
"section",
|
|
782
937
|
{
|
|
783
938
|
style: {
|
|
@@ -789,7 +944,7 @@ function ArticleDetailHero({
|
|
|
789
944
|
overflow: "hidden"
|
|
790
945
|
},
|
|
791
946
|
children: [
|
|
792
|
-
/* @__PURE__ */ (0,
|
|
947
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
|
|
793
948
|
import_image4.default,
|
|
794
949
|
{
|
|
795
950
|
src: article.featuredImage,
|
|
@@ -801,7 +956,7 @@ function ArticleDetailHero({
|
|
|
801
956
|
priority: true
|
|
802
957
|
}
|
|
803
958
|
),
|
|
804
|
-
/* @__PURE__ */ (0,
|
|
959
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
|
|
805
960
|
"div",
|
|
806
961
|
{
|
|
807
962
|
style: {
|
|
@@ -811,7 +966,7 @@ function ArticleDetailHero({
|
|
|
811
966
|
}
|
|
812
967
|
}
|
|
813
968
|
),
|
|
814
|
-
/* @__PURE__ */ (0,
|
|
969
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
|
|
815
970
|
"div",
|
|
816
971
|
{
|
|
817
972
|
style: {
|
|
@@ -825,7 +980,7 @@ function ArticleDetailHero({
|
|
|
825
980
|
width: "100%"
|
|
826
981
|
},
|
|
827
982
|
children: [
|
|
828
|
-
article.categories && article.categories.length > 0 && /* @__PURE__ */ (0,
|
|
983
|
+
article.categories && article.categories.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
|
|
829
984
|
"div",
|
|
830
985
|
{
|
|
831
986
|
style: {
|
|
@@ -836,15 +991,15 @@ function ArticleDetailHero({
|
|
|
836
991
|
marginBottom: "1rem"
|
|
837
992
|
},
|
|
838
993
|
children: article.categories.slice(0, 4).map(
|
|
839
|
-
(cat) => categoryBasePath ? /* @__PURE__ */ (0,
|
|
840
|
-
|
|
994
|
+
(cat) => categoryBasePath ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
|
|
995
|
+
import_link6.default,
|
|
841
996
|
{
|
|
842
997
|
href: `${categoryBasePath}/${toSlug(cat)}`,
|
|
843
998
|
className: "inline-block bg-white/20 text-white text-sm font-medium px-3 py-1 rounded-full hover:bg-white/30 transition-colors",
|
|
844
999
|
children: cat
|
|
845
1000
|
},
|
|
846
1001
|
cat
|
|
847
|
-
) : /* @__PURE__ */ (0,
|
|
1002
|
+
) : /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
|
|
848
1003
|
"span",
|
|
849
1004
|
{
|
|
850
1005
|
className: "inline-block bg-white/20 text-white text-sm font-medium px-3 py-1 rounded-full",
|
|
@@ -855,8 +1010,8 @@ function ArticleDetailHero({
|
|
|
855
1010
|
)
|
|
856
1011
|
}
|
|
857
1012
|
),
|
|
858
|
-
/* @__PURE__ */ (0,
|
|
859
|
-
/* @__PURE__ */ (0,
|
|
1013
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("h1", { className: "text-4xl md:text-5xl font-bold mb-4", children: article.title }),
|
|
1014
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
|
|
860
1015
|
"div",
|
|
861
1016
|
{
|
|
862
1017
|
style: {
|
|
@@ -868,17 +1023,35 @@ function ArticleDetailHero({
|
|
|
868
1023
|
color: "rgba(255,255,255,0.8)"
|
|
869
1024
|
},
|
|
870
1025
|
children: [
|
|
871
|
-
showDate && article.date && /* @__PURE__ */ (0,
|
|
872
|
-
/* @__PURE__ */ (0,
|
|
873
|
-
/* @__PURE__ */ (0,
|
|
1026
|
+
showDate && article.date && /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: "0.5rem" }, children: [
|
|
1027
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_lucide_react5.Calendar, { className: "h-4 w-4" }),
|
|
1028
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { children: article.date })
|
|
874
1029
|
] }),
|
|
875
|
-
/* @__PURE__ */ (0,
|
|
876
|
-
/* @__PURE__ */ (0,
|
|
877
|
-
/* @__PURE__ */ (0,
|
|
1030
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: "0.5rem" }, children: [
|
|
1031
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_lucide_react5.Clock, { className: "h-4 w-4" }),
|
|
1032
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { children: article.readTime })
|
|
878
1033
|
] }),
|
|
879
|
-
|
|
880
|
-
/* @__PURE__ */ (0,
|
|
881
|
-
/* @__PURE__ */ (0,
|
|
1034
|
+
showConfiguredAuthors && /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: "0.5rem" }, children: [
|
|
1035
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_lucide_react5.User, { className: "h-4 w-4" }),
|
|
1036
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("span", { children: [
|
|
1037
|
+
"By",
|
|
1038
|
+
" ",
|
|
1039
|
+
authors.map((author, index) => /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_react4.Fragment, { children: [
|
|
1040
|
+
index > 0 && ", ",
|
|
1041
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
|
|
1042
|
+
import_link6.default,
|
|
1043
|
+
{
|
|
1044
|
+
href: `/articles/authors/${author.slug}`,
|
|
1045
|
+
className: "font-medium text-white underline-offset-4 hover:underline",
|
|
1046
|
+
children: author.name
|
|
1047
|
+
}
|
|
1048
|
+
)
|
|
1049
|
+
] }, author.slug))
|
|
1050
|
+
] })
|
|
1051
|
+
] }),
|
|
1052
|
+
showLegacyAuthor && /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: "0.5rem" }, children: [
|
|
1053
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_lucide_react5.User, { className: "h-4 w-4" }),
|
|
1054
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("span", { children: [
|
|
882
1055
|
"By ",
|
|
883
1056
|
article.author
|
|
884
1057
|
] })
|
|
@@ -896,8 +1069,8 @@ function ArticleDetailHero({
|
|
|
896
1069
|
|
|
897
1070
|
// src/CategoryArticlesPage.tsx
|
|
898
1071
|
var import_image5 = __toESM(require("next/image"), 1);
|
|
899
|
-
var
|
|
900
|
-
var
|
|
1072
|
+
var import_link7 = __toESM(require("next/link"), 1);
|
|
1073
|
+
var import_jsx_runtime12 = require("react/jsx-runtime");
|
|
901
1074
|
function getCategoryDescription(config, slug, categoryName) {
|
|
902
1075
|
var _a;
|
|
903
1076
|
const raw = (_a = config.categoryDescriptions) == null ? void 0 : _a[slug];
|
|
@@ -905,6 +1078,40 @@ function getCategoryDescription(config, slug, categoryName) {
|
|
|
905
1078
|
if (typeof raw === "string") return { short: raw };
|
|
906
1079
|
return raw;
|
|
907
1080
|
}
|
|
1081
|
+
function buildCategoryBreadcrumbItems(config, categoryName) {
|
|
1082
|
+
var _a;
|
|
1083
|
+
const siteUrl = config.siteUrl.replace(/\/$/, "");
|
|
1084
|
+
const breadcrumbConfig = getBreadcrumbsConfig(config);
|
|
1085
|
+
const trail = (_a = breadcrumbConfig.category) != null ? _a : ["home", "articles", "category"];
|
|
1086
|
+
return trail.flatMap(
|
|
1087
|
+
(entry) => {
|
|
1088
|
+
var _a2;
|
|
1089
|
+
return buildCategoryBreadcrumbEntry(entry, {
|
|
1090
|
+
categoryName,
|
|
1091
|
+
siteUrl,
|
|
1092
|
+
labels: (_a2 = breadcrumbConfig.labels) != null ? _a2 : {}
|
|
1093
|
+
});
|
|
1094
|
+
}
|
|
1095
|
+
);
|
|
1096
|
+
}
|
|
1097
|
+
function isCustomBreadcrumbItem(entry) {
|
|
1098
|
+
return typeof entry === "object" && entry !== null && "name" in entry && "url" in entry;
|
|
1099
|
+
}
|
|
1100
|
+
function resolveCustomBreadcrumbItem(item, siteUrl) {
|
|
1101
|
+
if (item.url.startsWith("/")) return { name: item.name, url: `${siteUrl}${item.url}` };
|
|
1102
|
+
return { name: item.name, url: item.url };
|
|
1103
|
+
}
|
|
1104
|
+
function buildCategoryBreadcrumbEntry(entry, context) {
|
|
1105
|
+
var _a, _b;
|
|
1106
|
+
if (isCustomBreadcrumbItem(entry)) {
|
|
1107
|
+
return [resolveCustomBreadcrumbItem(entry, context.siteUrl)];
|
|
1108
|
+
}
|
|
1109
|
+
if (entry === "home") return [{ name: (_a = context.labels.home) != null ? _a : "Home", url: context.siteUrl }];
|
|
1110
|
+
if (entry === "articles") {
|
|
1111
|
+
return [{ name: (_b = context.labels.articles) != null ? _b : "Articles", url: `${context.siteUrl}/articles` }];
|
|
1112
|
+
}
|
|
1113
|
+
return [{ name: context.categoryName }];
|
|
1114
|
+
}
|
|
908
1115
|
function CategoryArticlesPage({ category, articles, config }) {
|
|
909
1116
|
var _a;
|
|
910
1117
|
if (articles.length === 0) return null;
|
|
@@ -912,19 +1119,18 @@ function CategoryArticlesPage({ category, articles, config }) {
|
|
|
912
1119
|
const heroImage = articles[0].featuredImage;
|
|
913
1120
|
const description = getCategoryDescription(config, category, categoryName);
|
|
914
1121
|
const pageSize = (_a = config.pageSize) != null ? _a : DEFAULT_PAGE_SIZE;
|
|
915
|
-
const
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
1122
|
+
const breadcrumbConfig = getBreadcrumbsConfig(config);
|
|
1123
|
+
const breadcrumbItems = buildCategoryBreadcrumbItems(config, categoryName);
|
|
1124
|
+
return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { children: [
|
|
1125
|
+
breadcrumbsAreEnabled(config) && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
|
|
1126
|
+
Breadcrumb,
|
|
919
1127
|
{
|
|
920
|
-
items:
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
{ name: categoryName, url: `${siteUrl}/articles/category/${category}` }
|
|
924
|
-
]
|
|
1128
|
+
items: breadcrumbItems,
|
|
1129
|
+
separator: breadcrumbConfig.separator,
|
|
1130
|
+
showSchema: breadcrumbConfig.showSchema !== false
|
|
925
1131
|
}
|
|
926
1132
|
),
|
|
927
|
-
/* @__PURE__ */ (0,
|
|
1133
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
|
|
928
1134
|
"section",
|
|
929
1135
|
{
|
|
930
1136
|
style: {
|
|
@@ -936,7 +1142,7 @@ function CategoryArticlesPage({ category, articles, config }) {
|
|
|
936
1142
|
overflow: "hidden"
|
|
937
1143
|
},
|
|
938
1144
|
children: [
|
|
939
|
-
/* @__PURE__ */ (0,
|
|
1145
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
|
|
940
1146
|
import_image5.default,
|
|
941
1147
|
{
|
|
942
1148
|
src: heroImage,
|
|
@@ -947,8 +1153,8 @@ function CategoryArticlesPage({ category, articles, config }) {
|
|
|
947
1153
|
priority: true
|
|
948
1154
|
}
|
|
949
1155
|
),
|
|
950
|
-
/* @__PURE__ */ (0,
|
|
951
|
-
/* @__PURE__ */ (0,
|
|
1156
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { style: { position: "absolute", inset: 0, backgroundColor: "rgba(0,0,0,0.6)" } }),
|
|
1157
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
|
|
952
1158
|
"div",
|
|
953
1159
|
{
|
|
954
1160
|
style: {
|
|
@@ -959,10 +1165,10 @@ function CategoryArticlesPage({ category, articles, config }) {
|
|
|
959
1165
|
padding: "0 1rem"
|
|
960
1166
|
},
|
|
961
1167
|
children: [
|
|
962
|
-
/* @__PURE__ */ (0,
|
|
963
|
-
/* @__PURE__ */ (0,
|
|
964
|
-
description.long && /* @__PURE__ */ (0,
|
|
965
|
-
/* @__PURE__ */ (0,
|
|
1168
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)("p", { className: "text-sm font-semibold uppercase tracking-widest mb-3 opacity-80", children: "Category" }),
|
|
1169
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)("h1", { className: "text-4xl md:text-5xl font-bold mb-3", children: categoryName }),
|
|
1170
|
+
description.long && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("p", { className: "text-lg opacity-90 max-w-2xl mx-auto mt-2", children: description.long }),
|
|
1171
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("p", { className: "text-lg opacity-70 mt-2", children: [
|
|
966
1172
|
articles.length,
|
|
967
1173
|
" article",
|
|
968
1174
|
articles.length === 1 ? "" : "s"
|
|
@@ -973,22 +1179,170 @@ function CategoryArticlesPage({ category, articles, config }) {
|
|
|
973
1179
|
]
|
|
974
1180
|
}
|
|
975
1181
|
),
|
|
976
|
-
/* @__PURE__ */ (0,
|
|
977
|
-
/* @__PURE__ */ (0,
|
|
978
|
-
/* @__PURE__ */ (0,
|
|
979
|
-
/* @__PURE__ */ (0,
|
|
1182
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)("section", { className: "py-16 bg-muted/50 flex-1", children: /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "max-w-7xl mx-auto px-4 sm:px-6 lg:px-8", children: [
|
|
1183
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "mb-8 flex items-center justify-between", children: [
|
|
1184
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_link7.default, { href: "/articles", className: "text-sm text-primary hover:underline", children: "\u2190 All Articles" }),
|
|
1185
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)("p", { className: "text-sm text-muted-foreground", children: description.short })
|
|
980
1186
|
] }),
|
|
981
|
-
/* @__PURE__ */ (0,
|
|
1187
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)(LatestArticles, { articles, pageSize })
|
|
982
1188
|
] }) })
|
|
983
1189
|
] });
|
|
984
1190
|
}
|
|
985
1191
|
|
|
986
|
-
// src/
|
|
987
|
-
var
|
|
1192
|
+
// src/AuthorArticlesPage.tsx
|
|
1193
|
+
var import_link8 = __toESM(require("next/link"), 1);
|
|
1194
|
+
var import_jsx_runtime13 = require("react/jsx-runtime");
|
|
1195
|
+
function getPersonSchema(author, config, articleCount) {
|
|
1196
|
+
var _a, _b;
|
|
1197
|
+
return __spreadProps(__spreadValues(__spreadValues({
|
|
1198
|
+
"@context": "https://schema.org",
|
|
1199
|
+
"@type": "Person",
|
|
1200
|
+
name: author.name,
|
|
1201
|
+
description: author.bio,
|
|
1202
|
+
url: (_a = author.url) != null ? _a : getAuthorUrl(author)
|
|
1203
|
+
}, getAuthorAvatar(author, config) && { image: getAuthorAvatar(author, config) }), getAuthorSameAs(author).length > 0 && { sameAs: getAuthorSameAs(author) }), {
|
|
1204
|
+
knowsAbout: config.siteName,
|
|
1205
|
+
mainEntityOfPage: (_b = author.url) != null ? _b : `${config.siteUrl.replace(/\/$/, "")}/articles/authors/${author.slug}`,
|
|
1206
|
+
interactionStatistic: {
|
|
1207
|
+
"@type": "InteractionCounter",
|
|
1208
|
+
interactionType: "https://schema.org/WriteAction",
|
|
1209
|
+
userInteractionCount: articleCount
|
|
1210
|
+
}
|
|
1211
|
+
});
|
|
1212
|
+
}
|
|
1213
|
+
function AuthorArticlesPage({ author, articles, config }) {
|
|
1214
|
+
var _a;
|
|
1215
|
+
const pageSize = (_a = config.pageSize) != null ? _a : DEFAULT_PAGE_SIZE;
|
|
1216
|
+
return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { children: [
|
|
1217
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
1218
|
+
"script",
|
|
1219
|
+
{
|
|
1220
|
+
type: "application/ld+json",
|
|
1221
|
+
dangerouslySetInnerHTML: {
|
|
1222
|
+
__html: JSON.stringify(getPersonSchema(author, config, articles.length))
|
|
1223
|
+
}
|
|
1224
|
+
}
|
|
1225
|
+
),
|
|
1226
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("section", { className: "bg-background py-16", children: /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "mx-auto max-w-7xl px-4 sm:px-6 lg:px-8", children: [
|
|
1227
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "mb-8 flex items-center justify-between gap-4", children: [
|
|
1228
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_link8.default, { href: "/articles", className: "text-sm text-primary hover:underline", children: "All Articles" }),
|
|
1229
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("p", { className: "text-sm text-muted-foreground", children: [
|
|
1230
|
+
"Articles by ",
|
|
1231
|
+
author.name
|
|
1232
|
+
] })
|
|
1233
|
+
] }),
|
|
1234
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(LatestArticles, { articles, pageSize })
|
|
1235
|
+
] }) })
|
|
1236
|
+
] });
|
|
1237
|
+
}
|
|
1238
|
+
|
|
1239
|
+
// src/AuthorCard.tsx
|
|
1240
|
+
var import_image6 = __toESM(require("next/image"), 1);
|
|
1241
|
+
var import_link9 = __toESM(require("next/link"), 1);
|
|
988
1242
|
var import_lucide_react6 = require("lucide-react");
|
|
989
|
-
var
|
|
1243
|
+
var import_jsx_runtime14 = require("react/jsx-runtime");
|
|
1244
|
+
function getInitials(name) {
|
|
1245
|
+
return name.split(" ").filter(Boolean).slice(0, 2).map((part) => part.charAt(0).toUpperCase()).join("");
|
|
1246
|
+
}
|
|
1247
|
+
function AuthorCard({
|
|
1248
|
+
author,
|
|
1249
|
+
linkToPage = true,
|
|
1250
|
+
showBio = false,
|
|
1251
|
+
showSocial = false
|
|
1252
|
+
}) {
|
|
1253
|
+
const avatar = getAuthorAvatar(author);
|
|
1254
|
+
const name = linkToPage ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_link9.default, { href: getAuthorUrl(author), className: "font-medium text-foreground hover:text-primary", children: author.name }) : /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { className: "font-medium text-foreground", children: author.name });
|
|
1255
|
+
return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "flex items-start gap-3 rounded-lg border border-border bg-card p-4 text-card-foreground", children: [
|
|
1256
|
+
avatar ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
1257
|
+
import_image6.default,
|
|
1258
|
+
{
|
|
1259
|
+
src: avatar,
|
|
1260
|
+
alt: author.name,
|
|
1261
|
+
width: 48,
|
|
1262
|
+
height: 48,
|
|
1263
|
+
className: "h-12 w-12 rounded-full object-cover"
|
|
1264
|
+
}
|
|
1265
|
+
) : /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { className: "flex h-12 w-12 shrink-0 items-center justify-center rounded-full bg-muted text-sm font-semibold text-muted-foreground", children: getInitials(author.name) }),
|
|
1266
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "min-w-0 flex-1", children: [
|
|
1267
|
+
name,
|
|
1268
|
+
showBio && author.bio && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("p", { className: "mt-1 line-clamp-2 text-sm text-muted-foreground", children: author.bio }),
|
|
1269
|
+
showSocial && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(AuthorSocialLinks, { author })
|
|
1270
|
+
] })
|
|
1271
|
+
] });
|
|
1272
|
+
}
|
|
1273
|
+
var SOCIAL_ICONS = {
|
|
1274
|
+
Website: import_lucide_react6.Globe,
|
|
1275
|
+
Facebook: import_lucide_react6.Facebook,
|
|
1276
|
+
Twitter: import_lucide_react6.Twitter,
|
|
1277
|
+
X: import_lucide_react6.Twitter,
|
|
1278
|
+
LinkedIn: import_lucide_react6.Linkedin,
|
|
1279
|
+
Instagram: import_lucide_react6.Instagram,
|
|
1280
|
+
YouTube: import_lucide_react6.Youtube,
|
|
1281
|
+
GitHub: import_lucide_react6.Github,
|
|
1282
|
+
Newsletter: import_lucide_react6.Mail
|
|
1283
|
+
};
|
|
1284
|
+
function getSocialIcon(label) {
|
|
1285
|
+
var _a;
|
|
1286
|
+
return (_a = SOCIAL_ICONS[label]) != null ? _a : import_lucide_react6.MessageCircle;
|
|
1287
|
+
}
|
|
1288
|
+
function AuthorSocialLinks({ author }) {
|
|
1289
|
+
const links = getAuthorSocialLinks(author);
|
|
1290
|
+
if (links.length === 0) return null;
|
|
1291
|
+
return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { className: "mt-3 flex items-center gap-2", children: links.map(({ href, label }) => {
|
|
1292
|
+
const Icon = getSocialIcon(label);
|
|
1293
|
+
return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
1294
|
+
import_link9.default,
|
|
1295
|
+
{
|
|
1296
|
+
href,
|
|
1297
|
+
className: "inline-flex h-8 w-8 items-center justify-center rounded-md border border-border text-muted-foreground hover:bg-accent hover:text-accent-foreground",
|
|
1298
|
+
"aria-label": `${author.name} on ${label}`,
|
|
1299
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(Icon, { className: "h-4 w-4" })
|
|
1300
|
+
},
|
|
1301
|
+
`${label}-${href}`
|
|
1302
|
+
);
|
|
1303
|
+
}) });
|
|
1304
|
+
}
|
|
1305
|
+
|
|
1306
|
+
// src/AuthorDetailHero.tsx
|
|
1307
|
+
var import_image7 = __toESM(require("next/image"), 1);
|
|
1308
|
+
var import_jsx_runtime15 = require("react/jsx-runtime");
|
|
1309
|
+
function getInitials2(name) {
|
|
1310
|
+
return name.split(" ").filter(Boolean).slice(0, 2).map((part) => part.charAt(0).toUpperCase()).join("");
|
|
1311
|
+
}
|
|
1312
|
+
function AuthorDetailHero({ author, articleCount }) {
|
|
1313
|
+
const avatar = getAuthorAvatar(author);
|
|
1314
|
+
return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("section", { className: "bg-muted/40 py-16", children: /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "mx-auto flex max-w-4xl flex-col items-center gap-6 px-4 text-center sm:px-6 lg:px-8", children: [
|
|
1315
|
+
avatar ? /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
|
|
1316
|
+
import_image7.default,
|
|
1317
|
+
{
|
|
1318
|
+
src: avatar,
|
|
1319
|
+
alt: author.name,
|
|
1320
|
+
width: 120,
|
|
1321
|
+
height: 120,
|
|
1322
|
+
className: "h-28 w-28 rounded-full object-cover",
|
|
1323
|
+
priority: true
|
|
1324
|
+
}
|
|
1325
|
+
) : /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { className: "flex h-28 w-28 items-center justify-center rounded-full bg-card text-3xl font-semibold text-muted-foreground shadow-sm", children: getInitials2(author.name) }),
|
|
1326
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { children: [
|
|
1327
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)("p", { className: "mb-3 text-sm font-semibold uppercase tracking-widest text-muted-foreground", children: "Author" }),
|
|
1328
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)("h1", { className: "text-4xl font-bold text-foreground md:text-5xl", children: author.name }),
|
|
1329
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)("p", { className: "mx-auto mt-4 max-w-2xl text-lg text-muted-foreground", children: author.bio }),
|
|
1330
|
+
typeof articleCount === "number" && /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("p", { className: "mt-3 text-sm text-muted-foreground", children: [
|
|
1331
|
+
articleCount,
|
|
1332
|
+
" article",
|
|
1333
|
+
articleCount === 1 ? "" : "s"
|
|
1334
|
+
] })
|
|
1335
|
+
] }),
|
|
1336
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)(AuthorSocialLinks, { author })
|
|
1337
|
+
] }) });
|
|
1338
|
+
}
|
|
1339
|
+
|
|
1340
|
+
// src/ArticleSocialShare.tsx
|
|
1341
|
+
var import_react5 = require("react");
|
|
1342
|
+
var import_lucide_react7 = require("lucide-react");
|
|
1343
|
+
var import_jsx_runtime16 = require("react/jsx-runtime");
|
|
990
1344
|
function ArticleSocialShare({ title, url, excerpt, shareMessage }) {
|
|
991
|
-
const [copied, setCopied] = (0,
|
|
1345
|
+
const [copied, setCopied] = (0, import_react5.useState)(false);
|
|
992
1346
|
const encodedTitle = encodeURIComponent(title);
|
|
993
1347
|
const encodedUrl = encodeURIComponent(url);
|
|
994
1348
|
const encodedExcerpt = encodeURIComponent(excerpt || "");
|
|
@@ -1013,84 +1367,84 @@ function ArticleSocialShare({ title, url, excerpt, shareMessage }) {
|
|
|
1013
1367
|
globalThis.open(shareUrl, "_blank", "width=600,height=400,scrollbars=yes,resizable=yes");
|
|
1014
1368
|
};
|
|
1015
1369
|
const btnClass = "inline-flex items-center gap-2 px-3 py-1.5 text-sm rounded-md border border-border bg-background text-foreground hover:bg-primary/10 hover:border-primary hover:text-primary transition-colors cursor-pointer";
|
|
1016
|
-
return /* @__PURE__ */ (0,
|
|
1017
|
-
/* @__PURE__ */ (0,
|
|
1018
|
-
/* @__PURE__ */ (0,
|
|
1019
|
-
/* @__PURE__ */ (0,
|
|
1370
|
+
return /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { className: "border-t border-border pt-8 mt-8", children: [
|
|
1371
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { className: "flex items-center gap-2 mb-4", children: [
|
|
1372
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_lucide_react7.Share2, { className: "h-5 w-5 text-muted-foreground" }),
|
|
1373
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("h3", { className: "text-lg font-semibold text-foreground", children: "Share this article" })
|
|
1020
1374
|
] }),
|
|
1021
|
-
/* @__PURE__ */ (0,
|
|
1022
|
-
/* @__PURE__ */ (0,
|
|
1023
|
-
/* @__PURE__ */ (0,
|
|
1375
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { className: "flex flex-wrap gap-2", children: [
|
|
1376
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("button", { className: btnClass, onClick: () => openShareWindow(shareLinks.linkedin), children: [
|
|
1377
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_lucide_react7.Users, { className: "h-4 w-4" }),
|
|
1024
1378
|
"LinkedIn"
|
|
1025
1379
|
] }),
|
|
1026
|
-
/* @__PURE__ */ (0,
|
|
1027
|
-
/* @__PURE__ */ (0,
|
|
1380
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("button", { className: btnClass, onClick: () => openShareWindow(shareLinks.facebook), children: [
|
|
1381
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { className: "text-xs font-bold", children: "f" }),
|
|
1028
1382
|
" Facebook"
|
|
1029
1383
|
] }),
|
|
1030
|
-
/* @__PURE__ */ (0,
|
|
1031
|
-
/* @__PURE__ */ (0,
|
|
1384
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("button", { className: btnClass, onClick: () => openShareWindow(shareLinks.twitter), children: [
|
|
1385
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_lucide_react7.MessageCircle, { className: "h-4 w-4" }),
|
|
1032
1386
|
"Twitter"
|
|
1033
1387
|
] }),
|
|
1034
|
-
/* @__PURE__ */ (0,
|
|
1035
|
-
/* @__PURE__ */ (0,
|
|
1388
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("button", { className: btnClass, onClick: () => openShareWindow(shareLinks.reddit), children: [
|
|
1389
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_lucide_react7.FileText, { className: "h-4 w-4" }),
|
|
1036
1390
|
"Reddit"
|
|
1037
1391
|
] }),
|
|
1038
|
-
/* @__PURE__ */ (0,
|
|
1039
|
-
/* @__PURE__ */ (0,
|
|
1392
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("button", { className: btnClass, onClick: () => openShareWindow(shareLinks.whatsapp), children: [
|
|
1393
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_lucide_react7.MessageCircle, { className: "h-4 w-4" }),
|
|
1040
1394
|
"WhatsApp"
|
|
1041
1395
|
] }),
|
|
1042
|
-
/* @__PURE__ */ (0,
|
|
1043
|
-
/* @__PURE__ */ (0,
|
|
1396
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("button", { className: btnClass, onClick: () => openShareWindow(shareLinks.telegram), children: [
|
|
1397
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_lucide_react7.MessageCircle, { className: "h-4 w-4" }),
|
|
1044
1398
|
"Telegram"
|
|
1045
1399
|
] }),
|
|
1046
|
-
/* @__PURE__ */ (0,
|
|
1047
|
-
/* @__PURE__ */ (0,
|
|
1400
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("a", { className: btnClass, href: shareLinks.email, children: [
|
|
1401
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_lucide_react7.Mail, { className: "h-4 w-4" }),
|
|
1048
1402
|
"Email"
|
|
1049
1403
|
] }),
|
|
1050
|
-
/* @__PURE__ */ (0,
|
|
1051
|
-
copied ? /* @__PURE__ */ (0,
|
|
1404
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("button", { className: btnClass, onClick: copyToClipboard, children: [
|
|
1405
|
+
copied ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_lucide_react7.Check, { className: "h-4 w-4" }) : /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_lucide_react7.Copy, { className: "h-4 w-4" }),
|
|
1052
1406
|
copied ? "Copied!" : "Copy Link"
|
|
1053
1407
|
] })
|
|
1054
1408
|
] }),
|
|
1055
|
-
shareMessage && /* @__PURE__ */ (0,
|
|
1409
|
+
shareMessage && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("p", { className: "text-sm text-muted-foreground mt-3", children: shareMessage })
|
|
1056
1410
|
] });
|
|
1057
1411
|
}
|
|
1058
1412
|
|
|
1059
1413
|
// src/ArticleNavigation.tsx
|
|
1060
|
-
var
|
|
1061
|
-
var
|
|
1062
|
-
var
|
|
1414
|
+
var import_link10 = __toESM(require("next/link"), 1);
|
|
1415
|
+
var import_lucide_react8 = require("lucide-react");
|
|
1416
|
+
var import_jsx_runtime17 = require("react/jsx-runtime");
|
|
1063
1417
|
function truncateTitle(title, maxLength = 60) {
|
|
1064
1418
|
return title.length > maxLength ? `${title.substring(0, maxLength)}...` : title;
|
|
1065
1419
|
}
|
|
1066
1420
|
function ArticleNavigation({ previous, next, basePath }) {
|
|
1067
1421
|
if (!previous && !next) return null;
|
|
1068
|
-
return /* @__PURE__ */ (0,
|
|
1069
|
-
/* @__PURE__ */ (0,
|
|
1070
|
-
|
|
1422
|
+
return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("nav", { className: "mt-12 pt-8 border-t border-border", "aria-label": "Article navigation", children: /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: "flex justify-between gap-4", children: [
|
|
1423
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)("div", { className: "flex-1 min-w-0", children: previous && /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(
|
|
1424
|
+
import_link10.default,
|
|
1071
1425
|
{
|
|
1072
1426
|
href: `${basePath}/${previous.slug}`,
|
|
1073
1427
|
className: "group flex items-center gap-3 p-4 rounded-lg border border-border hover:border-primary/20 hover:bg-muted/50 transition-colors",
|
|
1074
1428
|
children: [
|
|
1075
|
-
/* @__PURE__ */ (0,
|
|
1076
|
-
/* @__PURE__ */ (0,
|
|
1077
|
-
/* @__PURE__ */ (0,
|
|
1078
|
-
/* @__PURE__ */ (0,
|
|
1429
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_lucide_react8.ChevronLeft, { className: "h-5 w-5 text-muted-foreground group-hover:text-primary shrink-0" }),
|
|
1430
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: "min-w-0 flex-1", children: [
|
|
1431
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)("div", { className: "text-sm text-muted-foreground mb-1", children: "Previous Article" }),
|
|
1432
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)("div", { className: "font-medium text-foreground truncate", title: previous.title, children: truncateTitle(previous.title) })
|
|
1079
1433
|
] })
|
|
1080
1434
|
]
|
|
1081
1435
|
}
|
|
1082
1436
|
) }),
|
|
1083
|
-
/* @__PURE__ */ (0,
|
|
1084
|
-
|
|
1437
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)("div", { className: "flex-1 min-w-0", children: next && /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(
|
|
1438
|
+
import_link10.default,
|
|
1085
1439
|
{
|
|
1086
1440
|
href: `${basePath}/${next.slug}`,
|
|
1087
1441
|
className: "group flex items-center justify-end gap-3 p-4 rounded-lg border border-border hover:border-primary/20 hover:bg-muted/50 transition-colors",
|
|
1088
1442
|
children: [
|
|
1089
|
-
/* @__PURE__ */ (0,
|
|
1090
|
-
/* @__PURE__ */ (0,
|
|
1091
|
-
/* @__PURE__ */ (0,
|
|
1443
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: "min-w-0 flex-1 text-right", children: [
|
|
1444
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)("div", { className: "text-sm text-muted-foreground mb-1", children: "Next Article" }),
|
|
1445
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)("div", { className: "font-medium text-foreground truncate", title: next.title, children: truncateTitle(next.title) })
|
|
1092
1446
|
] }),
|
|
1093
|
-
/* @__PURE__ */ (0,
|
|
1447
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_lucide_react8.ChevronRight, { className: "h-5 w-5 text-muted-foreground group-hover:text-primary shrink-0" })
|
|
1094
1448
|
]
|
|
1095
1449
|
}
|
|
1096
1450
|
) })
|
|
@@ -1098,9 +1452,9 @@ function ArticleNavigation({ previous, next, basePath }) {
|
|
|
1098
1452
|
}
|
|
1099
1453
|
|
|
1100
1454
|
// src/ArticleBackLink.tsx
|
|
1101
|
-
var
|
|
1102
|
-
var
|
|
1103
|
-
var
|
|
1455
|
+
var import_link11 = __toESM(require("next/link"), 1);
|
|
1456
|
+
var import_lucide_react9 = require("lucide-react");
|
|
1457
|
+
var import_jsx_runtime18 = require("react/jsx-runtime");
|
|
1104
1458
|
function ArticleBackLink({
|
|
1105
1459
|
config,
|
|
1106
1460
|
href = "/articles",
|
|
@@ -1108,13 +1462,13 @@ function ArticleBackLink({
|
|
|
1108
1462
|
className
|
|
1109
1463
|
}) {
|
|
1110
1464
|
if (config.showBackToArticles === false) return null;
|
|
1111
|
-
return /* @__PURE__ */ (0,
|
|
1112
|
-
|
|
1465
|
+
return /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(
|
|
1466
|
+
import_link11.default,
|
|
1113
1467
|
{
|
|
1114
1468
|
href,
|
|
1115
1469
|
className: className != null ? className : "inline-flex items-center gap-2 mb-6 -ml-3 px-3 py-2 rounded-md text-sm font-medium text-muted-foreground hover:text-foreground hover:bg-muted/50 transition-colors",
|
|
1116
1470
|
children: [
|
|
1117
|
-
/* @__PURE__ */ (0,
|
|
1471
|
+
/* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_lucide_react9.ArrowLeft, { className: "h-4 w-4" }),
|
|
1118
1472
|
label
|
|
1119
1473
|
]
|
|
1120
1474
|
}
|
|
@@ -1122,17 +1476,17 @@ function ArticleBackLink({
|
|
|
1122
1476
|
}
|
|
1123
1477
|
|
|
1124
1478
|
// src/ArticleTOC.tsx
|
|
1125
|
-
var
|
|
1479
|
+
var import_jsx_runtime19 = require("react/jsx-runtime");
|
|
1126
1480
|
function ArticleTOC({ toc, className }) {
|
|
1127
1481
|
if (!toc.length) return null;
|
|
1128
|
-
return /* @__PURE__ */ (0,
|
|
1482
|
+
return /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(
|
|
1129
1483
|
"nav",
|
|
1130
1484
|
{
|
|
1131
1485
|
"aria-label": "Table of contents",
|
|
1132
1486
|
className: `mb-8 rounded-lg border border-border bg-muted/40 px-6 py-4 ${className != null ? className : ""}`,
|
|
1133
1487
|
children: [
|
|
1134
|
-
/* @__PURE__ */ (0,
|
|
1135
|
-
/* @__PURE__ */ (0,
|
|
1488
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsx)("p", { className: "mb-3 text-sm font-semibold uppercase tracking-wide text-muted-foreground", children: "On this page" }),
|
|
1489
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsx)("ul", { className: "space-y-1 text-sm", children: toc.map((item) => /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("li", { style: { paddingLeft: `${Math.max(0, item.depth - 2) * 1}rem` }, children: /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
|
|
1136
1490
|
"a",
|
|
1137
1491
|
{
|
|
1138
1492
|
href: `#${item.id}`,
|
|
@@ -1146,35 +1500,35 @@ function ArticleTOC({ toc, className }) {
|
|
|
1146
1500
|
}
|
|
1147
1501
|
|
|
1148
1502
|
// src/ScrollToTop.tsx
|
|
1149
|
-
var
|
|
1150
|
-
var
|
|
1151
|
-
var
|
|
1503
|
+
var import_react6 = require("react");
|
|
1504
|
+
var import_lucide_react10 = require("lucide-react");
|
|
1505
|
+
var import_jsx_runtime20 = require("react/jsx-runtime");
|
|
1152
1506
|
function ScrollToTop() {
|
|
1153
|
-
const [visible, setVisible] = (0,
|
|
1154
|
-
(0,
|
|
1507
|
+
const [visible, setVisible] = (0, import_react6.useState)(false);
|
|
1508
|
+
(0, import_react6.useEffect)(() => {
|
|
1155
1509
|
const onScroll = () => setVisible(globalThis.scrollY > 300);
|
|
1156
1510
|
globalThis.addEventListener("scroll", onScroll, { passive: true });
|
|
1157
1511
|
return () => globalThis.removeEventListener("scroll", onScroll);
|
|
1158
1512
|
}, []);
|
|
1159
1513
|
if (!visible) return null;
|
|
1160
|
-
return /* @__PURE__ */ (0,
|
|
1514
|
+
return /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
|
|
1161
1515
|
"button",
|
|
1162
1516
|
{
|
|
1163
1517
|
"aria-label": "Scroll to top",
|
|
1164
1518
|
onClick: () => globalThis.scrollTo({ top: 0, behavior: "smooth" }),
|
|
1165
1519
|
className: "fixed bottom-8 right-8 z-50 rounded-full bg-primary p-2 shadow-md text-primary-foreground hover:bg-primary/90 transition-colors",
|
|
1166
|
-
children: /* @__PURE__ */ (0,
|
|
1520
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_lucide_react10.ArrowUp, { className: "h-5 w-5" })
|
|
1167
1521
|
}
|
|
1168
1522
|
);
|
|
1169
1523
|
}
|
|
1170
1524
|
|
|
1171
1525
|
// src/CommentsSection.tsx
|
|
1172
|
-
var
|
|
1173
|
-
var
|
|
1526
|
+
var import_react9 = require("next-auth/react");
|
|
1527
|
+
var import_react10 = require("react");
|
|
1174
1528
|
|
|
1175
1529
|
// src/CommentForm.tsx
|
|
1176
|
-
var
|
|
1177
|
-
var
|
|
1530
|
+
var import_react7 = require("react");
|
|
1531
|
+
var import_jsx_runtime21 = require("react/jsx-runtime");
|
|
1178
1532
|
var MAX_LENGTH = 2e3;
|
|
1179
1533
|
var WARN_THRESHOLD = 1800;
|
|
1180
1534
|
function submitLabel(submitting, parentId) {
|
|
@@ -1182,9 +1536,9 @@ function submitLabel(submitting, parentId) {
|
|
|
1182
1536
|
return parentId ? "Reply" : "Post comment";
|
|
1183
1537
|
}
|
|
1184
1538
|
function CommentForm({ articleSlug, parentId, onSuccess, onCancel }) {
|
|
1185
|
-
const [body, setBody] = (0,
|
|
1186
|
-
const [submitting, setSubmitting] = (0,
|
|
1187
|
-
const [error, setError] = (0,
|
|
1539
|
+
const [body, setBody] = (0, import_react7.useState)("");
|
|
1540
|
+
const [submitting, setSubmitting] = (0, import_react7.useState)(false);
|
|
1541
|
+
const [error, setError] = (0, import_react7.useState)(null);
|
|
1188
1542
|
const remaining = MAX_LENGTH - body.length;
|
|
1189
1543
|
function handleSubmit(e) {
|
|
1190
1544
|
return __async(this, null, function* () {
|
|
@@ -1213,8 +1567,8 @@ function CommentForm({ articleSlug, parentId, onSuccess, onCancel }) {
|
|
|
1213
1567
|
}
|
|
1214
1568
|
});
|
|
1215
1569
|
}
|
|
1216
|
-
return /* @__PURE__ */ (0,
|
|
1217
|
-
/* @__PURE__ */ (0,
|
|
1570
|
+
return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("form", { onSubmit: handleSubmit, className: "space-y-2", children: [
|
|
1571
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
|
|
1218
1572
|
"textarea",
|
|
1219
1573
|
{
|
|
1220
1574
|
value: body,
|
|
@@ -1227,13 +1581,13 @@ function CommentForm({ articleSlug, parentId, onSuccess, onCancel }) {
|
|
|
1227
1581
|
"aria-label": parentId ? "Reply text" : "Comment text"
|
|
1228
1582
|
}
|
|
1229
1583
|
),
|
|
1230
|
-
remaining <= MAX_LENGTH - WARN_THRESHOLD && /* @__PURE__ */ (0,
|
|
1584
|
+
remaining <= MAX_LENGTH - WARN_THRESHOLD && /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("p", { className: `text-xs ${remaining < 0 ? "text-destructive" : "text-muted-foreground"}`, children: [
|
|
1231
1585
|
remaining,
|
|
1232
1586
|
" characters remaining"
|
|
1233
1587
|
] }),
|
|
1234
|
-
error && /* @__PURE__ */ (0,
|
|
1235
|
-
/* @__PURE__ */ (0,
|
|
1236
|
-
/* @__PURE__ */ (0,
|
|
1588
|
+
error && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("p", { className: "text-xs text-destructive", children: error }),
|
|
1589
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { className: "flex gap-2", children: [
|
|
1590
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
|
|
1237
1591
|
"button",
|
|
1238
1592
|
{
|
|
1239
1593
|
type: "submit",
|
|
@@ -1242,7 +1596,7 @@ function CommentForm({ articleSlug, parentId, onSuccess, onCancel }) {
|
|
|
1242
1596
|
children: submitLabel(submitting, parentId)
|
|
1243
1597
|
}
|
|
1244
1598
|
),
|
|
1245
|
-
onCancel && /* @__PURE__ */ (0,
|
|
1599
|
+
onCancel && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
|
|
1246
1600
|
"button",
|
|
1247
1601
|
{
|
|
1248
1602
|
type: "button",
|
|
@@ -1256,8 +1610,8 @@ function CommentForm({ articleSlug, parentId, onSuccess, onCancel }) {
|
|
|
1256
1610
|
}
|
|
1257
1611
|
|
|
1258
1612
|
// src/CommentItem.tsx
|
|
1259
|
-
var
|
|
1260
|
-
var
|
|
1613
|
+
var import_react8 = require("react");
|
|
1614
|
+
var import_jsx_runtime22 = require("react/jsx-runtime");
|
|
1261
1615
|
function relativeTime(iso) {
|
|
1262
1616
|
const diff = Date.now() - new Date(iso).getTime();
|
|
1263
1617
|
const minutes = Math.floor(diff / 6e4);
|
|
@@ -1278,8 +1632,8 @@ function CommentItem({
|
|
|
1278
1632
|
isReply = false,
|
|
1279
1633
|
onRefresh
|
|
1280
1634
|
}) {
|
|
1281
|
-
const [showReplyForm, setShowReplyForm] = (0,
|
|
1282
|
-
const [deleting, setDeleting] = (0,
|
|
1635
|
+
const [showReplyForm, setShowReplyForm] = (0, import_react8.useState)(false);
|
|
1636
|
+
const [deleting, setDeleting] = (0, import_react8.useState)(false);
|
|
1283
1637
|
const replies = "replies" in comment ? comment.replies : [];
|
|
1284
1638
|
const canReply = !isReply && !!currentUserId && !comment.isDeleted;
|
|
1285
1639
|
const canDelete = !!currentUserId && currentUserId === comment.authorId && !comment.isDeleted;
|
|
@@ -1295,15 +1649,15 @@ function CommentItem({
|
|
|
1295
1649
|
}
|
|
1296
1650
|
});
|
|
1297
1651
|
}
|
|
1298
|
-
return /* @__PURE__ */ (0,
|
|
1299
|
-
/* @__PURE__ */ (0,
|
|
1300
|
-
/* @__PURE__ */ (0,
|
|
1301
|
-
/* @__PURE__ */ (0,
|
|
1302
|
-
/* @__PURE__ */ (0,
|
|
1652
|
+
return /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("div", { className: `${isReply ? "ml-8 border-l-2 border-border pl-4" : ""}`, children: [
|
|
1653
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { className: "rounded-lg bg-muted/40 p-3 space-y-1", children: comment.isDeleted ? /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("p", { className: "text-sm italic text-muted-foreground", children: "Comment removed" }) : /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(import_jsx_runtime22.Fragment, { children: [
|
|
1654
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("div", { className: "flex items-center justify-between gap-2", children: [
|
|
1655
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)("span", { className: "text-sm font-medium text-foreground", children: comment.authorName }),
|
|
1656
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)("span", { className: "text-xs text-muted-foreground", children: relativeTime(comment.createdAt) })
|
|
1303
1657
|
] }),
|
|
1304
|
-
/* @__PURE__ */ (0,
|
|
1305
|
-
/* @__PURE__ */ (0,
|
|
1306
|
-
canReply && /* @__PURE__ */ (0,
|
|
1658
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)("p", { className: "text-sm text-foreground whitespace-pre-wrap break-words", children: comment.body }),
|
|
1659
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("div", { className: "flex gap-3 pt-1", children: [
|
|
1660
|
+
canReply && /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
|
|
1307
1661
|
"button",
|
|
1308
1662
|
{
|
|
1309
1663
|
onClick: () => setShowReplyForm((v) => !v),
|
|
@@ -1311,7 +1665,7 @@ function CommentItem({
|
|
|
1311
1665
|
children: showReplyForm ? "Cancel" : "Reply"
|
|
1312
1666
|
}
|
|
1313
1667
|
),
|
|
1314
|
-
canDelete && /* @__PURE__ */ (0,
|
|
1668
|
+
canDelete && /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
|
|
1315
1669
|
"button",
|
|
1316
1670
|
{
|
|
1317
1671
|
onClick: handleDelete,
|
|
@@ -1322,7 +1676,7 @@ function CommentItem({
|
|
|
1322
1676
|
)
|
|
1323
1677
|
] })
|
|
1324
1678
|
] }) }),
|
|
1325
|
-
showReplyForm && /* @__PURE__ */ (0,
|
|
1679
|
+
showReplyForm && /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { className: "mt-2 ml-2", children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
|
|
1326
1680
|
CommentForm,
|
|
1327
1681
|
{
|
|
1328
1682
|
articleSlug,
|
|
@@ -1334,7 +1688,7 @@ function CommentItem({
|
|
|
1334
1688
|
onCancel: () => setShowReplyForm(false)
|
|
1335
1689
|
}
|
|
1336
1690
|
) }),
|
|
1337
|
-
replies.length > 0 && /* @__PURE__ */ (0,
|
|
1691
|
+
replies.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { className: "mt-2 space-y-2", children: replies.map((reply) => /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
|
|
1338
1692
|
CommentItem,
|
|
1339
1693
|
{
|
|
1340
1694
|
comment: __spreadProps(__spreadValues({}, reply), { replies: [] }),
|
|
@@ -1349,7 +1703,7 @@ function CommentItem({
|
|
|
1349
1703
|
}
|
|
1350
1704
|
|
|
1351
1705
|
// src/CommentThread.tsx
|
|
1352
|
-
var
|
|
1706
|
+
var import_jsx_runtime23 = require("react/jsx-runtime");
|
|
1353
1707
|
function CommentThread({
|
|
1354
1708
|
comments,
|
|
1355
1709
|
articleSlug,
|
|
@@ -1357,9 +1711,9 @@ function CommentThread({
|
|
|
1357
1711
|
onRefresh
|
|
1358
1712
|
}) {
|
|
1359
1713
|
if (comments.length === 0) {
|
|
1360
|
-
return /* @__PURE__ */ (0,
|
|
1714
|
+
return /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("p", { className: "text-sm text-muted-foreground text-center py-6", children: "No comments yet. Be the first to share your thoughts." });
|
|
1361
1715
|
}
|
|
1362
|
-
return /* @__PURE__ */ (0,
|
|
1716
|
+
return /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("div", { className: "space-y-4", children: comments.map((comment) => /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
|
|
1363
1717
|
CommentItem,
|
|
1364
1718
|
{
|
|
1365
1719
|
comment,
|
|
@@ -1372,17 +1726,17 @@ function CommentThread({
|
|
|
1372
1726
|
}
|
|
1373
1727
|
|
|
1374
1728
|
// src/CommentsSection.tsx
|
|
1375
|
-
var
|
|
1729
|
+
var import_jsx_runtime24 = require("react/jsx-runtime");
|
|
1376
1730
|
function CommentsSection({ articleSlug, config }) {
|
|
1377
1731
|
var _a, _b, _c, _d, _e, _f;
|
|
1378
|
-
const { data: session, status } = (0,
|
|
1379
|
-
const [comments, setComments] = (0,
|
|
1380
|
-
const [loading, setLoading] = (0,
|
|
1381
|
-
const [fetchError, setFetchError] = (0,
|
|
1732
|
+
const { data: session, status } = (0, import_react9.useSession)();
|
|
1733
|
+
const [comments, setComments] = (0, import_react10.useState)([]);
|
|
1734
|
+
const [loading, setLoading] = (0, import_react10.useState)(true);
|
|
1735
|
+
const [fetchError, setFetchError] = (0, import_react10.useState)(null);
|
|
1382
1736
|
const perArticleEnabled = (_b = (_a = config.perArticleOverride) == null ? void 0 : _a[articleSlug]) != null ? _b : true;
|
|
1383
1737
|
const enabled = config.enabled && perArticleEnabled;
|
|
1384
1738
|
const currentUserId = (_f = (_e = (_c = session == null ? void 0 : session.user) == null ? void 0 : _c.email) != null ? _e : (_d = session == null ? void 0 : session.user) == null ? void 0 : _d.name) != null ? _f : void 0;
|
|
1385
|
-
const fetchComments = (0,
|
|
1739
|
+
const fetchComments = (0, import_react10.useCallback)(() => __async(this, null, function* () {
|
|
1386
1740
|
setLoading(true);
|
|
1387
1741
|
setFetchError(null);
|
|
1388
1742
|
try {
|
|
@@ -1396,7 +1750,7 @@ function CommentsSection({ articleSlug, config }) {
|
|
|
1396
1750
|
setLoading(false);
|
|
1397
1751
|
}
|
|
1398
1752
|
}), [articleSlug]);
|
|
1399
|
-
(0,
|
|
1753
|
+
(0, import_react10.useEffect)(() => {
|
|
1400
1754
|
if (enabled) {
|
|
1401
1755
|
fetchComments().catch(() => void 0);
|
|
1402
1756
|
}
|
|
@@ -1404,13 +1758,13 @@ function CommentsSection({ articleSlug, config }) {
|
|
|
1404
1758
|
if (!enabled) return null;
|
|
1405
1759
|
const count = comments.length;
|
|
1406
1760
|
const label = count === 1 ? "1 comment" : `${count} comments`;
|
|
1407
|
-
return /* @__PURE__ */ (0,
|
|
1408
|
-
/* @__PURE__ */ (0,
|
|
1409
|
-
status === "authenticated" ? /* @__PURE__ */ (0,
|
|
1410
|
-
/* @__PURE__ */ (0,
|
|
1761
|
+
return /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("section", { "aria-label": "Comments", className: "mt-12 pt-8 border-t border-border space-y-6", children: [
|
|
1762
|
+
/* @__PURE__ */ (0, import_jsx_runtime24.jsx)("h2", { className: "text-xl font-semibold text-foreground", children: loading ? "Comments" : label }),
|
|
1763
|
+
status === "authenticated" ? /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(CommentForm, { articleSlug, onSuccess: fetchComments }) : /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("p", { className: "text-sm text-muted-foreground", children: [
|
|
1764
|
+
/* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
|
|
1411
1765
|
"button",
|
|
1412
1766
|
{
|
|
1413
|
-
onClick: () => (0,
|
|
1767
|
+
onClick: () => (0, import_react9.signIn)(),
|
|
1414
1768
|
className: "text-primary underline hover:no-underline focus:outline-none",
|
|
1415
1769
|
children: "Sign in"
|
|
1416
1770
|
}
|
|
@@ -1418,8 +1772,8 @@ function CommentsSection({ articleSlug, config }) {
|
|
|
1418
1772
|
" ",
|
|
1419
1773
|
"to join the discussion."
|
|
1420
1774
|
] }),
|
|
1421
|
-
fetchError && /* @__PURE__ */ (0,
|
|
1422
|
-
loading ? /* @__PURE__ */ (0,
|
|
1775
|
+
fetchError && /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("p", { className: "text-sm text-destructive", children: fetchError }),
|
|
1776
|
+
loading ? /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("p", { className: "text-sm text-muted-foreground", children: "Loading comments\u2026" }) : /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
|
|
1423
1777
|
CommentThread,
|
|
1424
1778
|
{
|
|
1425
1779
|
comments,
|
|
@@ -1444,6 +1798,11 @@ function CommentsSection({ articleSlug, config }) {
|
|
|
1444
1798
|
ArticleTOC,
|
|
1445
1799
|
ArticlesHero,
|
|
1446
1800
|
ArticlesPage,
|
|
1801
|
+
AuthorArticlesPage,
|
|
1802
|
+
AuthorCard,
|
|
1803
|
+
AuthorDetailHero,
|
|
1804
|
+
AuthorSocialLinks,
|
|
1805
|
+
Breadcrumb,
|
|
1447
1806
|
BreadcrumbSchema,
|
|
1448
1807
|
CategoryArticlesPage,
|
|
1449
1808
|
CollectionPageSchema,
|
|
@@ -1459,6 +1818,8 @@ function CommentsSection({ articleSlug, config }) {
|
|
|
1459
1818
|
LatestArticles,
|
|
1460
1819
|
LatestArticlesSection,
|
|
1461
1820
|
ScrollToTop,
|
|
1821
|
+
breadcrumbsAreEnabled,
|
|
1822
|
+
getBreadcrumbsConfig,
|
|
1462
1823
|
useArticles
|
|
1463
1824
|
});
|
|
1464
1825
|
//# sourceMappingURL=index.cjs.map
|