@fullstackdatasolutions/articles 1.2.2 → 1.3.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 +46 -0
- package/README.md +313 -1
- package/dist/index.cjs +308 -79
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +267 -16
- package/dist/index.d.ts +267 -16
- package/dist/index.js +300 -79
- package/dist/index.js.map +1 -1
- package/dist/nextjs.cjs +324 -13
- package/dist/nextjs.cjs.map +1 -1
- package/dist/nextjs.d.cts +179 -2
- package/dist/nextjs.d.ts +179 -2
- package/dist/nextjs.js +324 -13
- package/dist/nextjs.js.map +1 -1
- package/dist/server.cjs +677 -51
- package/dist/server.cjs.map +1 -1
- package/dist/server.d.cts +333 -12
- package/dist/server.d.ts +333 -12
- package/dist/server.js +662 -51
- package/dist/server.js.map +1 -1
- package/package.json +1 -1
- package/src/ArticleAnswer.tsx +35 -0
- package/src/ArticleSchemas.tsx +263 -23
- package/src/AuthorArticlesPage.tsx +38 -8
- package/src/__tests__/ArticleAnswer.test.tsx +25 -0
- package/src/__tests__/ArticleSchemas.test.tsx +516 -0
- package/src/__tests__/AuthorArticlesPage.test.tsx +76 -0
- package/src/__tests__/authorUtils.test.ts +50 -0
- package/src/__tests__/linkClassification.test.ts +55 -0
- package/src/__tests__/markdown.test.ts +77 -1
- package/src/__tests__/nextjs.test.ts +31 -15
- package/src/__tests__/renderMdx.test.tsx +162 -3
- package/src/__tests__/seoUtils.test.ts +279 -0
- package/src/__tests__/server-articles.test.ts +413 -1
- package/src/__tests__/validateArticles.test.ts +167 -6
- package/src/articleTypes.ts +57 -0
- package/src/articlesConfig.ts +176 -1
- package/src/authorUtils.ts +19 -1
- package/src/errorReporting.ts +1 -0
- package/src/index.ts +17 -1
- package/src/linkClassification.ts +30 -0
- package/src/markdown.ts +103 -25
- package/src/nextjs.ts +7 -4
- package/src/renderMdx.tsx +43 -6
- package/src/seoUtils.ts +247 -26
- package/src/server-articles.ts +375 -24
- package/src/server.ts +35 -4
- package/src/validateArticles.ts +157 -12
package/dist/index.js
CHANGED
|
@@ -90,7 +90,51 @@ function ArticleCategoryGrid({ categories, pageSize = 8 }) {
|
|
|
90
90
|
] }) });
|
|
91
91
|
}
|
|
92
92
|
|
|
93
|
+
// src/articlesConfig.ts
|
|
94
|
+
var DEFAULT_PAGE_SIZE = 6;
|
|
95
|
+
var DEFAULT_CATEGORIES_PAGE_SIZE = 8;
|
|
96
|
+
var DEFAULT_LAYOUT = [
|
|
97
|
+
"hero",
|
|
98
|
+
"search",
|
|
99
|
+
"featured",
|
|
100
|
+
"latest",
|
|
101
|
+
"categories"
|
|
102
|
+
];
|
|
103
|
+
function breadcrumbsAreEnabled(config) {
|
|
104
|
+
var _a;
|
|
105
|
+
return config.breadcrumbs !== false && ((_a = config.breadcrumbs) == null ? void 0 : _a.show) !== false;
|
|
106
|
+
}
|
|
107
|
+
function getBreadcrumbsConfig(config) {
|
|
108
|
+
var _a;
|
|
109
|
+
if (config.breadcrumbs === false) return {};
|
|
110
|
+
return (_a = config.breadcrumbs) != null ? _a : {};
|
|
111
|
+
}
|
|
112
|
+
function getOrganizationId(config) {
|
|
113
|
+
return `${config.siteUrl.replace(/\/$/, "")}/#organization`;
|
|
114
|
+
}
|
|
115
|
+
function getWebSiteId(config) {
|
|
116
|
+
return `${config.siteUrl.replace(/\/$/, "")}/#website`;
|
|
117
|
+
}
|
|
118
|
+
function getPersonId(authorUrl) {
|
|
119
|
+
return `${authorUrl.replace(/\/$/, "")}#person`;
|
|
120
|
+
}
|
|
121
|
+
function resolveEntityUrl(value, config) {
|
|
122
|
+
if (/^https?:\/\//.test(value)) return value;
|
|
123
|
+
return `${config.siteUrl.replace(/\/$/, "")}/${value.replace(/^\/+/, "")}`;
|
|
124
|
+
}
|
|
125
|
+
var DEFAULT_TITLE_TEMPLATE = "{title} | {siteName}";
|
|
126
|
+
function formatPageTitle(title, config) {
|
|
127
|
+
var _a;
|
|
128
|
+
return ((_a = config.titleTemplate) != null ? _a : DEFAULT_TITLE_TEMPLATE).replaceAll("{title}", title).replaceAll("{siteName}", config.siteName);
|
|
129
|
+
}
|
|
130
|
+
|
|
93
131
|
// src/authorUtils.ts
|
|
132
|
+
function getAuthorIdentityUrl(author, config) {
|
|
133
|
+
if (author.identityUrl) return author.identityUrl;
|
|
134
|
+
if (author.url) return author.url;
|
|
135
|
+
if (!config) return void 0;
|
|
136
|
+
return `${config.siteUrl.replace(/\/$/, "")}/articles/authors/${author.slug}`;
|
|
137
|
+
}
|
|
94
138
|
function getAuthorUrl(author) {
|
|
95
139
|
var _a;
|
|
96
140
|
return (_a = author.url) != null ? _a : `/articles/authors/${author.slug}`;
|
|
@@ -105,7 +149,10 @@ function getAuthorAvatar(author, config) {
|
|
|
105
149
|
return `${config.siteUrl.replace(/\/$/, "")}${path}`;
|
|
106
150
|
}
|
|
107
151
|
function getAuthorSameAs(author) {
|
|
108
|
-
|
|
152
|
+
var _a, _b;
|
|
153
|
+
const derived = getAuthorSocialLinks(author).map((link) => link.href);
|
|
154
|
+
const explicit = (_b = (_a = author.sameAs) == null ? void 0 : _a.filter((url) => url.trim() !== "")) != null ? _b : [];
|
|
155
|
+
return [.../* @__PURE__ */ new Set([...derived, ...explicit])];
|
|
109
156
|
}
|
|
110
157
|
function normalizeHandle(value) {
|
|
111
158
|
return value.replace(/^@/, "");
|
|
@@ -238,37 +285,129 @@ function getPersonSchemas(article, authors, config) {
|
|
|
238
285
|
} else if (article.author) {
|
|
239
286
|
resolvedAuthors = [{ name: article.author, slug: "", bio: "" }];
|
|
240
287
|
}
|
|
241
|
-
return resolvedAuthors.map((author) =>
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
288
|
+
return resolvedAuthors.map((author) => {
|
|
289
|
+
const identityUrl = getAuthorIdentityUrl(author, config);
|
|
290
|
+
return __spreadValues(__spreadValues(__spreadValues(__spreadProps(__spreadValues({
|
|
291
|
+
"@type": "Person"
|
|
292
|
+
}, identityUrl && { "@id": getPersonId(identityUrl) }), {
|
|
293
|
+
name: author.name
|
|
294
|
+
}), author.url && { url: author.url }), getAuthorAvatar(author, config) && { image: getAuthorAvatar(author, config) }), getAuthorSameAs(author).length > 0 && { sameAs: getAuthorSameAs(author) });
|
|
295
|
+
});
|
|
245
296
|
}
|
|
246
|
-
function
|
|
297
|
+
function resolveModifiedAt(article, publishedAt, config) {
|
|
298
|
+
if (article.lastmod) return new Date(article.lastmod).toISOString();
|
|
299
|
+
return (config == null ? void 0 : config.lastmodFallback) === "none" ? void 0 : publishedAt;
|
|
300
|
+
}
|
|
301
|
+
function resolveSchemaImageUrl(image, articleUrl, config) {
|
|
302
|
+
if (/^https?:\/\//.test(image)) return image;
|
|
303
|
+
if (config) return resolveEntityUrl(image, config);
|
|
304
|
+
try {
|
|
305
|
+
return `${new URL(articleUrl).origin}/${image.replace(/^\/+/, "")}`;
|
|
306
|
+
} catch (e) {
|
|
307
|
+
return image;
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
function buildPublisher(siteName, siteLogo, config) {
|
|
311
|
+
if (config == null ? void 0 : config.organization) return { "@id": getOrganizationId(config) };
|
|
312
|
+
return __spreadValues({
|
|
313
|
+
"@type": "Organization",
|
|
314
|
+
name: siteName
|
|
315
|
+
}, siteLogo && { logo: { "@type": "ImageObject", url: siteLogo } });
|
|
316
|
+
}
|
|
317
|
+
function buildQuestionEntity(article, articleUrl, publishedAt) {
|
|
318
|
+
if (article.articleType !== "QAPage" || !article.answer) return void 0;
|
|
319
|
+
return __spreadProps(__spreadValues({
|
|
320
|
+
"@type": "Question",
|
|
321
|
+
name: article.title,
|
|
322
|
+
text: article.excerpt || article.title
|
|
323
|
+
}, publishedAt && { dateCreated: publishedAt }), {
|
|
324
|
+
answerCount: 1,
|
|
325
|
+
acceptedAnswer: { "@type": "Answer", text: article.answer, url: articleUrl }
|
|
326
|
+
});
|
|
327
|
+
}
|
|
328
|
+
function buildCommentFields(comments) {
|
|
329
|
+
var _a;
|
|
330
|
+
const visible = (_a = comments == null ? void 0 : comments.filter((comment) => !comment.isDeleted && !comment.parentId)) != null ? _a : [];
|
|
331
|
+
if (visible.length === 0) return {};
|
|
332
|
+
return {
|
|
333
|
+
commentCount: visible.length,
|
|
334
|
+
comment: visible.map((comment) => ({
|
|
335
|
+
"@type": "Comment",
|
|
336
|
+
text: comment.body,
|
|
337
|
+
dateCreated: comment.createdAt,
|
|
338
|
+
author: { "@type": "Person", name: comment.authorName }
|
|
339
|
+
}))
|
|
340
|
+
};
|
|
341
|
+
}
|
|
342
|
+
function buildTopicFields(article, config) {
|
|
343
|
+
var _a, _b;
|
|
344
|
+
const about = (_a = article.about) == null ? void 0 : _a.map((entity) => __spreadValues({
|
|
345
|
+
"@type": "Thing",
|
|
346
|
+
name: entity.name
|
|
347
|
+
}, entity.sameAs && { sameAs: entity.sameAs }));
|
|
348
|
+
const citation = (_b = article.citation) == null ? void 0 : _b.map((source) => __spreadValues({
|
|
349
|
+
"@type": "CreativeWork",
|
|
350
|
+
name: source.name
|
|
351
|
+
}, source.url && { url: source.url }));
|
|
352
|
+
const selectors = config == null ? void 0 : config.speakableSelectors;
|
|
353
|
+
return __spreadValues(__spreadValues(__spreadValues({}, (about == null ? void 0 : about.length) && { about }), (citation == null ? void 0 : citation.length) && { citation }), (selectors == null ? void 0 : selectors.length) && {
|
|
354
|
+
speakable: { "@type": "SpeakableSpecification", cssSelector: selectors }
|
|
355
|
+
});
|
|
356
|
+
}
|
|
357
|
+
function buildArticleSchema({
|
|
247
358
|
article,
|
|
248
359
|
articleUrl,
|
|
249
360
|
siteName,
|
|
250
361
|
siteLogo,
|
|
251
|
-
showAuthor
|
|
252
|
-
|
|
253
|
-
config
|
|
362
|
+
showAuthor,
|
|
363
|
+
personSchemas,
|
|
364
|
+
config,
|
|
365
|
+
comments
|
|
254
366
|
}) {
|
|
367
|
+
var _a, _b;
|
|
255
368
|
const publishedAt = article.date ? new Date(article.date).toISOString() : void 0;
|
|
256
|
-
const modifiedAt = article
|
|
257
|
-
const
|
|
258
|
-
|
|
369
|
+
const modifiedAt = resolveModifiedAt(article, publishedAt, config);
|
|
370
|
+
const mainEntity = buildQuestionEntity(article, articleUrl, publishedAt);
|
|
371
|
+
return __spreadValues(__spreadValues(__spreadValues(__spreadProps(__spreadValues(__spreadValues(__spreadValues(__spreadProps(__spreadValues(__spreadProps(__spreadValues(__spreadValues(__spreadValues({
|
|
259
372
|
"@context": "https://schema.org",
|
|
260
373
|
"@type": article.articleType || "Article",
|
|
261
374
|
mainEntityOfPage: { "@type": "WebPage", "@id": articleUrl },
|
|
262
375
|
headline: article.title,
|
|
263
|
-
image: article.featuredImage ? {
|
|
376
|
+
image: article.featuredImage ? {
|
|
377
|
+
"@type": "ImageObject",
|
|
378
|
+
url: resolveSchemaImageUrl(article.featuredImage, articleUrl, config)
|
|
379
|
+
} : void 0
|
|
264
380
|
}, publishedAt && { datePublished: publishedAt }), modifiedAt && { dateModified: modifiedAt }), showAuthor && personSchemas.length > 0 && { author: personSchemas }), {
|
|
265
|
-
publisher:
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
}, siteLogo && { logo: { "@type": "ImageObject", url: siteLogo } }),
|
|
269
|
-
description: article.excerpt,
|
|
381
|
+
publisher: buildPublisher(siteName, siteLogo, config),
|
|
382
|
+
description: article.excerpt
|
|
383
|
+
}), article.answer && { abstract: article.answer }), {
|
|
270
384
|
articleSection: article.category
|
|
271
|
-
}),
|
|
385
|
+
}), ((_a = article.tags) == null ? void 0 : _a.length) && { keywords: article.tags.join(", ") }), article.wordCount !== void 0 && { wordCount: article.wordCount }), article.series && { isPartOf: { "@type": "Blog", name: article.series } }), {
|
|
386
|
+
inLanguage: (_b = config == null ? void 0 : config.language) != null ? _b : "en",
|
|
387
|
+
isAccessibleForFree: (config == null ? void 0 : config.isAccessibleForFree) !== false
|
|
388
|
+
}), buildTopicFields(article, config)), buildCommentFields(comments)), mainEntity && { mainEntity });
|
|
389
|
+
}
|
|
390
|
+
function ArticleSEO({
|
|
391
|
+
article,
|
|
392
|
+
articleUrl,
|
|
393
|
+
siteName,
|
|
394
|
+
siteLogo,
|
|
395
|
+
showAuthor = true,
|
|
396
|
+
authors,
|
|
397
|
+
config,
|
|
398
|
+
comments
|
|
399
|
+
}) {
|
|
400
|
+
const personSchemas = getPersonSchemas(article, authors, config);
|
|
401
|
+
const structuredData = buildArticleSchema({
|
|
402
|
+
article,
|
|
403
|
+
articleUrl,
|
|
404
|
+
siteName,
|
|
405
|
+
siteLogo,
|
|
406
|
+
showAuthor,
|
|
407
|
+
personSchemas,
|
|
408
|
+
config,
|
|
409
|
+
comments
|
|
410
|
+
});
|
|
272
411
|
return /* @__PURE__ */ jsxs3(Fragment2, { children: [
|
|
273
412
|
/* @__PURE__ */ jsx3(
|
|
274
413
|
"script",
|
|
@@ -365,6 +504,65 @@ function CollectionPageSchema({
|
|
|
365
504
|
}
|
|
366
505
|
);
|
|
367
506
|
}
|
|
507
|
+
function OrganizationSchema({ config }) {
|
|
508
|
+
var _a, _b, _c, _d, _e;
|
|
509
|
+
const org = config.organization;
|
|
510
|
+
if (!org) return null;
|
|
511
|
+
const schema = __spreadValues(__spreadValues(__spreadValues(__spreadValues({
|
|
512
|
+
"@context": "https://schema.org",
|
|
513
|
+
"@type": (_a = org.type) != null ? _a : "Organization",
|
|
514
|
+
"@id": getOrganizationId(config),
|
|
515
|
+
name: (_b = org.name) != null ? _b : config.siteName,
|
|
516
|
+
url: (_c = org.url) != null ? _c : config.siteUrl.replace(/\/$/, "")
|
|
517
|
+
}, org.logo && {
|
|
518
|
+
logo: { "@type": "ImageObject", url: resolveEntityUrl(org.logo, config) }
|
|
519
|
+
}), ((_d = org.description) != null ? _d : config.description) && {
|
|
520
|
+
description: (_e = org.description) != null ? _e : config.description
|
|
521
|
+
}), org.sameAs && org.sameAs.length > 0 && { sameAs: org.sameAs }), org.parentOrganization && {
|
|
522
|
+
parentOrganization: {
|
|
523
|
+
"@type": "Organization",
|
|
524
|
+
name: org.parentOrganization.name,
|
|
525
|
+
url: org.parentOrganization.url
|
|
526
|
+
}
|
|
527
|
+
});
|
|
528
|
+
return /* @__PURE__ */ jsx3(
|
|
529
|
+
"script",
|
|
530
|
+
{
|
|
531
|
+
type: "application/ld+json",
|
|
532
|
+
dangerouslySetInnerHTML: { __html: JSON.stringify(schema) }
|
|
533
|
+
}
|
|
534
|
+
);
|
|
535
|
+
}
|
|
536
|
+
function WebSiteSchema({ config }) {
|
|
537
|
+
const org = config.organization;
|
|
538
|
+
if (!org) return null;
|
|
539
|
+
const siteUrl = config.siteUrl.replace(/\/$/, "");
|
|
540
|
+
const template = org.searchUrlTemplate;
|
|
541
|
+
const schema = __spreadValues(__spreadValues({
|
|
542
|
+
"@context": "https://schema.org",
|
|
543
|
+
"@type": "WebSite",
|
|
544
|
+
"@id": getWebSiteId(config),
|
|
545
|
+
name: config.siteName,
|
|
546
|
+
url: siteUrl,
|
|
547
|
+
publisher: { "@id": getOrganizationId(config) }
|
|
548
|
+
}, config.description && { description: config.description }), template && {
|
|
549
|
+
potentialAction: {
|
|
550
|
+
"@type": "SearchAction",
|
|
551
|
+
target: {
|
|
552
|
+
"@type": "EntryPoint",
|
|
553
|
+
urlTemplate: resolveEntityUrl(template, config)
|
|
554
|
+
},
|
|
555
|
+
"query-input": "required name=search_term_string"
|
|
556
|
+
}
|
|
557
|
+
});
|
|
558
|
+
return /* @__PURE__ */ jsx3(
|
|
559
|
+
"script",
|
|
560
|
+
{
|
|
561
|
+
type: "application/ld+json",
|
|
562
|
+
dangerouslySetInnerHTML: { __html: JSON.stringify(schema) }
|
|
563
|
+
}
|
|
564
|
+
);
|
|
565
|
+
}
|
|
368
566
|
|
|
369
567
|
// src/ArticleSearchBar.tsx
|
|
370
568
|
import { Search, X } from "lucide-react";
|
|
@@ -840,26 +1038,6 @@ function LatestArticlesSection({
|
|
|
840
1038
|
] }) });
|
|
841
1039
|
}
|
|
842
1040
|
|
|
843
|
-
// src/articlesConfig.ts
|
|
844
|
-
var DEFAULT_PAGE_SIZE = 6;
|
|
845
|
-
var DEFAULT_CATEGORIES_PAGE_SIZE = 8;
|
|
846
|
-
var DEFAULT_LAYOUT = [
|
|
847
|
-
"hero",
|
|
848
|
-
"search",
|
|
849
|
-
"featured",
|
|
850
|
-
"latest",
|
|
851
|
-
"categories"
|
|
852
|
-
];
|
|
853
|
-
function breadcrumbsAreEnabled(config) {
|
|
854
|
-
var _a;
|
|
855
|
-
return config.breadcrumbs !== false && ((_a = config.breadcrumbs) == null ? void 0 : _a.show) !== false;
|
|
856
|
-
}
|
|
857
|
-
function getBreadcrumbsConfig(config) {
|
|
858
|
-
var _a;
|
|
859
|
-
if (config.breadcrumbs === false) return {};
|
|
860
|
-
return (_a = config.breadcrumbs) != null ? _a : {};
|
|
861
|
-
}
|
|
862
|
-
|
|
863
1041
|
// src/useArticles.ts
|
|
864
1042
|
import { useState as useState3, useCallback, useEffect as useEffect3, useRef } from "react";
|
|
865
1043
|
function useArticles(initialArticles, initialCategories) {
|
|
@@ -1562,17 +1740,28 @@ function CtaViewTracker({ ctaId, articleSlug, config, children }) {
|
|
|
1562
1740
|
|
|
1563
1741
|
// src/AuthorArticlesPage.tsx
|
|
1564
1742
|
import { Fragment as Fragment4, jsx as jsx18, jsxs as jsxs17 } from "react/jsx-runtime";
|
|
1565
|
-
function
|
|
1743
|
+
function getKnowsAbout(author, articles) {
|
|
1744
|
+
var _a;
|
|
1745
|
+
if ((_a = author.knowsAbout) == null ? void 0 : _a.length) return [...author.knowsAbout];
|
|
1746
|
+
return [...new Set(articles.flatMap((article) => {
|
|
1747
|
+
var _a2;
|
|
1748
|
+
return (_a2 = article.categories) != null ? _a2 : [];
|
|
1749
|
+
}))].filter(Boolean);
|
|
1750
|
+
}
|
|
1751
|
+
function getPersonSchema(author, config, articleCount, knowsAbout) {
|
|
1566
1752
|
var _a, _b;
|
|
1567
|
-
|
|
1753
|
+
const pageUrl = `${config.siteUrl.replace(/\/$/, "")}/articles/authors/${author.slug}`;
|
|
1754
|
+
const identityUrl = (_a = getAuthorIdentityUrl(author, config)) != null ? _a : pageUrl;
|
|
1755
|
+
const authorUrl = (_b = author.url) != null ? _b : pageUrl;
|
|
1756
|
+
return __spreadProps(__spreadValues(__spreadValues(__spreadValues(__spreadValues({
|
|
1568
1757
|
"@context": "https://schema.org",
|
|
1569
1758
|
"@type": "Person",
|
|
1759
|
+
"@id": getPersonId(identityUrl),
|
|
1570
1760
|
name: author.name,
|
|
1571
1761
|
description: author.bio,
|
|
1572
|
-
url:
|
|
1573
|
-
}, getAuthorAvatar(author, config) && { image: getAuthorAvatar(author, config) }), getAuthorSameAs(author).length > 0 && { sameAs: getAuthorSameAs(author) }), {
|
|
1574
|
-
|
|
1575
|
-
mainEntityOfPage: (_b = author.url) != null ? _b : `${config.siteUrl.replace(/\/$/, "")}/articles/authors/${author.slug}`,
|
|
1762
|
+
url: authorUrl
|
|
1763
|
+
}, getAuthorAvatar(author, config) && { image: getAuthorAvatar(author, config) }), getAuthorSameAs(author).length > 0 && { sameAs: getAuthorSameAs(author) }), knowsAbout.length > 0 && { knowsAbout: [...knowsAbout] }), config.organization && { worksFor: { "@id": getOrganizationId(config) } }), {
|
|
1764
|
+
mainEntityOfPage: pageUrl,
|
|
1576
1765
|
interactionStatistic: {
|
|
1577
1766
|
"@type": "InteractionCounter",
|
|
1578
1767
|
interactionType: "https://schema.org/WriteAction",
|
|
@@ -1725,7 +1914,9 @@ function AuthorArticlesPage({
|
|
|
1725
1914
|
{
|
|
1726
1915
|
type: "application/ld+json",
|
|
1727
1916
|
dangerouslySetInnerHTML: {
|
|
1728
|
-
__html: JSON.stringify(
|
|
1917
|
+
__html: JSON.stringify(
|
|
1918
|
+
getPersonSchema(author, config, articleCount, getKnowsAbout(author, articles))
|
|
1919
|
+
)
|
|
1729
1920
|
}
|
|
1730
1921
|
}
|
|
1731
1922
|
),
|
|
@@ -2056,10 +2247,32 @@ function ArticleTOC({ toc, className }) {
|
|
|
2056
2247
|
);
|
|
2057
2248
|
}
|
|
2058
2249
|
|
|
2250
|
+
// src/ArticleAnswer.tsx
|
|
2251
|
+
import { jsx as jsx24, jsxs as jsxs23 } from "react/jsx-runtime";
|
|
2252
|
+
function ArticleAnswer({
|
|
2253
|
+
article,
|
|
2254
|
+
label = "The short answer",
|
|
2255
|
+
className
|
|
2256
|
+
}) {
|
|
2257
|
+
var _a;
|
|
2258
|
+
if (!((_a = article.answer) == null ? void 0 : _a.trim())) return null;
|
|
2259
|
+
return /* @__PURE__ */ jsxs23(
|
|
2260
|
+
"aside",
|
|
2261
|
+
{
|
|
2262
|
+
className: `mb-8 rounded-lg border-l-4 border-primary bg-muted/40 px-6 py-4 ${className != null ? className : ""}`,
|
|
2263
|
+
style: { borderLeftWidth: "4px" },
|
|
2264
|
+
children: [
|
|
2265
|
+
/* @__PURE__ */ jsx24("p", { className: "mb-2 text-sm font-semibold uppercase tracking-wide text-muted-foreground", children: label }),
|
|
2266
|
+
/* @__PURE__ */ jsx24("p", { className: "text-base leading-relaxed", children: article.answer })
|
|
2267
|
+
]
|
|
2268
|
+
}
|
|
2269
|
+
);
|
|
2270
|
+
}
|
|
2271
|
+
|
|
2059
2272
|
// src/ScrollToTop.tsx
|
|
2060
2273
|
import { useEffect as useEffect5, useState as useState5 } from "react";
|
|
2061
2274
|
import { ArrowUp } from "lucide-react";
|
|
2062
|
-
import { jsx as
|
|
2275
|
+
import { jsx as jsx25 } from "react/jsx-runtime";
|
|
2063
2276
|
function ScrollToTop() {
|
|
2064
2277
|
const [visible, setVisible] = useState5(false);
|
|
2065
2278
|
useEffect5(() => {
|
|
@@ -2068,13 +2281,13 @@ function ScrollToTop() {
|
|
|
2068
2281
|
return () => globalThis.removeEventListener("scroll", onScroll);
|
|
2069
2282
|
}, []);
|
|
2070
2283
|
if (!visible) return null;
|
|
2071
|
-
return /* @__PURE__ */
|
|
2284
|
+
return /* @__PURE__ */ jsx25(
|
|
2072
2285
|
"button",
|
|
2073
2286
|
{
|
|
2074
2287
|
"aria-label": "Scroll to top",
|
|
2075
2288
|
onClick: () => globalThis.scrollTo({ top: 0, behavior: "smooth" }),
|
|
2076
2289
|
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",
|
|
2077
|
-
children: /* @__PURE__ */
|
|
2290
|
+
children: /* @__PURE__ */ jsx25(ArrowUp, { className: "h-5 w-5" })
|
|
2078
2291
|
}
|
|
2079
2292
|
);
|
|
2080
2293
|
}
|
|
@@ -2085,7 +2298,7 @@ import { useCallback as useCallback2, useEffect as useEffect6, useState as useSt
|
|
|
2085
2298
|
|
|
2086
2299
|
// src/CommentForm.tsx
|
|
2087
2300
|
import { useState as useState6 } from "react";
|
|
2088
|
-
import { jsx as
|
|
2301
|
+
import { jsx as jsx26, jsxs as jsxs24 } from "react/jsx-runtime";
|
|
2089
2302
|
var MAX_LENGTH = 2e3;
|
|
2090
2303
|
var WARN_THRESHOLD = 1800;
|
|
2091
2304
|
function submitLabel(submitting, parentId) {
|
|
@@ -2124,8 +2337,8 @@ function CommentForm({ articleSlug, parentId, onSuccess, onCancel }) {
|
|
|
2124
2337
|
}
|
|
2125
2338
|
});
|
|
2126
2339
|
}
|
|
2127
|
-
return /* @__PURE__ */
|
|
2128
|
-
/* @__PURE__ */
|
|
2340
|
+
return /* @__PURE__ */ jsxs24("form", { onSubmit: handleSubmit, className: "space-y-2", children: [
|
|
2341
|
+
/* @__PURE__ */ jsx26(
|
|
2129
2342
|
"textarea",
|
|
2130
2343
|
{
|
|
2131
2344
|
value: body,
|
|
@@ -2138,13 +2351,13 @@ function CommentForm({ articleSlug, parentId, onSuccess, onCancel }) {
|
|
|
2138
2351
|
"aria-label": parentId ? "Reply text" : "Comment text"
|
|
2139
2352
|
}
|
|
2140
2353
|
),
|
|
2141
|
-
remaining <= MAX_LENGTH - WARN_THRESHOLD && /* @__PURE__ */
|
|
2354
|
+
remaining <= MAX_LENGTH - WARN_THRESHOLD && /* @__PURE__ */ jsxs24("p", { className: `text-xs ${remaining < 0 ? "text-destructive" : "text-muted-foreground"}`, children: [
|
|
2142
2355
|
remaining,
|
|
2143
2356
|
" characters remaining"
|
|
2144
2357
|
] }),
|
|
2145
|
-
error && /* @__PURE__ */
|
|
2146
|
-
/* @__PURE__ */
|
|
2147
|
-
/* @__PURE__ */
|
|
2358
|
+
error && /* @__PURE__ */ jsx26("p", { className: "text-xs text-destructive", children: error }),
|
|
2359
|
+
/* @__PURE__ */ jsxs24("div", { className: "flex gap-2", children: [
|
|
2360
|
+
/* @__PURE__ */ jsx26(
|
|
2148
2361
|
"button",
|
|
2149
2362
|
{
|
|
2150
2363
|
type: "submit",
|
|
@@ -2153,7 +2366,7 @@ function CommentForm({ articleSlug, parentId, onSuccess, onCancel }) {
|
|
|
2153
2366
|
children: submitLabel(submitting, parentId)
|
|
2154
2367
|
}
|
|
2155
2368
|
),
|
|
2156
|
-
onCancel && /* @__PURE__ */
|
|
2369
|
+
onCancel && /* @__PURE__ */ jsx26(
|
|
2157
2370
|
"button",
|
|
2158
2371
|
{
|
|
2159
2372
|
type: "button",
|
|
@@ -2168,7 +2381,7 @@ function CommentForm({ articleSlug, parentId, onSuccess, onCancel }) {
|
|
|
2168
2381
|
|
|
2169
2382
|
// src/CommentItem.tsx
|
|
2170
2383
|
import { useState as useState7 } from "react";
|
|
2171
|
-
import { Fragment as Fragment5, jsx as
|
|
2384
|
+
import { Fragment as Fragment5, jsx as jsx27, jsxs as jsxs25 } from "react/jsx-runtime";
|
|
2172
2385
|
function relativeTime(iso) {
|
|
2173
2386
|
const diff = Date.now() - new Date(iso).getTime();
|
|
2174
2387
|
const minutes = Math.floor(diff / 6e4);
|
|
@@ -2206,15 +2419,15 @@ function CommentItem({
|
|
|
2206
2419
|
}
|
|
2207
2420
|
});
|
|
2208
2421
|
}
|
|
2209
|
-
return /* @__PURE__ */
|
|
2210
|
-
/* @__PURE__ */
|
|
2211
|
-
/* @__PURE__ */
|
|
2212
|
-
/* @__PURE__ */
|
|
2213
|
-
/* @__PURE__ */
|
|
2422
|
+
return /* @__PURE__ */ jsxs25("div", { className: `${isReply ? "ml-8 border-l-2 border-border pl-4" : ""}`, children: [
|
|
2423
|
+
/* @__PURE__ */ jsx27("div", { className: "rounded-lg bg-muted/40 p-3 space-y-1", children: comment.isDeleted ? /* @__PURE__ */ jsx27("p", { className: "text-sm italic text-muted-foreground", children: "Comment removed" }) : /* @__PURE__ */ jsxs25(Fragment5, { children: [
|
|
2424
|
+
/* @__PURE__ */ jsxs25("div", { className: "flex items-center justify-between gap-2", children: [
|
|
2425
|
+
/* @__PURE__ */ jsx27("span", { className: "text-sm font-medium text-foreground", children: comment.authorName }),
|
|
2426
|
+
/* @__PURE__ */ jsx27("span", { className: "text-xs text-muted-foreground", children: relativeTime(comment.createdAt) })
|
|
2214
2427
|
] }),
|
|
2215
|
-
/* @__PURE__ */
|
|
2216
|
-
/* @__PURE__ */
|
|
2217
|
-
canReply && /* @__PURE__ */
|
|
2428
|
+
/* @__PURE__ */ jsx27("p", { className: "text-sm text-foreground whitespace-pre-wrap break-words", children: comment.body }),
|
|
2429
|
+
/* @__PURE__ */ jsxs25("div", { className: "flex gap-3 pt-1", children: [
|
|
2430
|
+
canReply && /* @__PURE__ */ jsx27(
|
|
2218
2431
|
"button",
|
|
2219
2432
|
{
|
|
2220
2433
|
onClick: () => setShowReplyForm((v) => !v),
|
|
@@ -2222,7 +2435,7 @@ function CommentItem({
|
|
|
2222
2435
|
children: showReplyForm ? "Cancel" : "Reply"
|
|
2223
2436
|
}
|
|
2224
2437
|
),
|
|
2225
|
-
canDelete && /* @__PURE__ */
|
|
2438
|
+
canDelete && /* @__PURE__ */ jsx27(
|
|
2226
2439
|
"button",
|
|
2227
2440
|
{
|
|
2228
2441
|
onClick: handleDelete,
|
|
@@ -2233,7 +2446,7 @@ function CommentItem({
|
|
|
2233
2446
|
)
|
|
2234
2447
|
] })
|
|
2235
2448
|
] }) }),
|
|
2236
|
-
showReplyForm && /* @__PURE__ */
|
|
2449
|
+
showReplyForm && /* @__PURE__ */ jsx27("div", { className: "mt-2 ml-2", children: /* @__PURE__ */ jsx27(
|
|
2237
2450
|
CommentForm,
|
|
2238
2451
|
{
|
|
2239
2452
|
articleSlug,
|
|
@@ -2245,7 +2458,7 @@ function CommentItem({
|
|
|
2245
2458
|
onCancel: () => setShowReplyForm(false)
|
|
2246
2459
|
}
|
|
2247
2460
|
) }),
|
|
2248
|
-
replies.length > 0 && /* @__PURE__ */
|
|
2461
|
+
replies.length > 0 && /* @__PURE__ */ jsx27("div", { className: "mt-2 space-y-2", children: replies.map((reply) => /* @__PURE__ */ jsx27(
|
|
2249
2462
|
CommentItem,
|
|
2250
2463
|
{
|
|
2251
2464
|
comment: __spreadProps(__spreadValues({}, reply), { replies: [] }),
|
|
@@ -2260,7 +2473,7 @@ function CommentItem({
|
|
|
2260
2473
|
}
|
|
2261
2474
|
|
|
2262
2475
|
// src/CommentThread.tsx
|
|
2263
|
-
import { jsx as
|
|
2476
|
+
import { jsx as jsx28 } from "react/jsx-runtime";
|
|
2264
2477
|
function CommentThread({
|
|
2265
2478
|
comments,
|
|
2266
2479
|
articleSlug,
|
|
@@ -2268,9 +2481,9 @@ function CommentThread({
|
|
|
2268
2481
|
onRefresh
|
|
2269
2482
|
}) {
|
|
2270
2483
|
if (comments.length === 0) {
|
|
2271
|
-
return /* @__PURE__ */
|
|
2484
|
+
return /* @__PURE__ */ jsx28("p", { className: "text-sm text-muted-foreground text-center py-6", children: "No comments yet. Be the first to share your thoughts." });
|
|
2272
2485
|
}
|
|
2273
|
-
return /* @__PURE__ */
|
|
2486
|
+
return /* @__PURE__ */ jsx28("div", { className: "space-y-4", children: comments.map((comment) => /* @__PURE__ */ jsx28(
|
|
2274
2487
|
CommentItem,
|
|
2275
2488
|
{
|
|
2276
2489
|
comment,
|
|
@@ -2283,7 +2496,7 @@ function CommentThread({
|
|
|
2283
2496
|
}
|
|
2284
2497
|
|
|
2285
2498
|
// src/CommentsSection.tsx
|
|
2286
|
-
import { jsx as
|
|
2499
|
+
import { jsx as jsx29, jsxs as jsxs26 } from "react/jsx-runtime";
|
|
2287
2500
|
function CommentsSection({ articleSlug, config }) {
|
|
2288
2501
|
var _a, _b, _c, _d, _e, _f;
|
|
2289
2502
|
const { data: session, status } = useSession();
|
|
@@ -2315,10 +2528,10 @@ function CommentsSection({ articleSlug, config }) {
|
|
|
2315
2528
|
if (!enabled) return null;
|
|
2316
2529
|
const count = comments.length;
|
|
2317
2530
|
const label = count === 1 ? "1 comment" : `${count} comments`;
|
|
2318
|
-
return /* @__PURE__ */
|
|
2319
|
-
/* @__PURE__ */
|
|
2320
|
-
status === "authenticated" ? /* @__PURE__ */
|
|
2321
|
-
/* @__PURE__ */
|
|
2531
|
+
return /* @__PURE__ */ jsxs26("section", { "aria-label": "Comments", className: "mt-12 pt-8 border-t border-border space-y-6", children: [
|
|
2532
|
+
/* @__PURE__ */ jsx29("h2", { className: "text-xl font-semibold text-foreground", children: loading ? "Comments" : label }),
|
|
2533
|
+
status === "authenticated" ? /* @__PURE__ */ jsx29(CommentForm, { articleSlug, onSuccess: fetchComments }) : /* @__PURE__ */ jsxs26("p", { className: "text-sm text-muted-foreground", children: [
|
|
2534
|
+
/* @__PURE__ */ jsx29(
|
|
2322
2535
|
"button",
|
|
2323
2536
|
{
|
|
2324
2537
|
onClick: () => signIn(),
|
|
@@ -2329,8 +2542,8 @@ function CommentsSection({ articleSlug, config }) {
|
|
|
2329
2542
|
" ",
|
|
2330
2543
|
"to join the discussion."
|
|
2331
2544
|
] }),
|
|
2332
|
-
fetchError && /* @__PURE__ */
|
|
2333
|
-
loading ? /* @__PURE__ */
|
|
2545
|
+
fetchError && /* @__PURE__ */ jsx29("p", { className: "text-sm text-destructive", children: fetchError }),
|
|
2546
|
+
loading ? /* @__PURE__ */ jsx29("p", { className: "text-sm text-muted-foreground", children: "Loading comments\u2026" }) : /* @__PURE__ */ jsx29(
|
|
2334
2547
|
CommentThread,
|
|
2335
2548
|
{
|
|
2336
2549
|
comments,
|
|
@@ -2342,6 +2555,7 @@ function CommentsSection({ articleSlug, config }) {
|
|
|
2342
2555
|
] });
|
|
2343
2556
|
}
|
|
2344
2557
|
export {
|
|
2558
|
+
ArticleAnswer,
|
|
2345
2559
|
ArticleBackLink,
|
|
2346
2560
|
ArticleCard,
|
|
2347
2561
|
ArticleCategoryGrid,
|
|
@@ -2370,16 +2584,23 @@ export {
|
|
|
2370
2584
|
DEFAULT_CATEGORIES_PAGE_SIZE,
|
|
2371
2585
|
DEFAULT_LAYOUT,
|
|
2372
2586
|
DEFAULT_PAGE_SIZE,
|
|
2587
|
+
DEFAULT_TITLE_TEMPLATE,
|
|
2373
2588
|
FAQPageSchema,
|
|
2374
2589
|
FeaturedArticle,
|
|
2375
2590
|
LatestArticles,
|
|
2376
2591
|
LatestArticlesSection,
|
|
2592
|
+
OrganizationSchema,
|
|
2377
2593
|
PaginationNav,
|
|
2378
2594
|
RelatedArticlesSection,
|
|
2379
2595
|
ScrollToTop,
|
|
2380
2596
|
SeriesArticlesPage,
|
|
2597
|
+
WebSiteSchema,
|
|
2381
2598
|
breadcrumbsAreEnabled,
|
|
2599
|
+
formatPageTitle,
|
|
2382
2600
|
getBreadcrumbsConfig,
|
|
2601
|
+
getOrganizationId,
|
|
2602
|
+
getPersonId,
|
|
2603
|
+
getWebSiteId,
|
|
2383
2604
|
useArticles
|
|
2384
2605
|
};
|
|
2385
2606
|
//# sourceMappingURL=index.js.map
|