@fullstackdatasolutions/articles 1.1.0 → 1.2.1
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/CHANGELOG.md +20 -0
- package/README.md +1 -0
- package/dist/index.cjs +41 -19
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +3 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.js +41 -19
- package/dist/index.js.map +1 -1
- package/dist/nextjs.cjs +13 -3
- package/dist/nextjs.cjs.map +1 -1
- package/dist/nextjs.js +13 -3
- package/dist/nextjs.js.map +1 -1
- package/dist/server.cjs +13 -3
- package/dist/server.cjs.map +1 -1
- package/dist/server.js +13 -3
- package/dist/server.js.map +1 -1
- package/package.json +1 -1
- package/src/ArticleDetailHero.tsx +44 -19
- package/src/Breadcrumb.tsx +4 -1
- package/src/__tests__/ArticleDetailHero.test.tsx +57 -0
- package/src/markdown.ts +48 -3
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,26 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [1.2.1] - 2026-08-13
|
|
9
|
+
|
|
10
|
+
### Fixed
|
|
11
|
+
|
|
12
|
+
- **`extractToc`/`markdownToHtml` mangled or silently dropped headings containing inline JSX or a markdown link.** Both functions parse headings through a plain (non-MDX) `remark-parse`, unlike `renderMdxSource`'s real `@mdx-js/mdx` compilation used for the actual page body. Two distinct bugs stacked on top of each other:
|
|
13
|
+
- A heading with inline JSX (the reader-facing color-coded rating dot used throughout the article library, e.g. `### Rage <span style={{ color: '#3b82f6' }}>●</span>`) isn't valid CommonMark inline HTML - `style={{ ... }}` isn't legal HTML attribute syntax - so remark's HTML tokenizer failed to recognize the tag and fell back to literal text, leaking the raw JSX source into both the visible TOC label (`Rage <span style={{ color: '#3b82f6' }}>●`) and the `rehype-slug`-generated id, which then didn't match the id the real MDX-rendered heading gets in the page - so the TOC entry silently linked to an anchor that didn't exist in the DOM. Fixed by stripping inline tags (keeping their inner text/content) from heading lines before the plain-remark parse, so the extracted text and generated slug match what the real render produces for the same heading.
|
|
14
|
+
- `nodeTextValue` only read a heading's direct text-node children, never recursing into child elements - so a heading using a markdown link as part of its text (e.g. `### [Dwarf](/link)`, a common pattern for hub/spoke class-guide headings) had its link text silently dropped. With the JSX-in-heading bug's own text also gone, this frequently zeroed out both `id` and `text` entirely, causing `extractHeadingItem` to return `null` and the heading to vanish from the TOC completely rather than merely render with garbled text. Fixed by making `nodeTextValue` recurse into element children.
|
|
15
|
+
|
|
16
|
+
No consumer-facing API change - both fixes only affect the text/slugs `extractToc` and `markdownToHtml` produce for previously-broken headings. Verified against theroleplayersguild.com's live-rendered heading ids (which come from the separate, already-correct `renderMdxSource` pipeline) to confirm the corrected slugs now match.
|
|
17
|
+
|
|
18
|
+
## [1.2.0] - 2026-08-12
|
|
19
|
+
|
|
20
|
+
### Added
|
|
21
|
+
|
|
22
|
+
- **`Breadcrumb` accepts `containerClassName`.** New optional prop, default `'max-w-7xl'` (unchanged from before), applied to the breadcrumb `<ol>` in place of the previously hardcoded value. Lets a consuming site align the breadcrumb bar's content width to its own reading column instead of the package's fixed default. Found on theroleplayersguild.com, where the breadcrumb's `max-w-7xl` sat visibly wider than the article body's `max-w-3xl` column beneath it.
|
|
23
|
+
|
|
24
|
+
### Fixed
|
|
25
|
+
|
|
26
|
+
- **`ArticleDetailHero` byline never rendered the configured author avatar.** It always showed a generic user icon even when a single `authors[]` entry had a real `avatar` set (and even though `RelatedArticlesSection`/`ArticleCard` byline avatars worked correctly from the same data). Now resolves and renders the avatar image for the single-author case, matching the existing `ArticleCard` pattern; falls back to the icon when no avatar is set or multiple authors are configured, so existing output is unchanged unless a consumer already had `authors[].avatar` configured.
|
|
27
|
+
|
|
8
28
|
## [1.1.0] - 2026-08-09
|
|
9
29
|
|
|
10
30
|
### Added
|
package/README.md
CHANGED
|
@@ -194,6 +194,7 @@ export default async function Page({ params }: ArticlePageProps) {
|
|
|
194
194
|
article={article}
|
|
195
195
|
authors={authors}
|
|
196
196
|
categoryBasePath="/articles/category"
|
|
197
|
+
config={siteConfig}
|
|
197
198
|
/>
|
|
198
199
|
<ArticleViewTracker article={article} config={siteConfig} />
|
|
199
200
|
{siteConfig.showToc !== false && article.toc && article.toc.length > 0 && (
|
package/dist/index.cjs
CHANGED
|
@@ -265,6 +265,7 @@ function getDisplayItems(items) {
|
|
|
265
265
|
function Breadcrumb({
|
|
266
266
|
items,
|
|
267
267
|
className = "",
|
|
268
|
+
containerClassName = "max-w-7xl",
|
|
268
269
|
showSchema = true,
|
|
269
270
|
separator = ">"
|
|
270
271
|
}) {
|
|
@@ -276,7 +277,7 @@ function Breadcrumb({
|
|
|
276
277
|
{
|
|
277
278
|
"aria-label": "Breadcrumb",
|
|
278
279
|
className: `border-b border-border bg-background/80 px-4 py-3 text-sm text-muted-foreground ${className}`,
|
|
279
|
-
children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("ol", { className:
|
|
280
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("ol", { className: `mx-auto flex items-center gap-2 overflow-hidden ${containerClassName}`, children: displayItems.map((item, index) => {
|
|
280
281
|
const isLast = index === displayItems.length - 1;
|
|
281
282
|
const key = `${item.name}-${index}`;
|
|
282
283
|
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("li", { className: "flex min-w-0 items-center gap-2", children: [
|
|
@@ -1155,6 +1156,15 @@ var import_jsx_runtime13 = require("react/jsx-runtime");
|
|
|
1155
1156
|
function toSlug(cat) {
|
|
1156
1157
|
return cat.toLowerCase().replaceAll(/\s+/g, "-").replaceAll(/[^a-z0-9-]/g, "");
|
|
1157
1158
|
}
|
|
1159
|
+
function resolveAvatarUrl(author, config) {
|
|
1160
|
+
if (!author.avatar) return null;
|
|
1161
|
+
if (author.avatar.startsWith("http://") || author.avatar.startsWith("https://")) {
|
|
1162
|
+
return author.avatar;
|
|
1163
|
+
}
|
|
1164
|
+
if (!(config == null ? void 0 : config.siteUrl)) return null;
|
|
1165
|
+
const siteUrl = config.siteUrl.replace(/\/$/, "");
|
|
1166
|
+
return `${siteUrl}/articles/authors/${author.slug}/${author.avatar.replace(/^\/+/, "")}`;
|
|
1167
|
+
}
|
|
1158
1168
|
function ArticleDetailHero({
|
|
1159
1169
|
article,
|
|
1160
1170
|
categoryBasePath = "/articles/category",
|
|
@@ -1272,24 +1282,36 @@ function ArticleDetailHero({
|
|
|
1272
1282
|
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_lucide_react7.Clock, { className: "h-4 w-4" }),
|
|
1273
1283
|
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { children: article.readTime })
|
|
1274
1284
|
] }),
|
|
1275
|
-
showConfiguredAuthors &&
|
|
1276
|
-
|
|
1277
|
-
/* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1285
|
+
showConfiguredAuthors && (() => {
|
|
1286
|
+
const singleAvatarUrl = authors.length === 1 ? resolveAvatarUrl(authors[0], config) : null;
|
|
1287
|
+
return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: "0.5rem" }, children: [
|
|
1288
|
+
singleAvatarUrl ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
1289
|
+
import_image5.default,
|
|
1290
|
+
{
|
|
1291
|
+
src: singleAvatarUrl,
|
|
1292
|
+
alt: "",
|
|
1293
|
+
width: 20,
|
|
1294
|
+
height: 20,
|
|
1295
|
+
className: "h-5 w-5 rounded-full object-cover"
|
|
1296
|
+
}
|
|
1297
|
+
) : /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_lucide_react7.User, { className: "h-4 w-4" }),
|
|
1298
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("span", { children: [
|
|
1299
|
+
"By",
|
|
1300
|
+
" ",
|
|
1301
|
+
authors.map((author, index) => /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(import_react4.Fragment, { children: [
|
|
1302
|
+
index > 0 && ", ",
|
|
1303
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
1304
|
+
import_link8.default,
|
|
1305
|
+
{
|
|
1306
|
+
href: `/articles/authors/${author.slug}`,
|
|
1307
|
+
className: "font-medium text-white underline-offset-4 hover:underline",
|
|
1308
|
+
children: author.name
|
|
1309
|
+
}
|
|
1310
|
+
)
|
|
1311
|
+
] }, author.slug))
|
|
1312
|
+
] })
|
|
1313
|
+
] });
|
|
1314
|
+
})(),
|
|
1293
1315
|
showLegacyAuthor && /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: "0.5rem" }, children: [
|
|
1294
1316
|
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_lucide_react7.User, { className: "h-4 w-4" }),
|
|
1295
1317
|
/* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("span", { children: [
|