@fullstackdatasolutions/articles 0.2.1 → 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
@@ -71,8 +71,11 @@ var src_exports = {};
71
71
  __export(src_exports, {
72
72
  ArticleCard: () => ArticleCard,
73
73
  ArticleCategoryGrid: () => ArticleCategoryGrid,
74
+ ArticleDetailHero: () => ArticleDetailHero,
75
+ ArticleNavigation: () => ArticleNavigation,
74
76
  ArticleSchema: () => ArticleSchema,
75
77
  ArticleSearchBar: () => ArticleSearchBar,
78
+ ArticleSocialShare: () => ArticleSocialShare,
76
79
  ArticlesHero: () => ArticlesHero,
77
80
  ArticlesPage: () => ArticlesPage,
78
81
  BreadcrumbSchema: () => BreadcrumbSchema,
@@ -110,37 +113,26 @@ function ArticleCategoryGrid({ categories, pageSize = 8 }) {
110
113
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h2", { className: "text-3xl font-bold text-foreground mb-4", children: "Browse by Category" }),
111
114
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "text-lg text-muted-foreground", children: "Explore our articles by topic to find the insights most relevant to your interests." })
112
115
  ] }),
113
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
114
- "div",
115
- {
116
- className: "sm:grid-cols-4",
117
- style: {
118
- display: "grid",
119
- gridTemplateColumns: "repeat(1, minmax(0, 1fr))",
120
- gap: "1.5rem"
121
- },
122
- children: visible.map((cat) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_link.default, { href: `/articles/category/${cat.slug}`, children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "group overflow-hidden rounded-xl border border-border shadow-sm hover:shadow-lg transition-all duration-300 cursor-pointer bg-card", children: [
123
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "overflow-hidden", style: { position: "relative", height: "13rem" }, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
124
- import_image.default,
125
- {
126
- src: cat.featuredImage,
127
- alt: cat.name,
128
- fill: true,
129
- sizes: "(max-width: 768px) 100vw, (max-width: 1200px) 50vw, 33vw",
130
- className: "object-cover group-hover:scale-105 transition-transform duration-300"
131
- }
132
- ) }),
133
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "p-4 text-center", children: [
134
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h3", { className: "font-semibold text-foreground text-base", children: cat.name }),
135
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("p", { className: "text-sm text-muted-foreground mt-1", children: [
136
- cat.count,
137
- " ",
138
- cat.count === 1 ? "article" : "articles"
139
- ] })
140
- ] })
141
- ] }) }, cat.slug))
142
- }
143
- ),
116
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "grid md:grid-cols-2 lg:grid-cols-3 gap-6", children: visible.map((cat) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_link.default, { href: `/articles/category/${cat.slug}`, children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "group overflow-hidden rounded-xl border border-border shadow-sm hover:shadow-lg transition-all duration-300 cursor-pointer bg-card", children: [
117
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "overflow-hidden", style: { position: "relative", height: "13rem" }, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
118
+ import_image.default,
119
+ {
120
+ src: cat.featuredImage,
121
+ alt: cat.name,
122
+ fill: true,
123
+ sizes: "(max-width: 768px) 100vw, (max-width: 1200px) 50vw, 33vw",
124
+ className: "object-cover group-hover:scale-105 transition-transform duration-300"
125
+ }
126
+ ) }),
127
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "p-4 text-center", children: [
128
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h3", { className: "font-semibold text-foreground text-base", children: cat.name }),
129
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("p", { className: "text-sm text-muted-foreground mt-1", children: [
130
+ cat.count,
131
+ " ",
132
+ cat.count === 1 ? "article" : "articles"
133
+ ] })
134
+ ] })
135
+ ] }) }, cat.slug)) }),
144
136
  hasMore && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "mt-10 text-center", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
145
137
  "button",
146
138
  {
@@ -670,10 +662,140 @@ function ArticlesPage({ config }) {
670
662
  ] });
671
663
  }
672
664
 
673
- // src/CategoryArticlesPage.tsx
665
+ // src/ArticleDetailHero.tsx
674
666
  var import_image4 = __toESM(require("next/image"), 1);
675
667
  var import_link5 = __toESM(require("next/link"), 1);
668
+ var import_lucide_react5 = require("lucide-react");
676
669
  var import_jsx_runtime10 = require("react/jsx-runtime");
670
+ function toSlug(cat) {
671
+ return cat.toLowerCase().replaceAll(/\s+/g, "-").replaceAll(/[^a-z0-9-]/g, "");
672
+ }
673
+ function ArticleDetailHero({
674
+ article,
675
+ categoryBasePath = "/articles/category",
676
+ showDate = false
677
+ }) {
678
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(
679
+ "section",
680
+ {
681
+ style: {
682
+ position: "relative",
683
+ height: "18rem",
684
+ display: "flex",
685
+ alignItems: "center",
686
+ justifyContent: "center",
687
+ overflow: "hidden"
688
+ },
689
+ children: [
690
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
691
+ import_image4.default,
692
+ {
693
+ src: article.featuredImage,
694
+ alt: article.title,
695
+ fill: true,
696
+ sizes: "100vw",
697
+ className: "object-cover",
698
+ style: { objectFit: "cover" },
699
+ priority: true
700
+ }
701
+ ),
702
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
703
+ "div",
704
+ {
705
+ style: {
706
+ position: "absolute",
707
+ inset: 0,
708
+ backgroundColor: "rgba(0,0,0,0.6)"
709
+ }
710
+ }
711
+ ),
712
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(
713
+ "div",
714
+ {
715
+ style: {
716
+ position: "relative",
717
+ zIndex: 10,
718
+ textAlign: "center",
719
+ color: "white",
720
+ padding: "0 1rem",
721
+ maxWidth: "56rem",
722
+ margin: "0 auto",
723
+ width: "100%"
724
+ },
725
+ children: [
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"
735
+ },
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
+ ),
756
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("h1", { className: "text-4xl md:text-5xl font-bold mb-4", children: article.title }),
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
+ )
787
+ ]
788
+ }
789
+ )
790
+ ]
791
+ }
792
+ );
793
+ }
794
+
795
+ // src/CategoryArticlesPage.tsx
796
+ var import_image5 = __toESM(require("next/image"), 1);
797
+ var import_link6 = __toESM(require("next/link"), 1);
798
+ var import_jsx_runtime11 = require("react/jsx-runtime");
677
799
  function getCategoryDescription(config, slug, categoryName) {
678
800
  var _a;
679
801
  const raw = (_a = config.categoryDescriptions) == null ? void 0 : _a[slug];
@@ -689,8 +811,8 @@ function CategoryArticlesPage({ category, articles, config }) {
689
811
  const description = getCategoryDescription(config, category, categoryName);
690
812
  const pageSize = (_a = config.pageSize) != null ? _a : DEFAULT_PAGE_SIZE;
691
813
  const siteUrl = config.siteUrl.replace(/\/$/, "");
692
- return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { children: [
693
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
814
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { children: [
815
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
694
816
  BreadcrumbSchema,
695
817
  {
696
818
  items: [
@@ -700,47 +822,195 @@ function CategoryArticlesPage({ category, articles, config }) {
700
822
  ]
701
823
  }
702
824
  ),
703
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("section", { className: "relative h-72 md:h-96 flex items-center justify-center overflow-hidden", children: [
704
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
705
- import_image4.default,
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
+ ),
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: [
875
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "mb-8 flex items-center justify-between", children: [
876
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_link6.default, { href: "/articles", className: "text-sm text-primary hover:underline", children: "\u2190 All Articles" }),
877
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("p", { className: "text-sm text-muted-foreground", children: description.short })
878
+ ] }),
879
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(LatestArticles, { articles, pageSize })
880
+ ] }) })
881
+ ] });
882
+ }
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",
706
946
  {
707
- src: heroImage,
708
- alt: categoryName,
709
- fill: true,
710
- sizes: "100vw",
711
- className: "object-cover",
712
- priority: true
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
+ ]
713
955
  }
714
956
  ),
715
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "absolute inset-0 bg-black/60" }),
716
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "relative z-10 text-center text-white px-4", children: [
717
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("p", { className: "text-sm font-semibold uppercase tracking-widest mb-3 opacity-80", children: "Category" }),
718
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("h1", { className: "text-4xl md:text-5xl font-bold mb-3", children: categoryName }),
719
- description.long && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("p", { className: "text-lg opacity-90 max-w-2xl mx-auto mt-2", children: description.long }),
720
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("p", { className: "text-lg opacity-70 mt-2", children: [
721
- articles.length,
722
- " article",
723
- articles.length === 1 ? "" : "s"
724
- ] })
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"
725
960
  ] })
726
961
  ] }),
727
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("section", { className: "py-16 bg-muted/50 flex-1", children: /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "max-w-7xl mx-auto px-4 sm:px-6 lg:px-8", children: [
728
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "mb-8 flex items-center justify-between", children: [
729
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_link5.default, { href: "/articles", className: "text-sm text-primary hover:underline", children: "\u2190 All Articles" }),
730
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("p", { className: "text-sm text-muted-foreground", children: description.short })
731
- ] }),
732
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(LatestArticles, { articles, pageSize })
733
- ] }) })
962
+ shareMessage && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("p", { className: "text-sm text-muted-foreground mt-3", children: shareMessage })
734
963
  ] });
735
964
  }
736
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
+
737
1007
  // src/CommentsSection.tsx
738
- var import_react6 = require("next-auth/react");
739
- var import_react7 = require("react");
1008
+ var import_react7 = require("next-auth/react");
1009
+ var import_react8 = require("react");
740
1010
 
741
1011
  // src/CommentForm.tsx
742
- var import_react4 = require("react");
743
- var import_jsx_runtime11 = require("react/jsx-runtime");
1012
+ var import_react5 = require("react");
1013
+ var import_jsx_runtime14 = require("react/jsx-runtime");
744
1014
  var MAX_LENGTH = 2e3;
745
1015
  var WARN_THRESHOLD = 1800;
746
1016
  function submitLabel(submitting, parentId) {
@@ -748,9 +1018,9 @@ function submitLabel(submitting, parentId) {
748
1018
  return parentId ? "Reply" : "Post comment";
749
1019
  }
750
1020
  function CommentForm({ articleSlug, parentId, onSuccess, onCancel }) {
751
- const [body, setBody] = (0, import_react4.useState)("");
752
- const [submitting, setSubmitting] = (0, import_react4.useState)(false);
753
- 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);
754
1024
  const remaining = MAX_LENGTH - body.length;
755
1025
  function handleSubmit(e) {
756
1026
  return __async(this, null, function* () {
@@ -779,8 +1049,8 @@ function CommentForm({ articleSlug, parentId, onSuccess, onCancel }) {
779
1049
  }
780
1050
  });
781
1051
  }
782
- return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("form", { onSubmit: handleSubmit, className: "space-y-2", children: [
783
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
1052
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("form", { onSubmit: handleSubmit, className: "space-y-2", children: [
1053
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
784
1054
  "textarea",
785
1055
  {
786
1056
  value: body,
@@ -793,13 +1063,13 @@ function CommentForm({ articleSlug, parentId, onSuccess, onCancel }) {
793
1063
  "aria-label": parentId ? "Reply text" : "Comment text"
794
1064
  }
795
1065
  ),
796
- remaining <= MAX_LENGTH - WARN_THRESHOLD && /* @__PURE__ */ (0, import_jsx_runtime11.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: [
797
1067
  remaining,
798
1068
  " characters remaining"
799
1069
  ] }),
800
- error && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("p", { className: "text-xs text-destructive", children: error }),
801
- /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "flex gap-2", children: [
802
- /* @__PURE__ */ (0, import_jsx_runtime11.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)(
803
1073
  "button",
804
1074
  {
805
1075
  type: "submit",
@@ -808,7 +1078,7 @@ function CommentForm({ articleSlug, parentId, onSuccess, onCancel }) {
808
1078
  children: submitLabel(submitting, parentId)
809
1079
  }
810
1080
  ),
811
- onCancel && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
1081
+ onCancel && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
812
1082
  "button",
813
1083
  {
814
1084
  type: "button",
@@ -822,8 +1092,8 @@ function CommentForm({ articleSlug, parentId, onSuccess, onCancel }) {
822
1092
  }
823
1093
 
824
1094
  // src/CommentItem.tsx
825
- var import_react5 = require("react");
826
- var import_jsx_runtime12 = require("react/jsx-runtime");
1095
+ var import_react6 = require("react");
1096
+ var import_jsx_runtime15 = require("react/jsx-runtime");
827
1097
  function relativeTime(iso) {
828
1098
  const diff = Date.now() - new Date(iso).getTime();
829
1099
  const minutes = Math.floor(diff / 6e4);
@@ -844,8 +1114,8 @@ function CommentItem({
844
1114
  isReply = false,
845
1115
  onRefresh
846
1116
  }) {
847
- const [showReplyForm, setShowReplyForm] = (0, import_react5.useState)(false);
848
- 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);
849
1119
  const replies = "replies" in comment ? comment.replies : [];
850
1120
  const canReply = !isReply && !!currentUserId && !comment.isDeleted;
851
1121
  const canDelete = !!currentUserId && currentUserId === comment.authorId && !comment.isDeleted;
@@ -861,15 +1131,15 @@ function CommentItem({
861
1131
  }
862
1132
  });
863
1133
  }
864
- return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: `${isReply ? "ml-8 border-l-2 border-border pl-4" : ""}`, children: [
865
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "rounded-lg bg-muted/40 p-3 space-y-1", children: comment.isDeleted ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("p", { className: "text-sm italic text-muted-foreground", children: "Comment removed" }) : /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(import_jsx_runtime12.Fragment, { children: [
866
- /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "flex items-center justify-between gap-2", children: [
867
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "text-sm font-medium text-foreground", children: comment.authorName }),
868
- /* @__PURE__ */ (0, import_jsx_runtime12.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) })
869
1139
  ] }),
870
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("p", { className: "text-sm text-foreground whitespace-pre-wrap break-words", children: comment.body }),
871
- /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "flex gap-3 pt-1", children: [
872
- canReply && /* @__PURE__ */ (0, import_jsx_runtime12.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)(
873
1143
  "button",
874
1144
  {
875
1145
  onClick: () => setShowReplyForm((v) => !v),
@@ -877,7 +1147,7 @@ function CommentItem({
877
1147
  children: showReplyForm ? "Cancel" : "Reply"
878
1148
  }
879
1149
  ),
880
- canDelete && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
1150
+ canDelete && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
881
1151
  "button",
882
1152
  {
883
1153
  onClick: handleDelete,
@@ -888,7 +1158,7 @@ function CommentItem({
888
1158
  )
889
1159
  ] })
890
1160
  ] }) }),
891
- showReplyForm && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "mt-2 ml-2", children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
1161
+ showReplyForm && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { className: "mt-2 ml-2", children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
892
1162
  CommentForm,
893
1163
  {
894
1164
  articleSlug,
@@ -900,7 +1170,7 @@ function CommentItem({
900
1170
  onCancel: () => setShowReplyForm(false)
901
1171
  }
902
1172
  ) }),
903
- replies.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "mt-2 space-y-2", children: replies.map((reply) => /* @__PURE__ */ (0, import_jsx_runtime12.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)(
904
1174
  CommentItem,
905
1175
  {
906
1176
  comment: __spreadProps(__spreadValues({}, reply), { replies: [] }),
@@ -915,7 +1185,7 @@ function CommentItem({
915
1185
  }
916
1186
 
917
1187
  // src/CommentThread.tsx
918
- var import_jsx_runtime13 = require("react/jsx-runtime");
1188
+ var import_jsx_runtime16 = require("react/jsx-runtime");
919
1189
  function CommentThread({
920
1190
  comments,
921
1191
  articleSlug,
@@ -923,9 +1193,9 @@ function CommentThread({
923
1193
  onRefresh
924
1194
  }) {
925
1195
  if (comments.length === 0) {
926
- return /* @__PURE__ */ (0, import_jsx_runtime13.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." });
927
1197
  }
928
- return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "space-y-4", children: comments.map((comment) => /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
1198
+ return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { className: "space-y-4", children: comments.map((comment) => /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
929
1199
  CommentItem,
930
1200
  {
931
1201
  comment,
@@ -938,17 +1208,17 @@ function CommentThread({
938
1208
  }
939
1209
 
940
1210
  // src/CommentsSection.tsx
941
- var import_jsx_runtime14 = require("react/jsx-runtime");
1211
+ var import_jsx_runtime17 = require("react/jsx-runtime");
942
1212
  function CommentsSection({ articleSlug, config }) {
943
1213
  var _a, _b, _c, _d, _e, _f;
944
- const { data: session, status } = (0, import_react6.useSession)();
945
- const [comments, setComments] = (0, import_react7.useState)([]);
946
- const [loading, setLoading] = (0, import_react7.useState)(true);
947
- 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);
948
1218
  const perArticleEnabled = (_b = (_a = config.perArticleOverride) == null ? void 0 : _a[articleSlug]) != null ? _b : true;
949
1219
  const enabled = config.enabled && perArticleEnabled;
950
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;
951
- const fetchComments = (0, import_react7.useCallback)(() => __async(this, null, function* () {
1221
+ const fetchComments = (0, import_react8.useCallback)(() => __async(this, null, function* () {
952
1222
  setLoading(true);
953
1223
  setFetchError(null);
954
1224
  try {
@@ -962,7 +1232,7 @@ function CommentsSection({ articleSlug, config }) {
962
1232
  setLoading(false);
963
1233
  }
964
1234
  }), [articleSlug]);
965
- (0, import_react7.useEffect)(() => {
1235
+ (0, import_react8.useEffect)(() => {
966
1236
  if (enabled) {
967
1237
  fetchComments().catch(() => void 0);
968
1238
  }
@@ -970,13 +1240,13 @@ function CommentsSection({ articleSlug, config }) {
970
1240
  if (!enabled) return null;
971
1241
  const count = comments.length;
972
1242
  const label = count === 1 ? "1 comment" : `${count} comments`;
973
- return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("section", { "aria-label": "Comments", className: "mt-12 pt-8 border-t border-border space-y-6", children: [
974
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("h2", { className: "text-xl font-semibold text-foreground", children: loading ? "Comments" : label }),
975
- status === "authenticated" ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(CommentForm, { articleSlug, onSuccess: fetchComments }) : /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("p", { className: "text-sm text-muted-foreground", children: [
976
- /* @__PURE__ */ (0, import_jsx_runtime14.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)(
977
1247
  "button",
978
1248
  {
979
- onClick: () => (0, import_react6.signIn)(),
1249
+ onClick: () => (0, import_react7.signIn)(),
980
1250
  className: "text-primary underline hover:no-underline focus:outline-none",
981
1251
  children: "Sign in"
982
1252
  }
@@ -984,8 +1254,8 @@ function CommentsSection({ articleSlug, config }) {
984
1254
  " ",
985
1255
  "to join the discussion."
986
1256
  ] }),
987
- fetchError && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("p", { className: "text-sm text-destructive", children: fetchError }),
988
- loading ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("p", { className: "text-sm text-muted-foreground", children: "Loading comments\u2026" }) : /* @__PURE__ */ (0, import_jsx_runtime14.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)(
989
1259
  CommentThread,
990
1260
  {
991
1261
  comments,
@@ -1000,8 +1270,11 @@ function CommentsSection({ articleSlug, config }) {
1000
1270
  0 && (module.exports = {
1001
1271
  ArticleCard,
1002
1272
  ArticleCategoryGrid,
1273
+ ArticleDetailHero,
1274
+ ArticleNavigation,
1003
1275
  ArticleSchema,
1004
1276
  ArticleSearchBar,
1277
+ ArticleSocialShare,
1005
1278
  ArticlesHero,
1006
1279
  ArticlesPage,
1007
1280
  BreadcrumbSchema,