next-blogpanel 1.0.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.
Files changed (127) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +211 -0
  3. package/dist/admin/index.cjs +3062 -0
  4. package/dist/admin/index.cjs.map +1 -0
  5. package/dist/admin/index.d.cts +51 -0
  6. package/dist/admin/index.d.ts +51 -0
  7. package/dist/admin/index.js +3029 -0
  8. package/dist/admin/index.js.map +1 -0
  9. package/dist/api/categories.cjs +82 -0
  10. package/dist/api/categories.cjs.map +1 -0
  11. package/dist/api/categories.d.cts +27 -0
  12. package/dist/api/categories.d.ts +27 -0
  13. package/dist/api/categories.js +77 -0
  14. package/dist/api/categories.js.map +1 -0
  15. package/dist/api/media-file.cjs +46 -0
  16. package/dist/api/media-file.cjs.map +1 -0
  17. package/dist/api/media-file.d.cts +7 -0
  18. package/dist/api/media-file.d.ts +7 -0
  19. package/dist/api/media-file.js +44 -0
  20. package/dist/api/media-file.js.map +1 -0
  21. package/dist/api/media.cjs +180 -0
  22. package/dist/api/media.cjs.map +1 -0
  23. package/dist/api/media.d.cts +22 -0
  24. package/dist/api/media.d.ts +22 -0
  25. package/dist/api/media.js +176 -0
  26. package/dist/api/media.js.map +1 -0
  27. package/dist/api/posts.cjs +103 -0
  28. package/dist/api/posts.cjs.map +1 -0
  29. package/dist/api/posts.d.cts +27 -0
  30. package/dist/api/posts.d.ts +27 -0
  31. package/dist/api/posts.js +98 -0
  32. package/dist/api/posts.js.map +1 -0
  33. package/dist/api/rss.cjs +25 -0
  34. package/dist/api/rss.cjs.map +1 -0
  35. package/dist/api/rss.d.cts +5 -0
  36. package/dist/api/rss.d.ts +5 -0
  37. package/dist/api/rss.js +23 -0
  38. package/dist/api/rss.js.map +1 -0
  39. package/dist/api/settings.cjs +40 -0
  40. package/dist/api/settings.cjs.map +1 -0
  41. package/dist/api/settings.d.cts +17 -0
  42. package/dist/api/settings.d.ts +17 -0
  43. package/dist/api/settings.js +37 -0
  44. package/dist/api/settings.js.map +1 -0
  45. package/dist/api/sitemap.cjs +25 -0
  46. package/dist/api/sitemap.cjs.map +1 -0
  47. package/dist/api/sitemap.d.cts +5 -0
  48. package/dist/api/sitemap.d.ts +5 -0
  49. package/dist/api/sitemap.js +23 -0
  50. package/dist/api/sitemap.js.map +1 -0
  51. package/dist/api/tokens.cjs +56 -0
  52. package/dist/api/tokens.cjs.map +1 -0
  53. package/dist/api/tokens.d.cts +22 -0
  54. package/dist/api/tokens.d.ts +22 -0
  55. package/dist/api/tokens.js +52 -0
  56. package/dist/api/tokens.js.map +1 -0
  57. package/dist/chunk-2M46MMWN.cjs +76 -0
  58. package/dist/chunk-2M46MMWN.cjs.map +1 -0
  59. package/dist/chunk-4GK22Q3R.cjs +229 -0
  60. package/dist/chunk-4GK22Q3R.cjs.map +1 -0
  61. package/dist/chunk-6PZ772NQ.js +595 -0
  62. package/dist/chunk-6PZ772NQ.js.map +1 -0
  63. package/dist/chunk-BZXPRGLN.cjs +351 -0
  64. package/dist/chunk-BZXPRGLN.cjs.map +1 -0
  65. package/dist/chunk-DAX5C6SC.cjs +86 -0
  66. package/dist/chunk-DAX5C6SC.cjs.map +1 -0
  67. package/dist/chunk-DWNMHQ4I.cjs +57 -0
  68. package/dist/chunk-DWNMHQ4I.cjs.map +1 -0
  69. package/dist/chunk-FPGVR2BX.cjs +121 -0
  70. package/dist/chunk-FPGVR2BX.cjs.map +1 -0
  71. package/dist/chunk-JBP3G4QJ.js +50 -0
  72. package/dist/chunk-JBP3G4QJ.js.map +1 -0
  73. package/dist/chunk-JDP4DIHI.js +215 -0
  74. package/dist/chunk-JDP4DIHI.js.map +1 -0
  75. package/dist/chunk-LIZOVA4W.js +118 -0
  76. package/dist/chunk-LIZOVA4W.js.map +1 -0
  77. package/dist/chunk-QLBTOCT3.js +118 -0
  78. package/dist/chunk-QLBTOCT3.js.map +1 -0
  79. package/dist/chunk-UI2KDZ5H.js +344 -0
  80. package/dist/chunk-UI2KDZ5H.js.map +1 -0
  81. package/dist/chunk-V7QPRX4A.cjs +125 -0
  82. package/dist/chunk-V7QPRX4A.cjs.map +1 -0
  83. package/dist/chunk-VEJFN65Y.js +82 -0
  84. package/dist/chunk-VEJFN65Y.js.map +1 -0
  85. package/dist/chunk-WYZFAI6O.js +74 -0
  86. package/dist/chunk-WYZFAI6O.js.map +1 -0
  87. package/dist/chunk-XLYTATRQ.cjs +637 -0
  88. package/dist/chunk-XLYTATRQ.cjs.map +1 -0
  89. package/dist/cli/index.cjs +2036 -0
  90. package/dist/components/index.cjs +647 -0
  91. package/dist/components/index.cjs.map +1 -0
  92. package/dist/components/index.d.cts +187 -0
  93. package/dist/components/index.d.ts +187 -0
  94. package/dist/components/index.js +633 -0
  95. package/dist/components/index.js.map +1 -0
  96. package/dist/db-M3KXLBUP.js +3 -0
  97. package/dist/db-M3KXLBUP.js.map +1 -0
  98. package/dist/db-TDCZNY3Q.cjs +112 -0
  99. package/dist/db-TDCZNY3Q.cjs.map +1 -0
  100. package/dist/editor/index.cjs +1126 -0
  101. package/dist/editor/index.cjs.map +1 -0
  102. package/dist/editor/index.d.cts +137 -0
  103. package/dist/editor/index.d.ts +137 -0
  104. package/dist/editor/index.js +1093 -0
  105. package/dist/editor/index.js.map +1 -0
  106. package/dist/index-BcNka7gK.d.cts +475 -0
  107. package/dist/index-DPRgaNEE.d.ts +475 -0
  108. package/dist/index.cjs +433 -0
  109. package/dist/index.cjs.map +1 -0
  110. package/dist/index.d.cts +27 -0
  111. package/dist/index.d.ts +27 -0
  112. package/dist/index.js +209 -0
  113. package/dist/index.js.map +1 -0
  114. package/dist/lib/index.cjs +193 -0
  115. package/dist/lib/index.cjs.map +1 -0
  116. package/dist/lib/index.d.cts +4 -0
  117. package/dist/lib/index.d.ts +4 -0
  118. package/dist/lib/index.js +8 -0
  119. package/dist/lib/index.js.map +1 -0
  120. package/dist/styles/admin.css +667 -0
  121. package/dist/styles/blog.css +851 -0
  122. package/dist/styles/editor.css +452 -0
  123. package/dist/styles/globals.css +270 -0
  124. package/dist/styles/prose.css +299 -0
  125. package/dist/types-CgrBuig-.d.cts +778 -0
  126. package/dist/types-CgrBuig-.d.ts +778 -0
  127. package/package.json +140 -0
@@ -0,0 +1,633 @@
1
+ "use client";
2
+ import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
3
+ import { useState, useRef, useCallback, useEffect } from 'react';
4
+
5
+ // src/components/BlogCard.tsx
6
+ function BlogCard({
7
+ post,
8
+ layout = "vertical",
9
+ basePath = "/blog",
10
+ className = ""
11
+ }) {
12
+ const date = post.publishedAt ? new Date(post.publishedAt).toLocaleDateString("en-US", {
13
+ year: "numeric",
14
+ month: "long",
15
+ day: "numeric"
16
+ }) : "";
17
+ return /* @__PURE__ */ jsx("article", { className: `nbp-card nbp-card-${layout} ${className}`, children: /* @__PURE__ */ jsxs("a", { href: `${basePath}/${post.slug}`, className: "nbp-card-link", children: [
18
+ post.coverImage?.url && /* @__PURE__ */ jsx("div", { className: "nbp-card-image", children: /* @__PURE__ */ jsx(
19
+ "img",
20
+ {
21
+ src: post.coverImage.url,
22
+ alt: post.coverImage.alt || post.title,
23
+ loading: "lazy"
24
+ }
25
+ ) }),
26
+ /* @__PURE__ */ jsxs("div", { className: "nbp-card-content", children: [
27
+ post.categories.length > 0 && /* @__PURE__ */ jsx("div", { className: "nbp-card-categories", children: post.categories.map((cat) => /* @__PURE__ */ jsx("span", { className: "nbp-card-category", children: cat }, cat)) }),
28
+ /* @__PURE__ */ jsx("h2", { className: "nbp-card-title", children: post.title }),
29
+ /* @__PURE__ */ jsx("p", { className: "nbp-card-excerpt", children: post.excerpt }),
30
+ /* @__PURE__ */ jsxs("div", { className: "nbp-card-meta", children: [
31
+ post.author.avatar && /* @__PURE__ */ jsx(
32
+ "img",
33
+ {
34
+ src: post.author.avatar,
35
+ alt: post.author.name,
36
+ className: "nbp-card-avatar"
37
+ }
38
+ ),
39
+ /* @__PURE__ */ jsx("span", { className: "nbp-card-author", children: post.author.name }),
40
+ date && /* @__PURE__ */ jsxs(Fragment, { children: [
41
+ /* @__PURE__ */ jsx("span", { className: "nbp-card-sep", children: "\xB7" }),
42
+ /* @__PURE__ */ jsx("time", { className: "nbp-card-date", children: date })
43
+ ] }),
44
+ /* @__PURE__ */ jsx("span", { className: "nbp-card-sep", children: "\xB7" }),
45
+ /* @__PURE__ */ jsxs("span", { className: "nbp-card-reading-time", children: [
46
+ post.readingTime,
47
+ " min read"
48
+ ] })
49
+ ] })
50
+ ] })
51
+ ] }) });
52
+ }
53
+ function BlogSearch({
54
+ onSearch,
55
+ apiPath = "/api/blog",
56
+ basePath = "/blog",
57
+ placeholder = "Search posts...",
58
+ className = ""
59
+ }) {
60
+ const [query, setQuery] = useState("");
61
+ const [results, setResults] = useState([]);
62
+ const [showResults, setShowResults] = useState(false);
63
+ const [loading, setLoading] = useState(false);
64
+ const debounceRef = useRef();
65
+ const containerRef = useRef(null);
66
+ const search = useCallback(
67
+ async (q) => {
68
+ if (!q.trim()) {
69
+ setResults([]);
70
+ setShowResults(false);
71
+ return;
72
+ }
73
+ setLoading(true);
74
+ try {
75
+ const res = await fetch(`${apiPath}/posts?q=${encodeURIComponent(q)}&limit=5&status=published`);
76
+ const data = await res.json();
77
+ if (data.success) {
78
+ setResults(data.data || []);
79
+ setShowResults(true);
80
+ }
81
+ } catch {
82
+ setResults([]);
83
+ } finally {
84
+ setLoading(false);
85
+ }
86
+ },
87
+ [apiPath]
88
+ );
89
+ const handleChange = (e) => {
90
+ const value = e.target.value;
91
+ setQuery(value);
92
+ if (debounceRef.current) clearTimeout(debounceRef.current);
93
+ debounceRef.current = setTimeout(() => {
94
+ void search(value);
95
+ onSearch?.(value);
96
+ }, 300);
97
+ };
98
+ const handleSubmit = (e) => {
99
+ e.preventDefault();
100
+ onSearch?.(query);
101
+ setShowResults(false);
102
+ };
103
+ useEffect(() => {
104
+ function handleClickOutside(e) {
105
+ if (containerRef.current && !containerRef.current.contains(e.target)) {
106
+ setShowResults(false);
107
+ }
108
+ }
109
+ document.addEventListener("mousedown", handleClickOutside);
110
+ return () => document.removeEventListener("mousedown", handleClickOutside);
111
+ }, []);
112
+ return /* @__PURE__ */ jsxs("div", { className: `nbp-search ${className}`, ref: containerRef, children: [
113
+ /* @__PURE__ */ jsxs("form", { onSubmit: handleSubmit, className: "nbp-search-form", children: [
114
+ /* @__PURE__ */ jsx(
115
+ "input",
116
+ {
117
+ type: "search",
118
+ value: query,
119
+ onChange: handleChange,
120
+ placeholder,
121
+ className: "nbp-search-input",
122
+ onFocus: () => results.length > 0 && setShowResults(true)
123
+ }
124
+ ),
125
+ loading && /* @__PURE__ */ jsx("span", { className: "nbp-search-spinner" })
126
+ ] }),
127
+ showResults && results.length > 0 && /* @__PURE__ */ jsx("div", { className: "nbp-search-results", children: results.map((result) => /* @__PURE__ */ jsxs(
128
+ "a",
129
+ {
130
+ href: `${basePath}/${result.slug}`,
131
+ className: "nbp-search-result",
132
+ onClick: () => setShowResults(false),
133
+ children: [
134
+ /* @__PURE__ */ jsx("div", { className: "nbp-search-result-title", children: result.title }),
135
+ /* @__PURE__ */ jsx("div", { className: "nbp-search-result-excerpt", children: result.excerpt?.slice(0, 100) })
136
+ ]
137
+ },
138
+ result.slug
139
+ )) })
140
+ ] });
141
+ }
142
+ function TableOfContentsComponent({ headings, className = "" }) {
143
+ const [activeId, setActiveId] = useState("");
144
+ useEffect(() => {
145
+ const observer = new IntersectionObserver(
146
+ (entries) => {
147
+ for (const entry of entries) {
148
+ if (entry.isIntersecting) {
149
+ setActiveId(entry.target.id);
150
+ }
151
+ }
152
+ },
153
+ { rootMargin: "-80px 0px -80% 0px" }
154
+ );
155
+ headings.forEach(({ id }) => {
156
+ const el = document.getElementById(id);
157
+ if (el) observer.observe(el);
158
+ });
159
+ return () => observer.disconnect();
160
+ }, [headings]);
161
+ if (headings.length === 0) return null;
162
+ return /* @__PURE__ */ jsxs("nav", { className: `nbp-toc ${className}`, children: [
163
+ /* @__PURE__ */ jsx("h3", { className: "nbp-toc-title", children: "Table of Contents" }),
164
+ /* @__PURE__ */ jsx("ul", { className: "nbp-toc-list", children: headings.map((heading) => /* @__PURE__ */ jsx(
165
+ "li",
166
+ {
167
+ className: `nbp-toc-item nbp-toc-level-${heading.level} ${activeId === heading.id ? "active" : ""}`,
168
+ children: /* @__PURE__ */ jsx("a", { href: `#${heading.id}`, className: "nbp-toc-link", children: heading.text })
169
+ },
170
+ heading.id
171
+ )) })
172
+ ] });
173
+ }
174
+ function ShareButtons({ url, title, className = "" }) {
175
+ const [copied, setCopied] = useState(false);
176
+ const fullUrl = typeof window !== "undefined" ? `${window.location.origin}${url}` : url;
177
+ const encodedUrl = encodeURIComponent(fullUrl);
178
+ const encodedTitle = encodeURIComponent(title);
179
+ const share = (platform) => {
180
+ const urls = {
181
+ twitter: `https://twitter.com/intent/tweet?text=${encodedTitle}&url=${encodedUrl}`,
182
+ linkedin: `https://www.linkedin.com/sharing/share-offsite/?url=${encodedUrl}`,
183
+ facebook: `https://www.facebook.com/sharer/sharer.php?u=${encodedUrl}`
184
+ };
185
+ const shareUrl = urls[platform];
186
+ if (shareUrl) {
187
+ window.open(shareUrl, "_blank", "width=600,height=400");
188
+ }
189
+ };
190
+ const copyLink = async () => {
191
+ try {
192
+ await navigator.clipboard.writeText(fullUrl);
193
+ setCopied(true);
194
+ setTimeout(() => setCopied(false), 2e3);
195
+ } catch {
196
+ const input = document.createElement("input");
197
+ input.value = fullUrl;
198
+ document.body.appendChild(input);
199
+ input.select();
200
+ document.execCommand("copy");
201
+ document.body.removeChild(input);
202
+ setCopied(true);
203
+ setTimeout(() => setCopied(false), 2e3);
204
+ }
205
+ };
206
+ return /* @__PURE__ */ jsxs("div", { className: `nbp-share ${className}`, children: [
207
+ /* @__PURE__ */ jsx("span", { className: "nbp-share-label", children: "Share this post:" }),
208
+ /* @__PURE__ */ jsxs("div", { className: "nbp-share-buttons", children: [
209
+ /* @__PURE__ */ jsx("button", { onClick: () => share("twitter"), className: "nbp-share-btn nbp-share-twitter", title: "Share on Twitter", children: /* @__PURE__ */ jsx("svg", { viewBox: "0 0 24 24", width: "18", height: "18", fill: "currentColor", children: /* @__PURE__ */ jsx("path", { d: "M18.244 2.25h3.308l-7.227 8.26 8.502 11.24H16.17l-5.214-6.817L4.99 21.75H1.68l7.73-8.835L1.254 2.25H8.08l4.713 6.231zm-1.161 17.52h1.833L7.084 4.126H5.117z" }) }) }),
210
+ /* @__PURE__ */ jsx("button", { onClick: () => share("linkedin"), className: "nbp-share-btn nbp-share-linkedin", title: "Share on LinkedIn", children: /* @__PURE__ */ jsx("svg", { viewBox: "0 0 24 24", width: "18", height: "18", fill: "currentColor", children: /* @__PURE__ */ jsx("path", { d: "M20.447 20.452h-3.554v-5.569c0-1.328-.027-3.037-1.852-3.037-1.853 0-2.136 1.445-2.136 2.939v5.667H9.351V9h3.414v1.561h.046c.477-.9 1.637-1.85 3.37-1.85 3.601 0 4.267 2.37 4.267 5.455v6.286zM5.337 7.433a2.062 2.062 0 01-2.063-2.065 2.064 2.064 0 112.063 2.065zm1.782 13.019H3.555V9h3.564v11.452zM22.225 0H1.771C.792 0 0 .774 0 1.729v20.542C0 23.227.792 24 1.771 24h20.451C23.2 24 24 23.227 24 22.271V1.729C24 .774 23.2 0 22.222 0h.003z" }) }) }),
211
+ /* @__PURE__ */ jsx("button", { onClick: () => share("facebook"), className: "nbp-share-btn nbp-share-facebook", title: "Share on Facebook", children: /* @__PURE__ */ jsx("svg", { viewBox: "0 0 24 24", width: "18", height: "18", fill: "currentColor", children: /* @__PURE__ */ jsx("path", { d: "M24 12.073c0-6.627-5.373-12-12-12s-12 5.373-12 12c0 5.99 4.388 10.954 10.125 11.854v-8.385H7.078v-3.47h3.047V9.43c0-3.007 1.792-4.669 4.533-4.669 1.312 0 2.686.235 2.686.235v2.953H15.83c-1.491 0-1.956.925-1.956 1.874v2.25h3.328l-.532 3.47h-2.796v8.385C19.612 23.027 24 18.062 24 12.073z" }) }) }),
212
+ /* @__PURE__ */ jsx("button", { onClick: copyLink, className: "nbp-share-btn nbp-share-copy", title: copied ? "Copied!" : "Copy link", children: copied ? /* @__PURE__ */ jsx("svg", { viewBox: "0 0 24 24", width: "18", height: "18", fill: "none", stroke: "currentColor", strokeWidth: "2", children: /* @__PURE__ */ jsx("path", { d: "M20 6L9 17l-5-5" }) }) : /* @__PURE__ */ jsxs("svg", { viewBox: "0 0 24 24", width: "18", height: "18", fill: "none", stroke: "currentColor", strokeWidth: "2", children: [
213
+ /* @__PURE__ */ jsx("rect", { x: "9", y: "9", width: "13", height: "13", rx: "2", ry: "2" }),
214
+ /* @__PURE__ */ jsx("path", { d: "M5 15H4a2 2 0 01-2-2V4a2 2 0 012-2h9a2 2 0 012 2v1" })
215
+ ] }) })
216
+ ] })
217
+ ] });
218
+ }
219
+ function ReadingProgressBar({ className = "" }) {
220
+ const [progress, setProgress] = useState(0);
221
+ useEffect(() => {
222
+ function handleScroll() {
223
+ const scrollTop = window.scrollY;
224
+ const docHeight = document.documentElement.scrollHeight - window.innerHeight;
225
+ if (docHeight > 0) {
226
+ setProgress(Math.min(scrollTop / docHeight * 100, 100));
227
+ }
228
+ }
229
+ window.addEventListener("scroll", handleScroll, { passive: true });
230
+ return () => window.removeEventListener("scroll", handleScroll);
231
+ }, []);
232
+ return /* @__PURE__ */ jsx("div", { className: `nbp-progress-bar ${className}`, children: /* @__PURE__ */ jsx(
233
+ "div",
234
+ {
235
+ className: "nbp-progress-fill",
236
+ style: { width: `${progress}%` }
237
+ }
238
+ ) });
239
+ }
240
+ function buildHref(basePath, page, category) {
241
+ const params = new URLSearchParams();
242
+ if (page > 1) params.set("page", String(page));
243
+ if (category) params.set("category", category);
244
+ const qs = params.toString();
245
+ return qs ? `${basePath}?${qs}` : basePath;
246
+ }
247
+ function Pagination({
248
+ currentPage,
249
+ totalPages,
250
+ basePath = "/blog",
251
+ category,
252
+ className = ""
253
+ }) {
254
+ if (totalPages <= 1) return null;
255
+ const pages = [];
256
+ const maxVisible = 5;
257
+ if (totalPages <= maxVisible + 2) {
258
+ for (let i = 1; i <= totalPages; i++) pages.push(i);
259
+ } else {
260
+ pages.push(1);
261
+ const start = Math.max(2, currentPage - 1);
262
+ const end = Math.min(totalPages - 1, currentPage + 1);
263
+ if (start > 2) pages.push("...");
264
+ for (let i = start; i <= end; i++) pages.push(i);
265
+ if (end < totalPages - 1) pages.push("...");
266
+ pages.push(totalPages);
267
+ }
268
+ return /* @__PURE__ */ jsxs("nav", { className: `nbp-pagination ${className}`, children: [
269
+ currentPage > 1 ? /* @__PURE__ */ jsx("a", { href: buildHref(basePath, currentPage - 1, category), className: "nbp-pagination-btn", children: "Previous" }) : /* @__PURE__ */ jsx("span", { className: "nbp-pagination-btn disabled", children: "Previous" }),
270
+ /* @__PURE__ */ jsx("div", { className: "nbp-pagination-pages", children: pages.map(
271
+ (page, idx) => typeof page === "string" ? /* @__PURE__ */ jsx("span", { className: "nbp-pagination-ellipsis", children: "..." }, `ellipsis-${idx}`) : page === currentPage ? /* @__PURE__ */ jsx("span", { className: "nbp-pagination-page active", children: page }, page) : /* @__PURE__ */ jsx(
272
+ "a",
273
+ {
274
+ href: buildHref(basePath, page, category),
275
+ className: "nbp-pagination-page",
276
+ children: page
277
+ },
278
+ page
279
+ )
280
+ ) }),
281
+ currentPage < totalPages ? /* @__PURE__ */ jsx("a", { href: buildHref(basePath, currentPage + 1, category), className: "nbp-pagination-btn", children: "Next" }) : /* @__PURE__ */ jsx("span", { className: "nbp-pagination-btn disabled", children: "Next" })
282
+ ] });
283
+ }
284
+ function AuthorCard({ author, className = "" }) {
285
+ return /* @__PURE__ */ jsxs("div", { className: `nbp-author-card ${className}`, children: [
286
+ author.avatar && /* @__PURE__ */ jsx("img", { src: author.avatar, alt: author.name, className: "nbp-author-avatar" }),
287
+ /* @__PURE__ */ jsxs("div", { className: "nbp-author-info", children: [
288
+ /* @__PURE__ */ jsx("div", { className: "nbp-author-name", children: author.url ? /* @__PURE__ */ jsx("a", { href: author.url, target: "_blank", rel: "noopener noreferrer", children: author.name }) : author.name }),
289
+ author.bio && /* @__PURE__ */ jsx("p", { className: "nbp-author-bio", children: author.bio })
290
+ ] })
291
+ ] });
292
+ }
293
+ function BreadcrumbNav({ items, className = "" }) {
294
+ return /* @__PURE__ */ jsx("nav", { className: `nbp-breadcrumb ${className}`, "aria-label": "Breadcrumb", children: /* @__PURE__ */ jsx("ol", { className: "nbp-breadcrumb-list", children: items.map((item, idx) => /* @__PURE__ */ jsxs("li", { className: "nbp-breadcrumb-item", children: [
295
+ idx > 0 && /* @__PURE__ */ jsx("span", { className: "nbp-breadcrumb-sep", children: "/" }),
296
+ item.href ? /* @__PURE__ */ jsx("a", { href: item.href, className: "nbp-breadcrumb-link", children: item.label }) : /* @__PURE__ */ jsx("span", { className: "nbp-breadcrumb-current", children: item.label })
297
+ ] }, idx)) }) });
298
+ }
299
+ function CategoryList({
300
+ categories,
301
+ activeCategory,
302
+ basePath = "/blog",
303
+ className = ""
304
+ }) {
305
+ if (categories.length === 0) return null;
306
+ return /* @__PURE__ */ jsxs("div", { className: `nbp-category-list ${className}`, children: [
307
+ /* @__PURE__ */ jsx("h3", { className: "nbp-category-list-title", children: "Categories" }),
308
+ /* @__PURE__ */ jsx("ul", { className: "nbp-category-items", children: categories.map((cat) => /* @__PURE__ */ jsx("li", { className: "nbp-category-item", children: /* @__PURE__ */ jsxs(
309
+ "a",
310
+ {
311
+ href: `${basePath}/category/${cat.slug}`,
312
+ className: `nbp-category-btn ${activeCategory === cat.slug ? "active" : ""}`,
313
+ children: [
314
+ cat.name,
315
+ /* @__PURE__ */ jsx("span", { className: "nbp-category-count", children: cat.postCount })
316
+ ]
317
+ }
318
+ ) }, cat.slug)) })
319
+ ] });
320
+ }
321
+ function TagCloud({ tags, basePath = "/blog", className = "" }) {
322
+ if (tags.length === 0) return null;
323
+ const maxCount = Math.max(...tags.map((t) => t.count));
324
+ return /* @__PURE__ */ jsxs("div", { className: `nbp-tag-cloud ${className}`, children: [
325
+ /* @__PURE__ */ jsx("h3", { className: "nbp-tag-cloud-title", children: "Tags" }),
326
+ /* @__PURE__ */ jsx("div", { className: "nbp-tags", children: tags.map((tag) => {
327
+ const size = 0.8 + tag.count / maxCount * 0.6;
328
+ return /* @__PURE__ */ jsxs(
329
+ "a",
330
+ {
331
+ href: `${basePath}?tag=${tag.name}`,
332
+ className: "nbp-tag",
333
+ style: { fontSize: `${size}rem` },
334
+ children: [
335
+ "#",
336
+ tag.name
337
+ ]
338
+ },
339
+ tag.name
340
+ );
341
+ }) })
342
+ ] });
343
+ }
344
+ function CodeBlock({
345
+ code,
346
+ language = "plaintext",
347
+ filename,
348
+ className = ""
349
+ }) {
350
+ const [copied, setCopied] = useState(false);
351
+ const handleCopy = async () => {
352
+ try {
353
+ await navigator.clipboard.writeText(code);
354
+ setCopied(true);
355
+ setTimeout(() => setCopied(false), 2e3);
356
+ } catch {
357
+ }
358
+ };
359
+ return /* @__PURE__ */ jsxs("div", { className: `nbp-code-block ${className}`, children: [
360
+ /* @__PURE__ */ jsxs("div", { className: "nbp-code-header", children: [
361
+ /* @__PURE__ */ jsx("span", { className: "nbp-code-lang", children: filename || language }),
362
+ /* @__PURE__ */ jsx("button", { onClick: handleCopy, className: "nbp-code-copy", children: copied ? "Copied!" : "Copy" })
363
+ ] }),
364
+ /* @__PURE__ */ jsx("pre", { className: `nbp-code-pre language-${language}`, children: /* @__PURE__ */ jsx("code", { children: code }) })
365
+ ] });
366
+ }
367
+ function BlogListPage({
368
+ posts,
369
+ total,
370
+ page = 1,
371
+ postsPerPage = 10,
372
+ categories = [],
373
+ activeCategory,
374
+ showCategories = true,
375
+ showSearch = true,
376
+ layout = "grid",
377
+ basePath = "/blog",
378
+ apiPath = "/api/blog",
379
+ className = "",
380
+ slots
381
+ }) {
382
+ const totalPages = Math.ceil(total / postsPerPage);
383
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
384
+ slots?.header,
385
+ /* @__PURE__ */ jsxs("div", { className: `nbp-blog-list ${className}`, children: [
386
+ /* @__PURE__ */ jsx("div", { className: "nbp-blog-header", children: showSearch && /* @__PURE__ */ jsx(BlogSearch, { apiPath }) }),
387
+ slots?.beforePosts,
388
+ /* @__PURE__ */ jsxs("div", { className: "nbp-blog-layout", children: [
389
+ /* @__PURE__ */ jsxs("div", { className: "nbp-blog-main", children: [
390
+ posts.length === 0 ? /* @__PURE__ */ jsx("div", { className: "nbp-empty-state", children: /* @__PURE__ */ jsx("p", { children: "No posts found." }) }) : /* @__PURE__ */ jsx("div", { className: `nbp-posts-${layout}`, children: posts.map(
391
+ (post) => slots?.renderCard ? slots.renderCard(post) : /* @__PURE__ */ jsx(
392
+ BlogCard,
393
+ {
394
+ post,
395
+ layout: layout === "list" ? "horizontal" : "vertical",
396
+ basePath
397
+ },
398
+ String(post._id || post.slug)
399
+ )
400
+ ) }),
401
+ totalPages > 1 && /* @__PURE__ */ jsx(
402
+ Pagination,
403
+ {
404
+ currentPage: page,
405
+ totalPages,
406
+ basePath,
407
+ category: activeCategory
408
+ }
409
+ )
410
+ ] }),
411
+ slots?.sidebar || showCategories && categories.length > 0 && /* @__PURE__ */ jsx("aside", { className: "nbp-blog-sidebar", children: /* @__PURE__ */ jsx(
412
+ CategoryList,
413
+ {
414
+ categories,
415
+ activeCategory,
416
+ basePath
417
+ }
418
+ ) })
419
+ ] }),
420
+ slots?.afterPosts
421
+ ] }),
422
+ slots?.footer
423
+ ] });
424
+ }
425
+
426
+ // src/lib/url.ts
427
+ function isAbsoluteUrl(url) {
428
+ return /^[a-z][a-z0-9+.-]*:/i.test(url) || url.startsWith("//");
429
+ }
430
+ var warned = /* @__PURE__ */ Symbol.for("next-blogpanel.url.warned");
431
+ function warnOnce(context, url) {
432
+ const store = globalThis;
433
+ const seen = store[warned] || (store[warned] = /* @__PURE__ */ new Set());
434
+ if (seen.has(context)) return;
435
+ seen.add(context);
436
+ console.warn(
437
+ `[blogpanel] BLOGPANEL_SITE_URL is not set, so ${context} was omitted rather than emitted as the relative URL "${url}". Open Graph, Twitter cards, schema.org and RSS all require absolute URLs; a relative one is not a smaller version of the right answer, it is invalid. Set BLOGPANEL_SITE_URL to your site's origin (no trailing slash).`
438
+ );
439
+ }
440
+ function absoluteUrl(url, siteUrl, context) {
441
+ if (!url) return void 0;
442
+ if (isAbsoluteUrl(url)) return url;
443
+ const base = (siteUrl || "").trim().replace(/\/+$/, "");
444
+ if (!base) {
445
+ if (context) warnOnce(context, url);
446
+ return void 0;
447
+ }
448
+ return url.startsWith("/") ? `${base}${url}` : `${base}/${url}`;
449
+ }
450
+ function BlogPostPage({
451
+ post,
452
+ relatedPosts = [],
453
+ showTOC = true,
454
+ tocPosition = "sidebar",
455
+ showAuthor = true,
456
+ showRelatedPosts = true,
457
+ showShareButtons = true,
458
+ showReadingProgress = true,
459
+ basePath = "/blog",
460
+ siteUrl = "",
461
+ className = "",
462
+ slots
463
+ }) {
464
+ if (!post) {
465
+ return /* @__PURE__ */ jsx("div", { className: "nbp-not-found", children: "Post not found" });
466
+ }
467
+ const headings = [];
468
+ const headingRegex = /<h([2-4])\s+id="([^"]*)"[^>]*>(.*?)<\/h[2-4]>/gi;
469
+ let match;
470
+ while ((match = headingRegex.exec(post.contentHTML || "")) !== null) {
471
+ headings.push({
472
+ level: parseInt(match[1]),
473
+ id: match[2],
474
+ text: match[3].replace(/<[^>]+>/g, "")
475
+ });
476
+ }
477
+ const date = post.publishedAt ? new Date(post.publishedAt).toLocaleDateString("en-US", {
478
+ year: "numeric",
479
+ month: "long",
480
+ day: "numeric"
481
+ }) : "";
482
+ const postUrl = `${basePath}/${post.slug}`;
483
+ const ldUrl = (path) => absoluteUrl(path, siteUrl) ?? path;
484
+ const ldImage = absoluteUrl(
485
+ post.coverImage?.url || post.seo?.ogImage,
486
+ siteUrl,
487
+ "the JSON-LD image on BlogPostPage (pass the siteUrl prop)"
488
+ );
489
+ const blogLabel = (() => {
490
+ const segment = basePath.split("/").filter(Boolean).pop() || "Blog";
491
+ return segment.charAt(0).toUpperCase() + segment.slice(1);
492
+ })();
493
+ const hasTOC = showTOC && tocPosition !== "none" && headings.length > 2;
494
+ const hasSidebarTOC = hasTOC && tocPosition === "sidebar";
495
+ const blogPostingLd = {
496
+ "@context": "https://schema.org",
497
+ "@type": "BlogPosting",
498
+ headline: post.title,
499
+ description: post.excerpt || "",
500
+ image: ldImage,
501
+ datePublished: post.publishedAt || void 0,
502
+ dateModified: post.updatedAt || post.publishedAt || void 0,
503
+ author: {
504
+ "@type": "Person",
505
+ name: post.author?.name || "Unknown",
506
+ ...post.author?.url ? { url: post.author.url } : {}
507
+ },
508
+ mainEntityOfPage: { "@type": "WebPage", "@id": ldUrl(postUrl) },
509
+ ...post.wordCount ? { wordCount: post.wordCount } : {},
510
+ ...post.categories?.[0] ? { articleSection: post.categories[0] } : {}
511
+ };
512
+ const breadcrumbLd = {
513
+ "@context": "https://schema.org",
514
+ "@type": "BreadcrumbList",
515
+ itemListElement: [
516
+ { "@type": "ListItem", position: 1, name: "Home", item: ldUrl("/") },
517
+ { "@type": "ListItem", position: 2, name: blogLabel, item: ldUrl(basePath) },
518
+ ...post.categories?.[0] ? [
519
+ { "@type": "ListItem", position: 3, name: post.categories[0], item: ldUrl(`${basePath}/category/${post.categories[0]}`) },
520
+ { "@type": "ListItem", position: 4, name: post.title }
521
+ ] : [{ "@type": "ListItem", position: 3, name: post.title }]
522
+ ]
523
+ };
524
+ const faqItems = [];
525
+ const faqRegex = /<div[^>]*class="nbp-faq-question"[^>]*>(.*?)<\/div>\s*<div[^>]*class="nbp-faq-answer"[^>]*>(.*?)<\/div>/gis;
526
+ let faqMatch;
527
+ while ((faqMatch = faqRegex.exec(post.contentHTML || "")) !== null) {
528
+ faqItems.push({
529
+ question: faqMatch[1].replace(/<[^>]+>/g, "").trim(),
530
+ answer: faqMatch[2].replace(/<[^>]+>/g, "").trim()
531
+ });
532
+ }
533
+ const faqLd = faqItems.length > 0 ? {
534
+ "@context": "https://schema.org",
535
+ "@type": "FAQPage",
536
+ mainEntity: faqItems.map((item) => ({
537
+ "@type": "Question",
538
+ name: item.question,
539
+ acceptedAnswer: { "@type": "Answer", text: item.answer }
540
+ }))
541
+ } : null;
542
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
543
+ /* @__PURE__ */ jsx(
544
+ "script",
545
+ {
546
+ type: "application/ld+json",
547
+ dangerouslySetInnerHTML: { __html: JSON.stringify(blogPostingLd) }
548
+ }
549
+ ),
550
+ /* @__PURE__ */ jsx(
551
+ "script",
552
+ {
553
+ type: "application/ld+json",
554
+ dangerouslySetInnerHTML: { __html: JSON.stringify(breadcrumbLd) }
555
+ }
556
+ ),
557
+ faqLd && /* @__PURE__ */ jsx(
558
+ "script",
559
+ {
560
+ type: "application/ld+json",
561
+ dangerouslySetInnerHTML: { __html: JSON.stringify(faqLd) }
562
+ }
563
+ ),
564
+ slots?.header,
565
+ /* @__PURE__ */ jsxs("article", { className: `nbp-post ${hasSidebarTOC ? "nbp-post-with-toc" : ""} ${className}`, children: [
566
+ showReadingProgress && /* @__PURE__ */ jsx(ReadingProgressBar, {}),
567
+ /* @__PURE__ */ jsx(
568
+ BreadcrumbNav,
569
+ {
570
+ items: [
571
+ { label: "Home", href: "/" },
572
+ { label: blogLabel, href: basePath },
573
+ ...post.categories?.[0] ? [{ label: post.categories[0], href: `${basePath}/category/${post.categories[0]}` }] : [],
574
+ { label: post.title }
575
+ ]
576
+ }
577
+ ),
578
+ /* @__PURE__ */ jsxs("header", { className: "nbp-post-header", children: [
579
+ post.categories?.length > 0 && /* @__PURE__ */ jsx("div", { className: "nbp-post-categories", children: post.categories.map((cat) => /* @__PURE__ */ jsx("a", { href: `${basePath}/category/${cat}`, className: "nbp-post-category", children: cat }, cat)) }),
580
+ /* @__PURE__ */ jsx("h1", { className: "nbp-post-title", children: post.title }),
581
+ /* @__PURE__ */ jsxs("div", { className: "nbp-post-meta", children: [
582
+ post.author?.avatar && /* @__PURE__ */ jsx("img", { src: post.author.avatar, alt: post.author.name, className: "nbp-post-avatar" }),
583
+ /* @__PURE__ */ jsx("span", { className: "nbp-post-author", children: post.author?.name }),
584
+ date && /* @__PURE__ */ jsxs(Fragment, { children: [
585
+ /* @__PURE__ */ jsx("span", { className: "nbp-post-sep", children: "\xB7" }),
586
+ /* @__PURE__ */ jsx("time", { className: "nbp-post-date", dateTime: String(post.publishedAt), children: date })
587
+ ] }),
588
+ /* @__PURE__ */ jsx("span", { className: "nbp-post-sep", children: "\xB7" }),
589
+ /* @__PURE__ */ jsxs("span", { className: "nbp-post-reading-time", children: [
590
+ post.readingTime,
591
+ " min read"
592
+ ] })
593
+ ] })
594
+ ] }),
595
+ post.coverImage?.url && /* @__PURE__ */ jsx("div", { className: "nbp-post-cover", children: /* @__PURE__ */ jsx(
596
+ "img",
597
+ {
598
+ src: post.coverImage.url,
599
+ alt: post.coverImage.alt || post.title,
600
+ className: "nbp-post-cover-img"
601
+ }
602
+ ) }),
603
+ hasTOC && tocPosition === "top" && /* @__PURE__ */ jsx("div", { className: "nbp-post-toc-inline", children: /* @__PURE__ */ jsx(TableOfContentsComponent, { headings }) }),
604
+ slots?.beforeContent,
605
+ /* @__PURE__ */ jsxs("div", { className: "nbp-post-layout", children: [
606
+ hasSidebarTOC && /* @__PURE__ */ jsx("aside", { className: "nbp-post-toc-sidebar", children: /* @__PURE__ */ jsx(TableOfContentsComponent, { headings }) }),
607
+ /* @__PURE__ */ jsx(
608
+ "div",
609
+ {
610
+ className: "nbp-post-content",
611
+ dangerouslySetInnerHTML: { __html: post.contentHTML || "" }
612
+ }
613
+ )
614
+ ] }),
615
+ slots?.afterContent,
616
+ post.tags?.length > 0 && /* @__PURE__ */ jsx("div", { className: "nbp-post-tags", children: post.tags.map((tag) => /* @__PURE__ */ jsxs("a", { href: `${basePath}?tag=${tag}`, className: "nbp-tag", children: [
617
+ "#",
618
+ tag
619
+ ] }, tag)) }),
620
+ showShareButtons && /* @__PURE__ */ jsx(ShareButtons, { url: postUrl, title: post.title }),
621
+ showAuthor && post.author && (post.author.bio || post.author.avatar) && /* @__PURE__ */ jsx(AuthorCard, { author: post.author }),
622
+ showRelatedPosts && relatedPosts.length > 0 && /* @__PURE__ */ jsxs("section", { className: "nbp-related", children: [
623
+ /* @__PURE__ */ jsx("h2", { className: "nbp-related-title", children: "Related Posts" }),
624
+ /* @__PURE__ */ jsx("div", { className: "nbp-related-grid", children: relatedPosts.map((p) => /* @__PURE__ */ jsx(BlogCard, { post: p, basePath }, String(p._id || p.slug))) })
625
+ ] })
626
+ ] }),
627
+ slots?.footer
628
+ ] });
629
+ }
630
+
631
+ export { AuthorCard, BlogCard, BlogListPage, BlogPostPage, BlogSearch, BreadcrumbNav, CategoryList, CodeBlock, Pagination, ReadingProgressBar, ShareButtons, TableOfContentsComponent as TableOfContents, TagCloud };
632
+ //# sourceMappingURL=index.js.map
633
+ //# sourceMappingURL=index.js.map