@fullstackdatasolutions/articles 0.5.1 → 0.7.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/dist/index.js CHANGED
@@ -1029,18 +1029,42 @@ function ArticleNavigation({ previous, next, basePath }) {
1029
1029
  ] }) });
1030
1030
  }
1031
1031
 
1032
- // src/ArticleTOC.tsx
1032
+ // src/ArticleBackLink.tsx
1033
+ import Link8 from "next/link";
1034
+ import { ArrowLeft } from "lucide-react";
1033
1035
  import { jsx as jsx14, jsxs as jsxs14 } from "react/jsx-runtime";
1036
+ function ArticleBackLink({
1037
+ config,
1038
+ href = "/articles",
1039
+ label = "Back to Articles",
1040
+ className
1041
+ }) {
1042
+ if (config.showBackToArticles === false) return null;
1043
+ return /* @__PURE__ */ jsxs14(
1044
+ Link8,
1045
+ {
1046
+ href,
1047
+ 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",
1048
+ children: [
1049
+ /* @__PURE__ */ jsx14(ArrowLeft, { className: "h-4 w-4" }),
1050
+ label
1051
+ ]
1052
+ }
1053
+ );
1054
+ }
1055
+
1056
+ // src/ArticleTOC.tsx
1057
+ import { jsx as jsx15, jsxs as jsxs15 } from "react/jsx-runtime";
1034
1058
  function ArticleTOC({ toc, className }) {
1035
1059
  if (!toc.length) return null;
1036
- return /* @__PURE__ */ jsxs14(
1060
+ return /* @__PURE__ */ jsxs15(
1037
1061
  "nav",
1038
1062
  {
1039
1063
  "aria-label": "Table of contents",
1040
1064
  className: `mb-8 rounded-lg border border-border bg-muted/40 px-6 py-4 ${className != null ? className : ""}`,
1041
1065
  children: [
1042
- /* @__PURE__ */ jsx14("p", { className: "mb-3 text-sm font-semibold uppercase tracking-wide text-muted-foreground", children: "On this page" }),
1043
- /* @__PURE__ */ jsx14("ul", { className: "space-y-1 text-sm", children: toc.map((item) => /* @__PURE__ */ jsx14("li", { style: { paddingLeft: `${Math.max(0, item.depth - 2) * 1}rem` }, children: /* @__PURE__ */ jsx14(
1066
+ /* @__PURE__ */ jsx15("p", { className: "mb-3 text-sm font-semibold uppercase tracking-wide text-muted-foreground", children: "On this page" }),
1067
+ /* @__PURE__ */ jsx15("ul", { className: "space-y-1 text-sm", children: toc.map((item) => /* @__PURE__ */ jsx15("li", { style: { paddingLeft: `${Math.max(0, item.depth - 2) * 1}rem` }, children: /* @__PURE__ */ jsx15(
1044
1068
  "a",
1045
1069
  {
1046
1070
  href: `#${item.id}`,
@@ -1056,7 +1080,7 @@ function ArticleTOC({ toc, className }) {
1056
1080
  // src/ScrollToTop.tsx
1057
1081
  import { useEffect as useEffect4, useState as useState5 } from "react";
1058
1082
  import { ArrowUp } from "lucide-react";
1059
- import { jsx as jsx15 } from "react/jsx-runtime";
1083
+ import { jsx as jsx16 } from "react/jsx-runtime";
1060
1084
  function ScrollToTop() {
1061
1085
  const [visible, setVisible] = useState5(false);
1062
1086
  useEffect4(() => {
@@ -1065,13 +1089,13 @@ function ScrollToTop() {
1065
1089
  return () => globalThis.removeEventListener("scroll", onScroll);
1066
1090
  }, []);
1067
1091
  if (!visible) return null;
1068
- return /* @__PURE__ */ jsx15(
1092
+ return /* @__PURE__ */ jsx16(
1069
1093
  "button",
1070
1094
  {
1071
1095
  "aria-label": "Scroll to top",
1072
1096
  onClick: () => globalThis.scrollTo({ top: 0, behavior: "smooth" }),
1073
1097
  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",
1074
- children: /* @__PURE__ */ jsx15(ArrowUp, { className: "h-5 w-5" })
1098
+ children: /* @__PURE__ */ jsx16(ArrowUp, { className: "h-5 w-5" })
1075
1099
  }
1076
1100
  );
1077
1101
  }
@@ -1082,7 +1106,7 @@ import { useCallback as useCallback2, useEffect as useEffect5, useState as useSt
1082
1106
 
1083
1107
  // src/CommentForm.tsx
1084
1108
  import { useState as useState6 } from "react";
1085
- import { jsx as jsx16, jsxs as jsxs15 } from "react/jsx-runtime";
1109
+ import { jsx as jsx17, jsxs as jsxs16 } from "react/jsx-runtime";
1086
1110
  var MAX_LENGTH = 2e3;
1087
1111
  var WARN_THRESHOLD = 1800;
1088
1112
  function submitLabel(submitting, parentId) {
@@ -1121,8 +1145,8 @@ function CommentForm({ articleSlug, parentId, onSuccess, onCancel }) {
1121
1145
  }
1122
1146
  });
1123
1147
  }
1124
- return /* @__PURE__ */ jsxs15("form", { onSubmit: handleSubmit, className: "space-y-2", children: [
1125
- /* @__PURE__ */ jsx16(
1148
+ return /* @__PURE__ */ jsxs16("form", { onSubmit: handleSubmit, className: "space-y-2", children: [
1149
+ /* @__PURE__ */ jsx17(
1126
1150
  "textarea",
1127
1151
  {
1128
1152
  value: body,
@@ -1135,13 +1159,13 @@ function CommentForm({ articleSlug, parentId, onSuccess, onCancel }) {
1135
1159
  "aria-label": parentId ? "Reply text" : "Comment text"
1136
1160
  }
1137
1161
  ),
1138
- remaining <= MAX_LENGTH - WARN_THRESHOLD && /* @__PURE__ */ jsxs15("p", { className: `text-xs ${remaining < 0 ? "text-destructive" : "text-muted-foreground"}`, children: [
1162
+ remaining <= MAX_LENGTH - WARN_THRESHOLD && /* @__PURE__ */ jsxs16("p", { className: `text-xs ${remaining < 0 ? "text-destructive" : "text-muted-foreground"}`, children: [
1139
1163
  remaining,
1140
1164
  " characters remaining"
1141
1165
  ] }),
1142
- error && /* @__PURE__ */ jsx16("p", { className: "text-xs text-destructive", children: error }),
1143
- /* @__PURE__ */ jsxs15("div", { className: "flex gap-2", children: [
1144
- /* @__PURE__ */ jsx16(
1166
+ error && /* @__PURE__ */ jsx17("p", { className: "text-xs text-destructive", children: error }),
1167
+ /* @__PURE__ */ jsxs16("div", { className: "flex gap-2", children: [
1168
+ /* @__PURE__ */ jsx17(
1145
1169
  "button",
1146
1170
  {
1147
1171
  type: "submit",
@@ -1150,7 +1174,7 @@ function CommentForm({ articleSlug, parentId, onSuccess, onCancel }) {
1150
1174
  children: submitLabel(submitting, parentId)
1151
1175
  }
1152
1176
  ),
1153
- onCancel && /* @__PURE__ */ jsx16(
1177
+ onCancel && /* @__PURE__ */ jsx17(
1154
1178
  "button",
1155
1179
  {
1156
1180
  type: "button",
@@ -1165,7 +1189,7 @@ function CommentForm({ articleSlug, parentId, onSuccess, onCancel }) {
1165
1189
 
1166
1190
  // src/CommentItem.tsx
1167
1191
  import { useState as useState7 } from "react";
1168
- import { Fragment as Fragment2, jsx as jsx17, jsxs as jsxs16 } from "react/jsx-runtime";
1192
+ import { Fragment as Fragment2, jsx as jsx18, jsxs as jsxs17 } from "react/jsx-runtime";
1169
1193
  function relativeTime(iso) {
1170
1194
  const diff = Date.now() - new Date(iso).getTime();
1171
1195
  const minutes = Math.floor(diff / 6e4);
@@ -1203,15 +1227,15 @@ function CommentItem({
1203
1227
  }
1204
1228
  });
1205
1229
  }
1206
- return /* @__PURE__ */ jsxs16("div", { className: `${isReply ? "ml-8 border-l-2 border-border pl-4" : ""}`, children: [
1207
- /* @__PURE__ */ jsx17("div", { className: "rounded-lg bg-muted/40 p-3 space-y-1", children: comment.isDeleted ? /* @__PURE__ */ jsx17("p", { className: "text-sm italic text-muted-foreground", children: "Comment removed" }) : /* @__PURE__ */ jsxs16(Fragment2, { children: [
1208
- /* @__PURE__ */ jsxs16("div", { className: "flex items-center justify-between gap-2", children: [
1209
- /* @__PURE__ */ jsx17("span", { className: "text-sm font-medium text-foreground", children: comment.authorName }),
1210
- /* @__PURE__ */ jsx17("span", { className: "text-xs text-muted-foreground", children: relativeTime(comment.createdAt) })
1230
+ return /* @__PURE__ */ jsxs17("div", { className: `${isReply ? "ml-8 border-l-2 border-border pl-4" : ""}`, children: [
1231
+ /* @__PURE__ */ jsx18("div", { className: "rounded-lg bg-muted/40 p-3 space-y-1", children: comment.isDeleted ? /* @__PURE__ */ jsx18("p", { className: "text-sm italic text-muted-foreground", children: "Comment removed" }) : /* @__PURE__ */ jsxs17(Fragment2, { children: [
1232
+ /* @__PURE__ */ jsxs17("div", { className: "flex items-center justify-between gap-2", children: [
1233
+ /* @__PURE__ */ jsx18("span", { className: "text-sm font-medium text-foreground", children: comment.authorName }),
1234
+ /* @__PURE__ */ jsx18("span", { className: "text-xs text-muted-foreground", children: relativeTime(comment.createdAt) })
1211
1235
  ] }),
1212
- /* @__PURE__ */ jsx17("p", { className: "text-sm text-foreground whitespace-pre-wrap break-words", children: comment.body }),
1213
- /* @__PURE__ */ jsxs16("div", { className: "flex gap-3 pt-1", children: [
1214
- canReply && /* @__PURE__ */ jsx17(
1236
+ /* @__PURE__ */ jsx18("p", { className: "text-sm text-foreground whitespace-pre-wrap break-words", children: comment.body }),
1237
+ /* @__PURE__ */ jsxs17("div", { className: "flex gap-3 pt-1", children: [
1238
+ canReply && /* @__PURE__ */ jsx18(
1215
1239
  "button",
1216
1240
  {
1217
1241
  onClick: () => setShowReplyForm((v) => !v),
@@ -1219,7 +1243,7 @@ function CommentItem({
1219
1243
  children: showReplyForm ? "Cancel" : "Reply"
1220
1244
  }
1221
1245
  ),
1222
- canDelete && /* @__PURE__ */ jsx17(
1246
+ canDelete && /* @__PURE__ */ jsx18(
1223
1247
  "button",
1224
1248
  {
1225
1249
  onClick: handleDelete,
@@ -1230,7 +1254,7 @@ function CommentItem({
1230
1254
  )
1231
1255
  ] })
1232
1256
  ] }) }),
1233
- showReplyForm && /* @__PURE__ */ jsx17("div", { className: "mt-2 ml-2", children: /* @__PURE__ */ jsx17(
1257
+ showReplyForm && /* @__PURE__ */ jsx18("div", { className: "mt-2 ml-2", children: /* @__PURE__ */ jsx18(
1234
1258
  CommentForm,
1235
1259
  {
1236
1260
  articleSlug,
@@ -1242,7 +1266,7 @@ function CommentItem({
1242
1266
  onCancel: () => setShowReplyForm(false)
1243
1267
  }
1244
1268
  ) }),
1245
- replies.length > 0 && /* @__PURE__ */ jsx17("div", { className: "mt-2 space-y-2", children: replies.map((reply) => /* @__PURE__ */ jsx17(
1269
+ replies.length > 0 && /* @__PURE__ */ jsx18("div", { className: "mt-2 space-y-2", children: replies.map((reply) => /* @__PURE__ */ jsx18(
1246
1270
  CommentItem,
1247
1271
  {
1248
1272
  comment: __spreadProps(__spreadValues({}, reply), { replies: [] }),
@@ -1257,7 +1281,7 @@ function CommentItem({
1257
1281
  }
1258
1282
 
1259
1283
  // src/CommentThread.tsx
1260
- import { jsx as jsx18 } from "react/jsx-runtime";
1284
+ import { jsx as jsx19 } from "react/jsx-runtime";
1261
1285
  function CommentThread({
1262
1286
  comments,
1263
1287
  articleSlug,
@@ -1265,9 +1289,9 @@ function CommentThread({
1265
1289
  onRefresh
1266
1290
  }) {
1267
1291
  if (comments.length === 0) {
1268
- return /* @__PURE__ */ jsx18("p", { className: "text-sm text-muted-foreground text-center py-6", children: "No comments yet. Be the first to share your thoughts." });
1292
+ return /* @__PURE__ */ jsx19("p", { className: "text-sm text-muted-foreground text-center py-6", children: "No comments yet. Be the first to share your thoughts." });
1269
1293
  }
1270
- return /* @__PURE__ */ jsx18("div", { className: "space-y-4", children: comments.map((comment) => /* @__PURE__ */ jsx18(
1294
+ return /* @__PURE__ */ jsx19("div", { className: "space-y-4", children: comments.map((comment) => /* @__PURE__ */ jsx19(
1271
1295
  CommentItem,
1272
1296
  {
1273
1297
  comment,
@@ -1280,7 +1304,7 @@ function CommentThread({
1280
1304
  }
1281
1305
 
1282
1306
  // src/CommentsSection.tsx
1283
- import { jsx as jsx19, jsxs as jsxs17 } from "react/jsx-runtime";
1307
+ import { jsx as jsx20, jsxs as jsxs18 } from "react/jsx-runtime";
1284
1308
  function CommentsSection({ articleSlug, config }) {
1285
1309
  var _a, _b, _c, _d, _e, _f;
1286
1310
  const { data: session, status } = useSession();
@@ -1312,10 +1336,10 @@ function CommentsSection({ articleSlug, config }) {
1312
1336
  if (!enabled) return null;
1313
1337
  const count = comments.length;
1314
1338
  const label = count === 1 ? "1 comment" : `${count} comments`;
1315
- return /* @__PURE__ */ jsxs17("section", { "aria-label": "Comments", className: "mt-12 pt-8 border-t border-border space-y-6", children: [
1316
- /* @__PURE__ */ jsx19("h2", { className: "text-xl font-semibold text-foreground", children: loading ? "Comments" : label }),
1317
- status === "authenticated" ? /* @__PURE__ */ jsx19(CommentForm, { articleSlug, onSuccess: fetchComments }) : /* @__PURE__ */ jsxs17("p", { className: "text-sm text-muted-foreground", children: [
1318
- /* @__PURE__ */ jsx19(
1339
+ return /* @__PURE__ */ jsxs18("section", { "aria-label": "Comments", className: "mt-12 pt-8 border-t border-border space-y-6", children: [
1340
+ /* @__PURE__ */ jsx20("h2", { className: "text-xl font-semibold text-foreground", children: loading ? "Comments" : label }),
1341
+ status === "authenticated" ? /* @__PURE__ */ jsx20(CommentForm, { articleSlug, onSuccess: fetchComments }) : /* @__PURE__ */ jsxs18("p", { className: "text-sm text-muted-foreground", children: [
1342
+ /* @__PURE__ */ jsx20(
1319
1343
  "button",
1320
1344
  {
1321
1345
  onClick: () => signIn(),
@@ -1326,8 +1350,8 @@ function CommentsSection({ articleSlug, config }) {
1326
1350
  " ",
1327
1351
  "to join the discussion."
1328
1352
  ] }),
1329
- fetchError && /* @__PURE__ */ jsx19("p", { className: "text-sm text-destructive", children: fetchError }),
1330
- loading ? /* @__PURE__ */ jsx19("p", { className: "text-sm text-muted-foreground", children: "Loading comments\u2026" }) : /* @__PURE__ */ jsx19(
1353
+ fetchError && /* @__PURE__ */ jsx20("p", { className: "text-sm text-destructive", children: fetchError }),
1354
+ loading ? /* @__PURE__ */ jsx20("p", { className: "text-sm text-muted-foreground", children: "Loading comments\u2026" }) : /* @__PURE__ */ jsx20(
1331
1355
  CommentThread,
1332
1356
  {
1333
1357
  comments,
@@ -1339,6 +1363,7 @@ function CommentsSection({ articleSlug, config }) {
1339
1363
  ] });
1340
1364
  }
1341
1365
  export {
1366
+ ArticleBackLink,
1342
1367
  ArticleCard,
1343
1368
  ArticleCategoryGrid,
1344
1369
  ArticleDetailHero,