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