@fullstackdatasolutions/articles 0.4.3 → 0.5.1

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/dist/index.cjs CHANGED
@@ -73,9 +73,11 @@ __export(src_exports, {
73
73
  ArticleCategoryGrid: () => ArticleCategoryGrid,
74
74
  ArticleDetailHero: () => ArticleDetailHero,
75
75
  ArticleNavigation: () => ArticleNavigation,
76
+ ArticleSEO: () => ArticleSEO,
76
77
  ArticleSchema: () => ArticleSchema,
77
78
  ArticleSearchBar: () => ArticleSearchBar,
78
79
  ArticleSocialShare: () => ArticleSocialShare,
80
+ ArticleTOC: () => ArticleTOC,
79
81
  ArticlesHero: () => ArticlesHero,
80
82
  ArticlesPage: () => ArticlesPage,
81
83
  BreadcrumbSchema: () => BreadcrumbSchema,
@@ -88,9 +90,11 @@ __export(src_exports, {
88
90
  DEFAULT_CATEGORIES_PAGE_SIZE: () => DEFAULT_CATEGORIES_PAGE_SIZE,
89
91
  DEFAULT_LAYOUT: () => DEFAULT_LAYOUT,
90
92
  DEFAULT_PAGE_SIZE: () => DEFAULT_PAGE_SIZE,
93
+ FAQPageSchema: () => FAQPageSchema,
91
94
  FeaturedArticle: () => FeaturedArticle,
92
95
  LatestArticles: () => LatestArticles,
93
96
  LatestArticlesSection: () => LatestArticlesSection,
97
+ ScrollToTop: () => ScrollToTop,
94
98
  useArticles: () => useArticles
95
99
  });
96
100
  module.exports = __toCommonJS(src_exports);
@@ -186,6 +190,86 @@ function BreadcrumbSchema({ items }) {
186
190
  }
187
191
  );
188
192
  }
193
+ function ArticleSEO({ article, articleUrl, siteName, siteLogo }) {
194
+ const publishedAt = article.date ? new Date(article.date).toISOString() : void 0;
195
+ const modifiedAt = article.lastmod ? new Date(article.lastmod).toISOString() : publishedAt;
196
+ const structuredData = __spreadValues(__spreadProps(__spreadValues(__spreadValues({
197
+ "@context": "https://schema.org",
198
+ "@type": article.articleType || "Article",
199
+ mainEntityOfPage: { "@type": "WebPage", "@id": articleUrl },
200
+ headline: article.title,
201
+ image: article.featuredImage ? { "@type": "ImageObject", url: article.featuredImage } : void 0
202
+ }, publishedAt && { datePublished: publishedAt }), modifiedAt && { dateModified: modifiedAt }), {
203
+ author: { "@type": "Person", name: article.author },
204
+ publisher: __spreadValues({
205
+ "@type": "Organization",
206
+ name: siteName
207
+ }, siteLogo && { logo: { "@type": "ImageObject", url: siteLogo } }),
208
+ description: article.excerpt
209
+ }), article.series && { isPartOf: { "@type": "Blog", name: article.series } });
210
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_jsx_runtime2.Fragment, { children: [
211
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
212
+ "script",
213
+ {
214
+ type: "application/ld+json",
215
+ dangerouslySetInnerHTML: { __html: JSON.stringify(structuredData) }
216
+ }
217
+ ),
218
+ article.faq && article.faq.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
219
+ "script",
220
+ {
221
+ type: "application/ld+json",
222
+ dangerouslySetInnerHTML: {
223
+ __html: JSON.stringify({
224
+ "@context": "https://schema.org",
225
+ "@type": "FAQPage",
226
+ mainEntity: article.faq.map(({ question, answer }) => ({
227
+ "@type": "Question",
228
+ name: question,
229
+ acceptedAnswer: { "@type": "Answer", text: answer }
230
+ }))
231
+ })
232
+ }
233
+ }
234
+ ),
235
+ article.howTo && article.howTo.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
236
+ "script",
237
+ {
238
+ type: "application/ld+json",
239
+ dangerouslySetInnerHTML: {
240
+ __html: JSON.stringify({
241
+ "@context": "https://schema.org",
242
+ "@type": "HowTo",
243
+ name: article.title,
244
+ step: article.howTo.map(({ name, text }) => ({
245
+ "@type": "HowToStep",
246
+ name,
247
+ text
248
+ }))
249
+ })
250
+ }
251
+ }
252
+ )
253
+ ] });
254
+ }
255
+ function FAQPageSchema({ items }) {
256
+ const schema = {
257
+ "@context": "https://schema.org",
258
+ "@type": "FAQPage",
259
+ mainEntity: items.map(({ question, answer }) => ({
260
+ "@type": "Question",
261
+ name: question,
262
+ acceptedAnswer: { "@type": "Answer", text: answer }
263
+ }))
264
+ };
265
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
266
+ "script",
267
+ {
268
+ type: "application/ld+json",
269
+ dangerouslySetInnerHTML: { __html: JSON.stringify(schema) }
270
+ }
271
+ );
272
+ }
189
273
  function CollectionPageSchema({
190
274
  title,
191
275
  description,
@@ -908,7 +992,7 @@ function ArticleSocialShare({ title, url, excerpt, shareMessage }) {
908
992
  }
909
993
  });
910
994
  const openShareWindow = (shareUrl) => {
911
- window.open(shareUrl, "_blank", "width=600,height=400,scrollbars=yes,resizable=yes");
995
+ globalThis.open(shareUrl, "_blank", "width=600,height=400,scrollbars=yes,resizable=yes");
912
996
  };
913
997
  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";
914
998
  return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "border-t border-border pt-8 mt-8", children: [
@@ -1004,13 +1088,60 @@ function ArticleNavigation({ previous, next, basePath }) {
1004
1088
  ] }) });
1005
1089
  }
1006
1090
 
1091
+ // src/ArticleTOC.tsx
1092
+ var import_jsx_runtime14 = require("react/jsx-runtime");
1093
+ function ArticleTOC({ toc, className }) {
1094
+ if (!toc.length) return null;
1095
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(
1096
+ "nav",
1097
+ {
1098
+ "aria-label": "Table of contents",
1099
+ className: `mb-8 rounded-lg border border-border bg-muted/40 px-6 py-4 ${className != null ? className : ""}`,
1100
+ children: [
1101
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("p", { className: "mb-3 text-sm font-semibold uppercase tracking-wide text-muted-foreground", children: "On this page" }),
1102
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("ul", { className: "space-y-1 text-sm", children: toc.map((item) => /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("li", { style: { paddingLeft: `${Math.max(0, item.depth - 2) * 1}rem` }, children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
1103
+ "a",
1104
+ {
1105
+ href: `#${item.id}`,
1106
+ className: "text-muted-foreground hover:text-foreground transition-colors",
1107
+ children: item.text
1108
+ }
1109
+ ) }, item.id)) })
1110
+ ]
1111
+ }
1112
+ );
1113
+ }
1114
+
1115
+ // src/ScrollToTop.tsx
1116
+ var import_react5 = require("react");
1117
+ var import_lucide_react8 = require("lucide-react");
1118
+ var import_jsx_runtime15 = require("react/jsx-runtime");
1119
+ function ScrollToTop() {
1120
+ const [visible, setVisible] = (0, import_react5.useState)(false);
1121
+ (0, import_react5.useEffect)(() => {
1122
+ const onScroll = () => setVisible(globalThis.scrollY > 300);
1123
+ globalThis.addEventListener("scroll", onScroll, { passive: true });
1124
+ return () => globalThis.removeEventListener("scroll", onScroll);
1125
+ }, []);
1126
+ if (!visible) return null;
1127
+ return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
1128
+ "button",
1129
+ {
1130
+ "aria-label": "Scroll to top",
1131
+ onClick: () => globalThis.scrollTo({ top: 0, behavior: "smooth" }),
1132
+ 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",
1133
+ children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_lucide_react8.ArrowUp, { className: "h-5 w-5" })
1134
+ }
1135
+ );
1136
+ }
1137
+
1007
1138
  // src/CommentsSection.tsx
1008
- var import_react7 = require("next-auth/react");
1009
- var import_react8 = require("react");
1139
+ var import_react8 = require("next-auth/react");
1140
+ var import_react9 = require("react");
1010
1141
 
1011
1142
  // src/CommentForm.tsx
1012
- var import_react5 = require("react");
1013
- var import_jsx_runtime14 = require("react/jsx-runtime");
1143
+ var import_react6 = require("react");
1144
+ var import_jsx_runtime16 = require("react/jsx-runtime");
1014
1145
  var MAX_LENGTH = 2e3;
1015
1146
  var WARN_THRESHOLD = 1800;
1016
1147
  function submitLabel(submitting, parentId) {
@@ -1018,9 +1149,9 @@ function submitLabel(submitting, parentId) {
1018
1149
  return parentId ? "Reply" : "Post comment";
1019
1150
  }
1020
1151
  function CommentForm({ articleSlug, parentId, onSuccess, onCancel }) {
1021
- const [body, setBody] = (0, import_react5.useState)("");
1022
- const [submitting, setSubmitting] = (0, import_react5.useState)(false);
1023
- const [error, setError] = (0, import_react5.useState)(null);
1152
+ const [body, setBody] = (0, import_react6.useState)("");
1153
+ const [submitting, setSubmitting] = (0, import_react6.useState)(false);
1154
+ const [error, setError] = (0, import_react6.useState)(null);
1024
1155
  const remaining = MAX_LENGTH - body.length;
1025
1156
  function handleSubmit(e) {
1026
1157
  return __async(this, null, function* () {
@@ -1049,8 +1180,8 @@ function CommentForm({ articleSlug, parentId, onSuccess, onCancel }) {
1049
1180
  }
1050
1181
  });
1051
1182
  }
1052
- return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("form", { onSubmit: handleSubmit, className: "space-y-2", children: [
1053
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
1183
+ return /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("form", { onSubmit: handleSubmit, className: "space-y-2", children: [
1184
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
1054
1185
  "textarea",
1055
1186
  {
1056
1187
  value: body,
@@ -1063,13 +1194,13 @@ function CommentForm({ articleSlug, parentId, onSuccess, onCancel }) {
1063
1194
  "aria-label": parentId ? "Reply text" : "Comment text"
1064
1195
  }
1065
1196
  ),
1066
- remaining <= MAX_LENGTH - WARN_THRESHOLD && /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("p", { className: `text-xs ${remaining < 0 ? "text-destructive" : "text-muted-foreground"}`, children: [
1197
+ remaining <= MAX_LENGTH - WARN_THRESHOLD && /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("p", { className: `text-xs ${remaining < 0 ? "text-destructive" : "text-muted-foreground"}`, children: [
1067
1198
  remaining,
1068
1199
  " characters remaining"
1069
1200
  ] }),
1070
- error && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("p", { className: "text-xs text-destructive", children: error }),
1071
- /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "flex gap-2", children: [
1072
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
1201
+ error && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("p", { className: "text-xs text-destructive", children: error }),
1202
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { className: "flex gap-2", children: [
1203
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
1073
1204
  "button",
1074
1205
  {
1075
1206
  type: "submit",
@@ -1078,7 +1209,7 @@ function CommentForm({ articleSlug, parentId, onSuccess, onCancel }) {
1078
1209
  children: submitLabel(submitting, parentId)
1079
1210
  }
1080
1211
  ),
1081
- onCancel && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
1212
+ onCancel && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
1082
1213
  "button",
1083
1214
  {
1084
1215
  type: "button",
@@ -1092,8 +1223,8 @@ function CommentForm({ articleSlug, parentId, onSuccess, onCancel }) {
1092
1223
  }
1093
1224
 
1094
1225
  // src/CommentItem.tsx
1095
- var import_react6 = require("react");
1096
- var import_jsx_runtime15 = require("react/jsx-runtime");
1226
+ var import_react7 = require("react");
1227
+ var import_jsx_runtime17 = require("react/jsx-runtime");
1097
1228
  function relativeTime(iso) {
1098
1229
  const diff = Date.now() - new Date(iso).getTime();
1099
1230
  const minutes = Math.floor(diff / 6e4);
@@ -1114,8 +1245,8 @@ function CommentItem({
1114
1245
  isReply = false,
1115
1246
  onRefresh
1116
1247
  }) {
1117
- const [showReplyForm, setShowReplyForm] = (0, import_react6.useState)(false);
1118
- const [deleting, setDeleting] = (0, import_react6.useState)(false);
1248
+ const [showReplyForm, setShowReplyForm] = (0, import_react7.useState)(false);
1249
+ const [deleting, setDeleting] = (0, import_react7.useState)(false);
1119
1250
  const replies = "replies" in comment ? comment.replies : [];
1120
1251
  const canReply = !isReply && !!currentUserId && !comment.isDeleted;
1121
1252
  const canDelete = !!currentUserId && currentUserId === comment.authorId && !comment.isDeleted;
@@ -1131,15 +1262,15 @@ function CommentItem({
1131
1262
  }
1132
1263
  });
1133
1264
  }
1134
- return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: `${isReply ? "ml-8 border-l-2 border-border pl-4" : ""}`, children: [
1135
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { className: "rounded-lg bg-muted/40 p-3 space-y-1", children: comment.isDeleted ? /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("p", { className: "text-sm italic text-muted-foreground", children: "Comment removed" }) : /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(import_jsx_runtime15.Fragment, { children: [
1136
- /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "flex items-center justify-between gap-2", children: [
1137
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "text-sm font-medium text-foreground", children: comment.authorName }),
1138
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "text-xs text-muted-foreground", children: relativeTime(comment.createdAt) })
1265
+ return /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: `${isReply ? "ml-8 border-l-2 border-border pl-4" : ""}`, children: [
1266
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("div", { className: "rounded-lg bg-muted/40 p-3 space-y-1", children: comment.isDeleted ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("p", { className: "text-sm italic text-muted-foreground", children: "Comment removed" }) : /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(import_jsx_runtime17.Fragment, { children: [
1267
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: "flex items-center justify-between gap-2", children: [
1268
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { className: "text-sm font-medium text-foreground", children: comment.authorName }),
1269
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { className: "text-xs text-muted-foreground", children: relativeTime(comment.createdAt) })
1139
1270
  ] }),
1140
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("p", { className: "text-sm text-foreground whitespace-pre-wrap break-words", children: comment.body }),
1141
- /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "flex gap-3 pt-1", children: [
1142
- canReply && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
1271
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("p", { className: "text-sm text-foreground whitespace-pre-wrap break-words", children: comment.body }),
1272
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: "flex gap-3 pt-1", children: [
1273
+ canReply && /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
1143
1274
  "button",
1144
1275
  {
1145
1276
  onClick: () => setShowReplyForm((v) => !v),
@@ -1147,7 +1278,7 @@ function CommentItem({
1147
1278
  children: showReplyForm ? "Cancel" : "Reply"
1148
1279
  }
1149
1280
  ),
1150
- canDelete && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
1281
+ canDelete && /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
1151
1282
  "button",
1152
1283
  {
1153
1284
  onClick: handleDelete,
@@ -1158,7 +1289,7 @@ function CommentItem({
1158
1289
  )
1159
1290
  ] })
1160
1291
  ] }) }),
1161
- showReplyForm && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { className: "mt-2 ml-2", children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
1292
+ showReplyForm && /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("div", { className: "mt-2 ml-2", children: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
1162
1293
  CommentForm,
1163
1294
  {
1164
1295
  articleSlug,
@@ -1170,7 +1301,7 @@ function CommentItem({
1170
1301
  onCancel: () => setShowReplyForm(false)
1171
1302
  }
1172
1303
  ) }),
1173
- replies.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { className: "mt-2 space-y-2", children: replies.map((reply) => /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
1304
+ replies.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("div", { className: "mt-2 space-y-2", children: replies.map((reply) => /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
1174
1305
  CommentItem,
1175
1306
  {
1176
1307
  comment: __spreadProps(__spreadValues({}, reply), { replies: [] }),
@@ -1185,7 +1316,7 @@ function CommentItem({
1185
1316
  }
1186
1317
 
1187
1318
  // src/CommentThread.tsx
1188
- var import_jsx_runtime16 = require("react/jsx-runtime");
1319
+ var import_jsx_runtime18 = require("react/jsx-runtime");
1189
1320
  function CommentThread({
1190
1321
  comments,
1191
1322
  articleSlug,
@@ -1193,9 +1324,9 @@ function CommentThread({
1193
1324
  onRefresh
1194
1325
  }) {
1195
1326
  if (comments.length === 0) {
1196
- return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("p", { className: "text-sm text-muted-foreground text-center py-6", children: "No comments yet. Be the first to share your thoughts." });
1327
+ return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("p", { className: "text-sm text-muted-foreground text-center py-6", children: "No comments yet. Be the first to share your thoughts." });
1197
1328
  }
1198
- return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { className: "space-y-4", children: comments.map((comment) => /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
1329
+ return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { className: "space-y-4", children: comments.map((comment) => /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
1199
1330
  CommentItem,
1200
1331
  {
1201
1332
  comment,
@@ -1208,17 +1339,17 @@ function CommentThread({
1208
1339
  }
1209
1340
 
1210
1341
  // src/CommentsSection.tsx
1211
- var import_jsx_runtime17 = require("react/jsx-runtime");
1342
+ var import_jsx_runtime19 = require("react/jsx-runtime");
1212
1343
  function CommentsSection({ articleSlug, config }) {
1213
1344
  var _a, _b, _c, _d, _e, _f;
1214
- const { data: session, status } = (0, import_react7.useSession)();
1215
- const [comments, setComments] = (0, import_react8.useState)([]);
1216
- const [loading, setLoading] = (0, import_react8.useState)(true);
1217
- const [fetchError, setFetchError] = (0, import_react8.useState)(null);
1345
+ const { data: session, status } = (0, import_react8.useSession)();
1346
+ const [comments, setComments] = (0, import_react9.useState)([]);
1347
+ const [loading, setLoading] = (0, import_react9.useState)(true);
1348
+ const [fetchError, setFetchError] = (0, import_react9.useState)(null);
1218
1349
  const perArticleEnabled = (_b = (_a = config.perArticleOverride) == null ? void 0 : _a[articleSlug]) != null ? _b : true;
1219
1350
  const enabled = config.enabled && perArticleEnabled;
1220
1351
  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;
1221
- const fetchComments = (0, import_react8.useCallback)(() => __async(this, null, function* () {
1352
+ const fetchComments = (0, import_react9.useCallback)(() => __async(this, null, function* () {
1222
1353
  setLoading(true);
1223
1354
  setFetchError(null);
1224
1355
  try {
@@ -1232,7 +1363,7 @@ function CommentsSection({ articleSlug, config }) {
1232
1363
  setLoading(false);
1233
1364
  }
1234
1365
  }), [articleSlug]);
1235
- (0, import_react8.useEffect)(() => {
1366
+ (0, import_react9.useEffect)(() => {
1236
1367
  if (enabled) {
1237
1368
  fetchComments().catch(() => void 0);
1238
1369
  }
@@ -1240,13 +1371,13 @@ function CommentsSection({ articleSlug, config }) {
1240
1371
  if (!enabled) return null;
1241
1372
  const count = comments.length;
1242
1373
  const label = count === 1 ? "1 comment" : `${count} comments`;
1243
- return /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("section", { "aria-label": "Comments", className: "mt-12 pt-8 border-t border-border space-y-6", children: [
1244
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("h2", { className: "text-xl font-semibold text-foreground", children: loading ? "Comments" : label }),
1245
- status === "authenticated" ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(CommentForm, { articleSlug, onSuccess: fetchComments }) : /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("p", { className: "text-sm text-muted-foreground", children: [
1246
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
1374
+ return /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("section", { "aria-label": "Comments", className: "mt-12 pt-8 border-t border-border space-y-6", children: [
1375
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("h2", { className: "text-xl font-semibold text-foreground", children: loading ? "Comments" : label }),
1376
+ status === "authenticated" ? /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(CommentForm, { articleSlug, onSuccess: fetchComments }) : /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("p", { className: "text-sm text-muted-foreground", children: [
1377
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
1247
1378
  "button",
1248
1379
  {
1249
- onClick: () => (0, import_react7.signIn)(),
1380
+ onClick: () => (0, import_react8.signIn)(),
1250
1381
  className: "text-primary underline hover:no-underline focus:outline-none",
1251
1382
  children: "Sign in"
1252
1383
  }
@@ -1254,8 +1385,8 @@ function CommentsSection({ articleSlug, config }) {
1254
1385
  " ",
1255
1386
  "to join the discussion."
1256
1387
  ] }),
1257
- fetchError && /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("p", { className: "text-sm text-destructive", children: fetchError }),
1258
- loading ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("p", { className: "text-sm text-muted-foreground", children: "Loading comments\u2026" }) : /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
1388
+ fetchError && /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("p", { className: "text-sm text-destructive", children: fetchError }),
1389
+ loading ? /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("p", { className: "text-sm text-muted-foreground", children: "Loading comments\u2026" }) : /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
1259
1390
  CommentThread,
1260
1391
  {
1261
1392
  comments,
@@ -1272,9 +1403,11 @@ function CommentsSection({ articleSlug, config }) {
1272
1403
  ArticleCategoryGrid,
1273
1404
  ArticleDetailHero,
1274
1405
  ArticleNavigation,
1406
+ ArticleSEO,
1275
1407
  ArticleSchema,
1276
1408
  ArticleSearchBar,
1277
1409
  ArticleSocialShare,
1410
+ ArticleTOC,
1278
1411
  ArticlesHero,
1279
1412
  ArticlesPage,
1280
1413
  BreadcrumbSchema,
@@ -1287,9 +1420,11 @@ function CommentsSection({ articleSlug, config }) {
1287
1420
  DEFAULT_CATEGORIES_PAGE_SIZE,
1288
1421
  DEFAULT_LAYOUT,
1289
1422
  DEFAULT_PAGE_SIZE,
1423
+ FAQPageSchema,
1290
1424
  FeaturedArticle,
1291
1425
  LatestArticles,
1292
1426
  LatestArticlesSection,
1427
+ ScrollToTop,
1293
1428
  useArticles
1294
1429
  });
1295
1430
  //# sourceMappingURL=index.cjs.map