@fullstackdatasolutions/articles 0.3.0 → 0.4.2

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
@@ -72,8 +72,10 @@ __export(src_exports, {
72
72
  ArticleCard: () => ArticleCard,
73
73
  ArticleCategoryGrid: () => ArticleCategoryGrid,
74
74
  ArticleDetailHero: () => ArticleDetailHero,
75
+ ArticleNavigation: () => ArticleNavigation,
75
76
  ArticleSchema: () => ArticleSchema,
76
77
  ArticleSearchBar: () => ArticleSearchBar,
78
+ ArticleSocialShare: () => ArticleSocialShare,
77
79
  ArticlesHero: () => ArticlesHero,
78
80
  ArticlesPage: () => ArticlesPage,
79
81
  BreadcrumbSchema: () => BreadcrumbSchema,
@@ -668,7 +670,11 @@ var import_jsx_runtime10 = require("react/jsx-runtime");
668
670
  function toSlug(cat) {
669
671
  return cat.toLowerCase().replaceAll(/\s+/g, "-").replaceAll(/[^a-z0-9-]/g, "");
670
672
  }
671
- function ArticleDetailHero({ article, categoryBasePath }) {
673
+ function ArticleDetailHero({
674
+ article,
675
+ categoryBasePath = "/articles/category",
676
+ showDate = false
677
+ }) {
672
678
  return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(
673
679
  "section",
674
680
  {
@@ -689,6 +695,7 @@ function ArticleDetailHero({ article, categoryBasePath }) {
689
695
  fill: true,
690
696
  sizes: "100vw",
691
697
  className: "object-cover",
698
+ style: { objectFit: "cover" },
692
699
  priority: true
693
700
  }
694
701
  ),
@@ -716,42 +723,67 @@ function ArticleDetailHero({ article, categoryBasePath }) {
716
723
  width: "100%"
717
724
  },
718
725
  children: [
719
- article.categories && article.categories.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { style: { display: "flex", flexWrap: "wrap", justifyContent: "center", gap: "0.5rem", marginBottom: "1rem" }, children: article.categories.slice(0, 4).map(
720
- (cat) => categoryBasePath ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
721
- import_link5.default,
722
- {
723
- href: `${categoryBasePath}/${toSlug(cat)}`,
724
- className: "inline-block bg-white/20 text-white text-sm font-medium px-3 py-1 rounded-full hover:bg-white/30 transition-colors",
725
- children: cat
726
- },
727
- cat
728
- ) : /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
729
- "span",
730
- {
731
- className: "inline-block bg-white/20 text-white text-sm font-medium px-3 py-1 rounded-full",
732
- children: cat
726
+ article.categories && article.categories.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
727
+ "div",
728
+ {
729
+ style: {
730
+ display: "flex",
731
+ flexWrap: "wrap",
732
+ justifyContent: "center",
733
+ gap: "0.5rem",
734
+ marginBottom: "1rem"
733
735
  },
734
- cat
735
- )
736
- ) }),
736
+ children: article.categories.slice(0, 4).map(
737
+ (cat) => categoryBasePath ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
738
+ import_link5.default,
739
+ {
740
+ href: `${categoryBasePath}/${toSlug(cat)}`,
741
+ className: "inline-block bg-white/20 text-white text-sm font-medium px-3 py-1 rounded-full hover:bg-white/30 transition-colors",
742
+ children: cat
743
+ },
744
+ cat
745
+ ) : /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
746
+ "span",
747
+ {
748
+ className: "inline-block bg-white/20 text-white text-sm font-medium px-3 py-1 rounded-full",
749
+ children: cat
750
+ },
751
+ cat
752
+ )
753
+ )
754
+ }
755
+ ),
737
756
  /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("h1", { className: "text-4xl md:text-5xl font-bold mb-4", children: article.title }),
738
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { style: { display: "flex", alignItems: "center", justifyContent: "center", gap: "1.5rem", fontSize: "0.875rem", color: "rgba(255,255,255,0.8)" }, children: [
739
- article.date && /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: "0.5rem" }, children: [
740
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_lucide_react5.Calendar, { className: "h-4 w-4" }),
741
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { children: article.date })
742
- ] }),
743
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: "0.5rem" }, children: [
744
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_lucide_react5.Clock, { className: "h-4 w-4" }),
745
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { children: article.readTime })
746
- ] }),
747
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: "0.5rem" }, children: [
748
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_lucide_react5.User, { className: "h-4 w-4" }),
749
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("span", { children: [
750
- "By ",
751
- article.author
752
- ] })
753
- ] })
754
- ] })
757
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(
758
+ "div",
759
+ {
760
+ style: {
761
+ display: "flex",
762
+ alignItems: "center",
763
+ justifyContent: "center",
764
+ gap: "1.5rem",
765
+ fontSize: "0.875rem",
766
+ color: "rgba(255,255,255,0.8)"
767
+ },
768
+ children: [
769
+ showDate && article.date && /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: "0.5rem" }, children: [
770
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_lucide_react5.Calendar, { className: "h-4 w-4" }),
771
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { children: article.date })
772
+ ] }),
773
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: "0.5rem" }, children: [
774
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_lucide_react5.Clock, { className: "h-4 w-4" }),
775
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { children: article.readTime })
776
+ ] }),
777
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: "0.5rem" }, children: [
778
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_lucide_react5.User, { className: "h-4 w-4" }),
779
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("span", { children: [
780
+ "By ",
781
+ article.author
782
+ ] })
783
+ ] })
784
+ ]
785
+ }
786
+ )
755
787
  ]
756
788
  }
757
789
  )
@@ -790,30 +822,55 @@ function CategoryArticlesPage({ category, articles, config }) {
790
822
  ]
791
823
  }
792
824
  ),
793
- /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("section", { className: "relative h-72 md:h-96 flex items-center justify-center overflow-hidden", children: [
794
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
795
- import_image5.default,
796
- {
797
- src: heroImage,
798
- alt: categoryName,
799
- fill: true,
800
- sizes: "100vw",
801
- className: "object-cover",
802
- priority: true
803
- }
804
- ),
805
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: "absolute inset-0 bg-black/60" }),
806
- /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "relative z-10 text-center text-white px-4", children: [
807
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("p", { className: "text-sm font-semibold uppercase tracking-widest mb-3 opacity-80", children: "Category" }),
808
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("h1", { className: "text-4xl md:text-5xl font-bold mb-3", children: categoryName }),
809
- description.long && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("p", { className: "text-lg opacity-90 max-w-2xl mx-auto mt-2", children: description.long }),
810
- /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("p", { className: "text-lg opacity-70 mt-2", children: [
811
- articles.length,
812
- " article",
813
- articles.length === 1 ? "" : "s"
814
- ] })
815
- ] })
816
- ] }),
825
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
826
+ "section",
827
+ {
828
+ style: {
829
+ position: "relative",
830
+ height: "20rem",
831
+ display: "flex",
832
+ alignItems: "center",
833
+ justifyContent: "center",
834
+ overflow: "hidden"
835
+ },
836
+ children: [
837
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
838
+ import_image5.default,
839
+ {
840
+ src: heroImage,
841
+ alt: categoryName,
842
+ fill: true,
843
+ sizes: "100vw",
844
+ style: { objectFit: "cover" },
845
+ priority: true
846
+ }
847
+ ),
848
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { style: { position: "absolute", inset: 0, backgroundColor: "rgba(0,0,0,0.6)" } }),
849
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
850
+ "div",
851
+ {
852
+ style: {
853
+ position: "relative",
854
+ zIndex: 10,
855
+ textAlign: "center",
856
+ color: "white",
857
+ padding: "0 1rem"
858
+ },
859
+ children: [
860
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("p", { className: "text-sm font-semibold uppercase tracking-widest mb-3 opacity-80", children: "Category" }),
861
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("h1", { className: "text-4xl md:text-5xl font-bold mb-3", children: categoryName }),
862
+ description.long && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("p", { className: "text-lg opacity-90 max-w-2xl mx-auto mt-2", children: description.long }),
863
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("p", { className: "text-lg opacity-70 mt-2", children: [
864
+ articles.length,
865
+ " article",
866
+ articles.length === 1 ? "" : "s"
867
+ ] })
868
+ ]
869
+ }
870
+ )
871
+ ]
872
+ }
873
+ ),
817
874
  /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("section", { className: "py-16 bg-muted/50 flex-1", children: /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "max-w-7xl mx-auto px-4 sm:px-6 lg:px-8", children: [
818
875
  /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "mb-8 flex items-center justify-between", children: [
819
876
  /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_link6.default, { href: "/articles", className: "text-sm text-primary hover:underline", children: "\u2190 All Articles" }),
@@ -824,13 +881,136 @@ function CategoryArticlesPage({ category, articles, config }) {
824
881
  ] });
825
882
  }
826
883
 
884
+ // src/ArticleSocialShare.tsx
885
+ var import_react4 = require("react");
886
+ var import_lucide_react6 = require("lucide-react");
887
+ var import_jsx_runtime12 = require("react/jsx-runtime");
888
+ function ArticleSocialShare({ title, url, excerpt, shareMessage }) {
889
+ const [copied, setCopied] = (0, import_react4.useState)(false);
890
+ const encodedTitle = encodeURIComponent(title);
891
+ const encodedUrl = encodeURIComponent(url);
892
+ const encodedExcerpt = encodeURIComponent(excerpt || "");
893
+ const shareLinks = {
894
+ linkedin: `https://www.linkedin.com/sharing/share-offsite/?url=${encodedUrl}`,
895
+ facebook: `https://www.facebook.com/sharer/sharer.php?u=${encodedUrl}`,
896
+ twitter: `https://twitter.com/intent/tweet?text=${encodedTitle}&url=${encodedUrl}`,
897
+ reddit: `https://reddit.com/submit?url=${encodedUrl}&title=${encodedTitle}`,
898
+ email: `mailto:?subject=${encodedTitle}&body=${encodedExcerpt}%0A%0A${encodedUrl}`,
899
+ whatsapp: `https://wa.me/?text=${encodedTitle}%20${encodedUrl}`,
900
+ telegram: `https://t.me/share/url?url=${encodedUrl}&text=${encodedTitle}`
901
+ };
902
+ const copyToClipboard = () => __async(this, null, function* () {
903
+ try {
904
+ yield navigator.clipboard.writeText(url);
905
+ setCopied(true);
906
+ setTimeout(() => setCopied(false), 2e3);
907
+ } catch (e) {
908
+ }
909
+ });
910
+ const openShareWindow = (shareUrl) => {
911
+ window.open(shareUrl, "_blank", "width=600,height=400,scrollbars=yes,resizable=yes");
912
+ };
913
+ 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
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "border-t border-border pt-8 mt-8", children: [
915
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "flex items-center gap-2 mb-4", children: [
916
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_lucide_react6.Share2, { className: "h-5 w-5 text-muted-foreground" }),
917
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("h3", { className: "text-lg font-semibold text-foreground", children: "Share this article" })
918
+ ] }),
919
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "flex flex-wrap gap-2", children: [
920
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("button", { className: btnClass, onClick: () => openShareWindow(shareLinks.linkedin), children: [
921
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_lucide_react6.Users, { className: "h-4 w-4" }),
922
+ "LinkedIn"
923
+ ] }),
924
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("button", { className: btnClass, onClick: () => openShareWindow(shareLinks.facebook), children: [
925
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "text-xs font-bold", children: "f" }),
926
+ " Facebook"
927
+ ] }),
928
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("button", { className: btnClass, onClick: () => openShareWindow(shareLinks.twitter), children: [
929
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_lucide_react6.MessageCircle, { className: "h-4 w-4" }),
930
+ "Twitter"
931
+ ] }),
932
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("button", { className: btnClass, onClick: () => openShareWindow(shareLinks.reddit), children: [
933
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_lucide_react6.FileText, { className: "h-4 w-4" }),
934
+ "Reddit"
935
+ ] }),
936
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("button", { className: btnClass, onClick: () => openShareWindow(shareLinks.whatsapp), children: [
937
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_lucide_react6.MessageCircle, { className: "h-4 w-4" }),
938
+ "WhatsApp"
939
+ ] }),
940
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("button", { className: btnClass, onClick: () => openShareWindow(shareLinks.telegram), children: [
941
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_lucide_react6.MessageCircle, { className: "h-4 w-4" }),
942
+ "Telegram"
943
+ ] }),
944
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
945
+ "button",
946
+ {
947
+ className: btnClass,
948
+ onClick: () => {
949
+ globalThis.location.href = shareLinks.email;
950
+ },
951
+ children: [
952
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_lucide_react6.Mail, { className: "h-4 w-4" }),
953
+ "Email"
954
+ ]
955
+ }
956
+ ),
957
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("button", { className: btnClass, onClick: copyToClipboard, children: [
958
+ copied ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_lucide_react6.Check, { className: "h-4 w-4" }) : /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_lucide_react6.Copy, { className: "h-4 w-4" }),
959
+ copied ? "Copied!" : "Copy Link"
960
+ ] })
961
+ ] }),
962
+ shareMessage && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("p", { className: "text-sm text-muted-foreground mt-3", children: shareMessage })
963
+ ] });
964
+ }
965
+
966
+ // src/ArticleNavigation.tsx
967
+ var import_link7 = __toESM(require("next/link"), 1);
968
+ var import_lucide_react7 = require("lucide-react");
969
+ var import_jsx_runtime13 = require("react/jsx-runtime");
970
+ function truncateTitle(title, maxLength = 60) {
971
+ return title.length > maxLength ? `${title.substring(0, maxLength)}...` : title;
972
+ }
973
+ function ArticleNavigation({ previous, next, basePath }) {
974
+ if (!previous && !next) return null;
975
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("nav", { className: "mt-12 pt-8 border-t border-border", "aria-label": "Article navigation", children: /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "flex justify-between gap-4", children: [
976
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "flex-1 min-w-0", children: previous && /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(
977
+ import_link7.default,
978
+ {
979
+ href: `${basePath}/${previous.slug}`,
980
+ className: "group flex items-center gap-3 p-4 rounded-lg border border-border hover:border-primary/20 hover:bg-muted/50 transition-colors",
981
+ children: [
982
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_lucide_react7.ChevronLeft, { className: "h-5 w-5 text-muted-foreground group-hover:text-primary shrink-0" }),
983
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "min-w-0 flex-1", children: [
984
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "text-sm text-muted-foreground mb-1", children: "Previous Article" }),
985
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "font-medium text-foreground truncate", title: previous.title, children: truncateTitle(previous.title) })
986
+ ] })
987
+ ]
988
+ }
989
+ ) }),
990
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "flex-1 min-w-0", children: next && /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(
991
+ import_link7.default,
992
+ {
993
+ href: `${basePath}/${next.slug}`,
994
+ 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",
995
+ children: [
996
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "min-w-0 flex-1 text-right", children: [
997
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "text-sm text-muted-foreground mb-1", children: "Next Article" }),
998
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "font-medium text-foreground truncate", title: next.title, children: truncateTitle(next.title) })
999
+ ] }),
1000
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_lucide_react7.ChevronRight, { className: "h-5 w-5 text-muted-foreground group-hover:text-primary shrink-0" })
1001
+ ]
1002
+ }
1003
+ ) })
1004
+ ] }) });
1005
+ }
1006
+
827
1007
  // src/CommentsSection.tsx
828
- var import_react6 = require("next-auth/react");
829
- var import_react7 = require("react");
1008
+ var import_react7 = require("next-auth/react");
1009
+ var import_react8 = require("react");
830
1010
 
831
1011
  // src/CommentForm.tsx
832
- var import_react4 = require("react");
833
- var import_jsx_runtime12 = require("react/jsx-runtime");
1012
+ var import_react5 = require("react");
1013
+ var import_jsx_runtime14 = require("react/jsx-runtime");
834
1014
  var MAX_LENGTH = 2e3;
835
1015
  var WARN_THRESHOLD = 1800;
836
1016
  function submitLabel(submitting, parentId) {
@@ -838,9 +1018,9 @@ function submitLabel(submitting, parentId) {
838
1018
  return parentId ? "Reply" : "Post comment";
839
1019
  }
840
1020
  function CommentForm({ articleSlug, parentId, onSuccess, onCancel }) {
841
- const [body, setBody] = (0, import_react4.useState)("");
842
- const [submitting, setSubmitting] = (0, import_react4.useState)(false);
843
- const [error, setError] = (0, import_react4.useState)(null);
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);
844
1024
  const remaining = MAX_LENGTH - body.length;
845
1025
  function handleSubmit(e) {
846
1026
  return __async(this, null, function* () {
@@ -869,8 +1049,8 @@ function CommentForm({ articleSlug, parentId, onSuccess, onCancel }) {
869
1049
  }
870
1050
  });
871
1051
  }
872
- return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("form", { onSubmit: handleSubmit, className: "space-y-2", children: [
873
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
1052
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("form", { onSubmit: handleSubmit, className: "space-y-2", children: [
1053
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
874
1054
  "textarea",
875
1055
  {
876
1056
  value: body,
@@ -883,13 +1063,13 @@ function CommentForm({ articleSlug, parentId, onSuccess, onCancel }) {
883
1063
  "aria-label": parentId ? "Reply text" : "Comment text"
884
1064
  }
885
1065
  ),
886
- remaining <= MAX_LENGTH - WARN_THRESHOLD && /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("p", { className: `text-xs ${remaining < 0 ? "text-destructive" : "text-muted-foreground"}`, children: [
1066
+ remaining <= MAX_LENGTH - WARN_THRESHOLD && /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("p", { className: `text-xs ${remaining < 0 ? "text-destructive" : "text-muted-foreground"}`, children: [
887
1067
  remaining,
888
1068
  " characters remaining"
889
1069
  ] }),
890
- error && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("p", { className: "text-xs text-destructive", children: error }),
891
- /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "flex gap-2", children: [
892
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
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)(
893
1073
  "button",
894
1074
  {
895
1075
  type: "submit",
@@ -898,7 +1078,7 @@ function CommentForm({ articleSlug, parentId, onSuccess, onCancel }) {
898
1078
  children: submitLabel(submitting, parentId)
899
1079
  }
900
1080
  ),
901
- onCancel && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
1081
+ onCancel && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
902
1082
  "button",
903
1083
  {
904
1084
  type: "button",
@@ -912,8 +1092,8 @@ function CommentForm({ articleSlug, parentId, onSuccess, onCancel }) {
912
1092
  }
913
1093
 
914
1094
  // src/CommentItem.tsx
915
- var import_react5 = require("react");
916
- var import_jsx_runtime13 = require("react/jsx-runtime");
1095
+ var import_react6 = require("react");
1096
+ var import_jsx_runtime15 = require("react/jsx-runtime");
917
1097
  function relativeTime(iso) {
918
1098
  const diff = Date.now() - new Date(iso).getTime();
919
1099
  const minutes = Math.floor(diff / 6e4);
@@ -934,8 +1114,8 @@ function CommentItem({
934
1114
  isReply = false,
935
1115
  onRefresh
936
1116
  }) {
937
- const [showReplyForm, setShowReplyForm] = (0, import_react5.useState)(false);
938
- const [deleting, setDeleting] = (0, import_react5.useState)(false);
1117
+ const [showReplyForm, setShowReplyForm] = (0, import_react6.useState)(false);
1118
+ const [deleting, setDeleting] = (0, import_react6.useState)(false);
939
1119
  const replies = "replies" in comment ? comment.replies : [];
940
1120
  const canReply = !isReply && !!currentUserId && !comment.isDeleted;
941
1121
  const canDelete = !!currentUserId && currentUserId === comment.authorId && !comment.isDeleted;
@@ -951,15 +1131,15 @@ function CommentItem({
951
1131
  }
952
1132
  });
953
1133
  }
954
- return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: `${isReply ? "ml-8 border-l-2 border-border pl-4" : ""}`, children: [
955
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "rounded-lg bg-muted/40 p-3 space-y-1", children: comment.isDeleted ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("p", { className: "text-sm italic text-muted-foreground", children: "Comment removed" }) : /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(import_jsx_runtime13.Fragment, { children: [
956
- /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "flex items-center justify-between gap-2", children: [
957
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "text-sm font-medium text-foreground", children: comment.authorName }),
958
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "text-xs text-muted-foreground", children: relativeTime(comment.createdAt) })
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) })
959
1139
  ] }),
960
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("p", { className: "text-sm text-foreground whitespace-pre-wrap break-words", children: comment.body }),
961
- /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "flex gap-3 pt-1", children: [
962
- canReply && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
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)(
963
1143
  "button",
964
1144
  {
965
1145
  onClick: () => setShowReplyForm((v) => !v),
@@ -967,7 +1147,7 @@ function CommentItem({
967
1147
  children: showReplyForm ? "Cancel" : "Reply"
968
1148
  }
969
1149
  ),
970
- canDelete && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
1150
+ canDelete && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
971
1151
  "button",
972
1152
  {
973
1153
  onClick: handleDelete,
@@ -978,7 +1158,7 @@ function CommentItem({
978
1158
  )
979
1159
  ] })
980
1160
  ] }) }),
981
- showReplyForm && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "mt-2 ml-2", children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
1161
+ showReplyForm && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { className: "mt-2 ml-2", children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
982
1162
  CommentForm,
983
1163
  {
984
1164
  articleSlug,
@@ -990,7 +1170,7 @@ function CommentItem({
990
1170
  onCancel: () => setShowReplyForm(false)
991
1171
  }
992
1172
  ) }),
993
- replies.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "mt-2 space-y-2", children: replies.map((reply) => /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
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)(
994
1174
  CommentItem,
995
1175
  {
996
1176
  comment: __spreadProps(__spreadValues({}, reply), { replies: [] }),
@@ -1005,7 +1185,7 @@ function CommentItem({
1005
1185
  }
1006
1186
 
1007
1187
  // src/CommentThread.tsx
1008
- var import_jsx_runtime14 = require("react/jsx-runtime");
1188
+ var import_jsx_runtime16 = require("react/jsx-runtime");
1009
1189
  function CommentThread({
1010
1190
  comments,
1011
1191
  articleSlug,
@@ -1013,9 +1193,9 @@ function CommentThread({
1013
1193
  onRefresh
1014
1194
  }) {
1015
1195
  if (comments.length === 0) {
1016
- return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("p", { className: "text-sm text-muted-foreground text-center py-6", children: "No comments yet. Be the first to share your thoughts." });
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." });
1017
1197
  }
1018
- return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { className: "space-y-4", children: comments.map((comment) => /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
1198
+ return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { className: "space-y-4", children: comments.map((comment) => /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
1019
1199
  CommentItem,
1020
1200
  {
1021
1201
  comment,
@@ -1028,17 +1208,17 @@ function CommentThread({
1028
1208
  }
1029
1209
 
1030
1210
  // src/CommentsSection.tsx
1031
- var import_jsx_runtime15 = require("react/jsx-runtime");
1211
+ var import_jsx_runtime17 = require("react/jsx-runtime");
1032
1212
  function CommentsSection({ articleSlug, config }) {
1033
1213
  var _a, _b, _c, _d, _e, _f;
1034
- const { data: session, status } = (0, import_react6.useSession)();
1035
- const [comments, setComments] = (0, import_react7.useState)([]);
1036
- const [loading, setLoading] = (0, import_react7.useState)(true);
1037
- const [fetchError, setFetchError] = (0, import_react7.useState)(null);
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);
1038
1218
  const perArticleEnabled = (_b = (_a = config.perArticleOverride) == null ? void 0 : _a[articleSlug]) != null ? _b : true;
1039
1219
  const enabled = config.enabled && perArticleEnabled;
1040
1220
  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;
1041
- const fetchComments = (0, import_react7.useCallback)(() => __async(this, null, function* () {
1221
+ const fetchComments = (0, import_react8.useCallback)(() => __async(this, null, function* () {
1042
1222
  setLoading(true);
1043
1223
  setFetchError(null);
1044
1224
  try {
@@ -1052,7 +1232,7 @@ function CommentsSection({ articleSlug, config }) {
1052
1232
  setLoading(false);
1053
1233
  }
1054
1234
  }), [articleSlug]);
1055
- (0, import_react7.useEffect)(() => {
1235
+ (0, import_react8.useEffect)(() => {
1056
1236
  if (enabled) {
1057
1237
  fetchComments().catch(() => void 0);
1058
1238
  }
@@ -1060,13 +1240,13 @@ function CommentsSection({ articleSlug, config }) {
1060
1240
  if (!enabled) return null;
1061
1241
  const count = comments.length;
1062
1242
  const label = count === 1 ? "1 comment" : `${count} comments`;
1063
- return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("section", { "aria-label": "Comments", className: "mt-12 pt-8 border-t border-border space-y-6", children: [
1064
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("h2", { className: "text-xl font-semibold text-foreground", children: loading ? "Comments" : label }),
1065
- status === "authenticated" ? /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(CommentForm, { articleSlug, onSuccess: fetchComments }) : /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("p", { className: "text-sm text-muted-foreground", children: [
1066
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
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)(
1067
1247
  "button",
1068
1248
  {
1069
- onClick: () => (0, import_react6.signIn)(),
1249
+ onClick: () => (0, import_react7.signIn)(),
1070
1250
  className: "text-primary underline hover:no-underline focus:outline-none",
1071
1251
  children: "Sign in"
1072
1252
  }
@@ -1074,8 +1254,8 @@ function CommentsSection({ articleSlug, config }) {
1074
1254
  " ",
1075
1255
  "to join the discussion."
1076
1256
  ] }),
1077
- fetchError && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("p", { className: "text-sm text-destructive", children: fetchError }),
1078
- loading ? /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("p", { className: "text-sm text-muted-foreground", children: "Loading comments\u2026" }) : /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
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)(
1079
1259
  CommentThread,
1080
1260
  {
1081
1261
  comments,
@@ -1091,8 +1271,10 @@ function CommentsSection({ articleSlug, config }) {
1091
1271
  ArticleCard,
1092
1272
  ArticleCategoryGrid,
1093
1273
  ArticleDetailHero,
1274
+ ArticleNavigation,
1094
1275
  ArticleSchema,
1095
1276
  ArticleSearchBar,
1277
+ ArticleSocialShare,
1096
1278
  ArticlesHero,
1097
1279
  ArticlesPage,
1098
1280
  BreadcrumbSchema,