@binarynoir/vitepress-blog 0.3.0 → 0.5.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.
- package/README.md +162 -83
- package/dist/{chunk-LYTJBMVK.js → chunk-FPFH7ORO.js} +216 -75
- package/dist/chunk-FPFH7ORO.js.map +1 -0
- package/dist/client.cjs +228 -56
- package/dist/client.cjs.map +1 -1
- package/dist/client.d.cts +26 -14
- package/dist/client.d.ts +26 -14
- package/dist/client.js +227 -56
- package/dist/client.js.map +1 -1
- package/dist/index.cjs +213 -72
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/{plugin-Bb4rRwO8.d.cts → plugin-kzO68Abz.d.cts} +60 -22
- package/dist/{plugin-Bb4rRwO8.d.ts → plugin-kzO68Abz.d.ts} +60 -22
- package/dist/style.css +282 -26
- package/dist/vitepress.cjs +216 -75
- package/dist/vitepress.cjs.map +1 -1
- package/dist/vitepress.d.cts +2 -2
- package/dist/vitepress.d.ts +2 -2
- package/dist/vitepress.js +2 -2
- package/dist/vitepress.js.map +1 -1
- package/package.json +1 -1
- package/dist/chunk-LYTJBMVK.js.map +0 -1
package/dist/vitepress.cjs
CHANGED
|
@@ -64,6 +64,11 @@ function hasAnyTag(post, tags) {
|
|
|
64
64
|
const wanted = new Set(tags.map((tag) => tag.toLowerCase()));
|
|
65
65
|
return post.post.tags.some((tag) => wanted.has(tag.toLowerCase()));
|
|
66
66
|
}
|
|
67
|
+
function inAnyCategory(post, categories) {
|
|
68
|
+
if (categories.length === 0) return true;
|
|
69
|
+
const category = post.post.category.toLowerCase();
|
|
70
|
+
return !!category && categories.some((wanted) => wanted.toLowerCase() === category);
|
|
71
|
+
}
|
|
67
72
|
function blogCoversPost(index, post, sections) {
|
|
68
73
|
if (index.index.scope === "section") return isInFolders(post.relativePath, index.index.folders);
|
|
69
74
|
if (!isInFolders(post.relativePath, index.index.folders)) return false;
|
|
@@ -77,22 +82,22 @@ function compareDates(a, b) {
|
|
|
77
82
|
function compareByDate(a, b) {
|
|
78
83
|
return compareDates(a.post.date, b.post.date) || a.post.title.localeCompare(b.post.title);
|
|
79
84
|
}
|
|
80
|
-
function
|
|
81
|
-
const
|
|
82
|
-
const pinned = new Set(limit ?
|
|
85
|
+
function pinPosts(posts, limit) {
|
|
86
|
+
const candidates = posts.filter((post) => post.post.pinned);
|
|
87
|
+
const pinned = new Set(limit ? candidates.slice(0, limit) : candidates);
|
|
83
88
|
return { pinned: [...pinned], rest: posts.filter((post) => !pinned.has(post)) };
|
|
84
89
|
}
|
|
85
90
|
function listBlogPosts(pages, index, options) {
|
|
86
91
|
const sections = pages.filter(isIndex).filter((page) => page.index.scope === "section");
|
|
87
|
-
let posts = pages.filter(isPost).filter((post) => options.drafts || !post.post.draft).filter((post) => blogCoversPost(index, post, sections) && hasAnyTag(post, index.index.tags)).sort(compareByDate);
|
|
88
|
-
if (options.
|
|
89
|
-
const { pinned, rest } =
|
|
92
|
+
let posts = pages.filter(isPost).filter((post) => options.drafts || !post.post.draft).filter((post) => blogCoversPost(index, post, sections) && hasAnyTag(post, index.index.tags)).filter((post) => inAnyCategory(post, index.index.categories)).sort(compareByDate);
|
|
93
|
+
if (options.pinnedFirst) {
|
|
94
|
+
const { pinned, rest } = pinPosts(posts, index.index.pinLimit);
|
|
90
95
|
posts = [...pinned, ...rest];
|
|
91
96
|
}
|
|
92
97
|
return options.applyLimit && index.index.limit ? posts.slice(0, index.index.limit) : posts;
|
|
93
98
|
}
|
|
94
99
|
function findHomeBlog(pages, post, drafts) {
|
|
95
|
-
const listing = pages.filter(isIndex).filter((index) => listBlogPosts(pages, index, { drafts,
|
|
100
|
+
const listing = pages.filter(isIndex).filter((index) => listBlogPosts(pages, index, { drafts, pinnedFirst: false, applyLimit: false }).includes(post));
|
|
96
101
|
const sections = listing.filter((index) => index.index.scope === "section");
|
|
97
102
|
if (sections.length) return deepestFirst(sections, post.relativePath)[0];
|
|
98
103
|
return listing[0] ?? null;
|
|
@@ -111,7 +116,7 @@ function findBlogForPage(pages, relativePath) {
|
|
|
111
116
|
}
|
|
112
117
|
function findBlogsListing(pages, post) {
|
|
113
118
|
return pages.filter(isIndex).filter(
|
|
114
|
-
(index) => listBlogPosts(pages, index, { drafts: false,
|
|
119
|
+
(index) => listBlogPosts(pages, index, { drafts: false, pinnedFirst: false, applyLimit: false }).includes(post)
|
|
115
120
|
);
|
|
116
121
|
}
|
|
117
122
|
function listAnnouncements(pages, drafts) {
|
|
@@ -120,7 +125,7 @@ function listAnnouncements(pages, drafts) {
|
|
|
120
125
|
function findAnnouncementBlog(pages, announcements, drafts) {
|
|
121
126
|
if (announcements.length === 0) return null;
|
|
122
127
|
const listingAll = pages.filter(isIndex).filter((index) => {
|
|
123
|
-
const listed = listBlogPosts(pages, index, { drafts,
|
|
128
|
+
const listed = listBlogPosts(pages, index, { drafts, pinnedFirst: false, applyLimit: false });
|
|
124
129
|
return announcements.every((post) => listed.includes(post));
|
|
125
130
|
});
|
|
126
131
|
return listingAll.find((index) => index.index.scope === "site") ?? listingAll[0] ?? findHomeBlog(pages, announcements[0], drafts);
|
|
@@ -137,18 +142,28 @@ var DEFAULT_LABELS = {
|
|
|
137
142
|
older: "Older post",
|
|
138
143
|
allPosts: "All posts in {blog}",
|
|
139
144
|
draft: "Draft",
|
|
140
|
-
|
|
145
|
+
pinned: "Pinned",
|
|
141
146
|
announcement: "Announcement",
|
|
142
147
|
announcements: "Announcements",
|
|
143
|
-
noAnnouncements: "No announcements.",
|
|
144
148
|
announcementCount: "{n} announcements",
|
|
145
149
|
dismiss: "Dismiss",
|
|
146
150
|
noPosts: "No posts yet.",
|
|
147
151
|
undated: "Undated",
|
|
152
|
+
browse: "Browse posts",
|
|
153
|
+
categories: "Categories",
|
|
148
154
|
tags: "Keywords",
|
|
149
|
-
|
|
155
|
+
archive: "Archive",
|
|
156
|
+
category: "Category",
|
|
157
|
+
tag: "Keyword",
|
|
158
|
+
postCount: "{n} posts",
|
|
159
|
+
postCountOne: "{n} post",
|
|
160
|
+
noMatches: "No posts match these filters.",
|
|
161
|
+
removeFilter: "Remove filter: {filter}",
|
|
150
162
|
showAll: "Show all posts",
|
|
151
|
-
|
|
163
|
+
more: "+{n} more",
|
|
164
|
+
less: "Show fewer",
|
|
165
|
+
filter: "Filter {row}",
|
|
166
|
+
noFilterMatches: "Nothing matches.",
|
|
152
167
|
previous: "Previous",
|
|
153
168
|
next: "Next",
|
|
154
169
|
pages: "Pages",
|
|
@@ -319,6 +334,13 @@ function toDateTimeAttribute({ date, hasTime }, timeZone) {
|
|
|
319
334
|
function yearIn(date, timeZone) {
|
|
320
335
|
return Number(toZonedIsoDate(date, timeZone).slice(0, 4));
|
|
321
336
|
}
|
|
337
|
+
function monthIn(date, timeZone) {
|
|
338
|
+
return toZonedIsoDate(date, timeZone).slice(0, 7);
|
|
339
|
+
}
|
|
340
|
+
function formatMonth(month, locale, format) {
|
|
341
|
+
const [year, number] = month.split("-").map(Number);
|
|
342
|
+
return new Intl.DateTimeFormat(locale, { timeZone: "UTC", ...format }).format(Date.UTC(year, number - 1, 1, 12));
|
|
343
|
+
}
|
|
322
344
|
|
|
323
345
|
// src/frontmatter.ts
|
|
324
346
|
var POST_FRONTMATTER_KEY = "blog";
|
|
@@ -329,6 +351,8 @@ var FENCED_CODE = /^[ \t]*(`{3,}|~{3,})[^\n]*\n[\s\S]*?(?:^[ \t]*\1[`~]*[ \t]*$|
|
|
|
329
351
|
var HEADING_1 = /^ {0,3}#[ \t]+(.*?)(?:[ \t]+#+)?[ \t]*$/m;
|
|
330
352
|
var TAG_SYNTAX = /\(\(<?tag([|/])[^|/)\r\n]*(?:\1[^|/)\r\n]*){0,2}\)\)/g;
|
|
331
353
|
var MAX_SUMMARY_LENGTH = 200;
|
|
354
|
+
var DEFAULT_CATEGORY_LIMIT = 8;
|
|
355
|
+
var DEFAULT_TAG_LIMIT = 12;
|
|
332
356
|
function readFrontmatter(raw) {
|
|
333
357
|
const source = raw.replace(/^/, "");
|
|
334
358
|
const match = FRONTMATTER.exec(source);
|
|
@@ -442,7 +466,8 @@ function resolvePostData(data, content, fallbackTitle, options = {}) {
|
|
|
442
466
|
tags: toStringList(pick("tags")),
|
|
443
467
|
category: toText(pick("category")),
|
|
444
468
|
scope: toScope(post.scope),
|
|
445
|
-
|
|
469
|
+
// `sticky` is this setting's old name.
|
|
470
|
+
pinned: (post.pinned ?? post.sticky) === true,
|
|
446
471
|
draft: post.draft === true,
|
|
447
472
|
announcement: post.announcement === true || !!announcementText,
|
|
448
473
|
announcementText,
|
|
@@ -455,6 +480,9 @@ function resolvePostData(data, content, fallbackTitle, options = {}) {
|
|
|
455
480
|
function toLayout(value) {
|
|
456
481
|
return value === "grid" || value === "compact" ? value : "list";
|
|
457
482
|
}
|
|
483
|
+
function toGroupBy(value) {
|
|
484
|
+
return value === "year" || value === "month" ? value : "";
|
|
485
|
+
}
|
|
458
486
|
function defaultFeedPath(relativePath) {
|
|
459
487
|
const name = import_node_path.default.posix.basename(relativePath, ".md");
|
|
460
488
|
return name === "index" ? "feed.xml" : `${name}-feed.xml`;
|
|
@@ -488,13 +516,20 @@ function resolveIndexConfig(data, relativePath, defaults = {}) {
|
|
|
488
516
|
scope,
|
|
489
517
|
folders: folders.length ? resolveFolders(folders, relativePath) : scope === "section" ? [ownFolder] : [],
|
|
490
518
|
tags: toStringList(options.tags),
|
|
519
|
+
categories: toStringList(options.categories),
|
|
491
520
|
limit: typeof options.limit === "number" && options.limit > 0 ? Math.floor(options.limit) : 0,
|
|
492
521
|
layout: toLayout(options.layout),
|
|
493
|
-
|
|
522
|
+
groupBy: toGroupBy(options.groupBy),
|
|
494
523
|
showTags: options.showTags !== false,
|
|
524
|
+
showCategory: options.showCategory !== false,
|
|
525
|
+
categoryList: options.categoryList !== false,
|
|
526
|
+
categoryLimit: toCount(options.categoryLimit, DEFAULT_CATEGORY_LIMIT),
|
|
495
527
|
tagList: options.tagList !== false,
|
|
528
|
+
tagLimit: toCount(options.tagLimit, DEFAULT_TAG_LIMIT),
|
|
529
|
+
tagMinCount: toCount(options.tagMinCount, 1),
|
|
530
|
+
archive: options.archive !== false,
|
|
496
531
|
pageSize: toCount(options.pageSize, toCount(defaults.pageSize, 10)),
|
|
497
|
-
|
|
532
|
+
pinLimit: toCount(options.pinLimit ?? options.stickyLimit, toCount(defaults.pinLimit ?? defaults.stickyLimit, 0)),
|
|
498
533
|
rssLink: typeof options.rssLink === "boolean" ? options.rssLink : defaults.rssLink ?? true,
|
|
499
534
|
feed: toFeed(options.feed, relativePath)
|
|
500
535
|
};
|
|
@@ -622,7 +657,8 @@ function renderRssLink(index, context, show = index.index.rssLink) {
|
|
|
622
657
|
if (!show || !context.feeds || !index.index.feed) return "";
|
|
623
658
|
const title = fill(context.labels.rssFeed, { blog: index.title }).replace(/"/g, """);
|
|
624
659
|
const url = toMarkdownUrl(`/${toFeedPath(index)}`);
|
|
625
|
-
|
|
660
|
+
const text = `<span class="vp-blog-rss__text">${escapeMarkdown(context.labels.subscribe)}</span>`;
|
|
661
|
+
return `<span class="vp-blog-rss">[${RSS_ICON} ${text}](${url} "${title}")</span>`;
|
|
626
662
|
}
|
|
627
663
|
function fill(template, values) {
|
|
628
664
|
return template.replace(/\{(\w+)\}/g, (match, key) => values[key] ?? match);
|
|
@@ -652,27 +688,33 @@ function renderAuthors(authors, context) {
|
|
|
652
688
|
function renderBadge(kind, context) {
|
|
653
689
|
return `<span class="vp-blog-badge vp-blog-badge--${kind}">${escapeTemplateText(context.labels[kind])}</span>`;
|
|
654
690
|
}
|
|
655
|
-
function
|
|
656
|
-
const
|
|
691
|
+
function toFilterUrl(blogRoutePath, param, value) {
|
|
692
|
+
const encoded = encodeURIComponent(value.toLowerCase()).replace(/[()]/g, (c) => c === "(" ? "%28" : "%29");
|
|
657
693
|
const page = blogRoutePath.replace(/(^|\/)index\.md$/, "$1");
|
|
658
|
-
return `${toMarkdownUrl(`/${page}`)}
|
|
694
|
+
return `${toMarkdownUrl(`/${page}`)}?${param}=${encoded}`;
|
|
695
|
+
}
|
|
696
|
+
function renderFilterLink(text, param, blogRoutePath, className) {
|
|
697
|
+
return blogRoutePath ? `[${escapeMarkdown(text)}](${toFilterUrl(blogRoutePath, param, text)})` : `<span class="${className}">${escapeTemplateText(text)}</span>`;
|
|
659
698
|
}
|
|
660
699
|
function renderTags(tags, blogRoutePath) {
|
|
661
|
-
return tags.map(
|
|
662
|
-
(tag) => blogRoutePath ? `[${escapeMarkdown(tag)}](${toTagUrl(blogRoutePath, tag)})` : `<span class="vp-blog-tag">${escapeTemplateText(tag)}</span>`
|
|
663
|
-
).join(" ");
|
|
700
|
+
return tags.map((tag) => renderFilterLink(tag, "tag", blogRoutePath, "vp-blog-tag")).join(" ");
|
|
664
701
|
}
|
|
665
702
|
function toTagsAttribute(post) {
|
|
666
703
|
return escapeHtml(post.post.tags.map((tag) => tag.toLowerCase()).join("|"));
|
|
667
704
|
}
|
|
668
|
-
function toItemAttributes(post) {
|
|
669
|
-
|
|
705
|
+
function toItemAttributes(post, context) {
|
|
706
|
+
const { category, date, announcement } = post.post;
|
|
707
|
+
const attributes = [`data-vp-blog-item data-tags="${toTagsAttribute(post)}"`];
|
|
708
|
+
if (category) attributes.push(`data-category="${escapeHtml(category.toLowerCase())}"`);
|
|
709
|
+
if (date) attributes.push(`data-month="${monthIn(date, context.dates.timeZone)}"`);
|
|
710
|
+
if (announcement) attributes.push("data-announcement");
|
|
711
|
+
return attributes.join(" ");
|
|
670
712
|
}
|
|
671
713
|
function renderBadges(post, context) {
|
|
672
|
-
const { draft,
|
|
714
|
+
const { draft, pinned, announcement } = post.post;
|
|
673
715
|
return [
|
|
674
716
|
draft && renderBadge("draft", context),
|
|
675
|
-
|
|
717
|
+
pinned && renderBadge("pinned", context),
|
|
676
718
|
announcement && renderBadge("announcement", context)
|
|
677
719
|
].filter(Boolean).join(" ");
|
|
678
720
|
}
|
|
@@ -707,7 +749,7 @@ function renderCard(post, context, index) {
|
|
|
707
749
|
const link = `/${post.routePath}`;
|
|
708
750
|
const image = toImageSource(post, context.pageDirectory);
|
|
709
751
|
const modifiers = [
|
|
710
|
-
post.post.
|
|
752
|
+
post.post.pinned && "pinned",
|
|
711
753
|
post.post.draft && "draft",
|
|
712
754
|
post.post.announcement && "announcement",
|
|
713
755
|
image && "with-image",
|
|
@@ -715,9 +757,10 @@ function renderCard(post, context, index) {
|
|
|
715
757
|
].filter(Boolean).map((modifier) => ` vp-blog-card--${modifier}`).join("");
|
|
716
758
|
const alt = escapeMarkdown(post.post.imageAlt);
|
|
717
759
|
return [
|
|
718
|
-
`<article class="vp-blog-card${modifiers}"${imageStyle(post)} ${toItemAttributes(post)}>`,
|
|
760
|
+
`<article class="vp-blog-card${modifiers}"${imageStyle(post)} ${toItemAttributes(post, context)}>`,
|
|
719
761
|
...image ? block("vp-blog-card__image", `[})](${toMarkdownUrl(link)})`) : [],
|
|
720
762
|
'<div class="vp-blog-card__body">',
|
|
763
|
+
...index.index.showCategory && post.post.category ? block("vp-blog-card__category", renderFilterLink(post.post.category, "category", index.routePath, "")) : [],
|
|
721
764
|
...block("vp-blog-card__title", toPageLink(post.post.title, post.routePath)),
|
|
722
765
|
...block("vp-blog-card__meta", renderMeta(post, context, { authors: true, updated: false, badges: true })),
|
|
723
766
|
...block("vp-blog-card__summary", escapeMarkdown(post.post.summary)),
|
|
@@ -729,7 +772,7 @@ function renderCard(post, context, index) {
|
|
|
729
772
|
function renderCompactItem(post, context) {
|
|
730
773
|
const date = post.post.date ? `${renderTime(post.post.date, post.post.dateHasTime, context)} ` : "";
|
|
731
774
|
const badges = renderBadges(post, context);
|
|
732
|
-
const marker = `<span hidden ${toItemAttributes(post)}></span>`;
|
|
775
|
+
const marker = `<span hidden ${toItemAttributes(post, context)}></span>`;
|
|
733
776
|
return `- ${marker}${date}${toPageLink(post.post.title, post.routePath)}${badges ? ` ${badges}` : ""}`;
|
|
734
777
|
}
|
|
735
778
|
function renderGroup(posts, index, context) {
|
|
@@ -749,18 +792,23 @@ function renderGroup(posts, index, context) {
|
|
|
749
792
|
"</div>"
|
|
750
793
|
];
|
|
751
794
|
}
|
|
752
|
-
function
|
|
753
|
-
const
|
|
795
|
+
function renderGroups(posts, index, context) {
|
|
796
|
+
const { groupBy } = index.index;
|
|
797
|
+
if (!groupBy) return renderGroup(posts, index, context);
|
|
798
|
+
const { timeZone, locale } = context.dates;
|
|
799
|
+
const groups = /* @__PURE__ */ new Map();
|
|
754
800
|
for (const post of posts) {
|
|
755
|
-
const
|
|
756
|
-
|
|
801
|
+
const { date } = post.post;
|
|
802
|
+
const key = !date ? "" : groupBy === "year" ? String(yearIn(date, timeZone)) : monthIn(date, timeZone);
|
|
803
|
+
groups.set(key, [...groups.get(key) ?? [], post]);
|
|
757
804
|
}
|
|
758
805
|
const lines = [];
|
|
759
|
-
for (const [
|
|
806
|
+
for (const [key, group] of groups) {
|
|
807
|
+
const heading = !key ? context.labels.undated : groupBy === "year" ? key : formatMonth(key, locale, { month: "long", year: "numeric" });
|
|
760
808
|
lines.push(
|
|
761
|
-
|
|
809
|
+
`<section class="vp-blog-group vp-blog-${groupBy}">`,
|
|
762
810
|
"",
|
|
763
|
-
`## ${escapeMarkdown(
|
|
811
|
+
`## ${escapeMarkdown(heading)}`,
|
|
764
812
|
"",
|
|
765
813
|
...renderGroup(group, index, context),
|
|
766
814
|
"",
|
|
@@ -769,29 +817,107 @@ function renderYears(posts, index, context) {
|
|
|
769
817
|
}
|
|
770
818
|
return lines;
|
|
771
819
|
}
|
|
772
|
-
function
|
|
773
|
-
const
|
|
820
|
+
function collectValues(posts, pick) {
|
|
821
|
+
const values = /* @__PURE__ */ new Map();
|
|
774
822
|
for (const post of posts) {
|
|
775
|
-
for (const
|
|
776
|
-
const entry =
|
|
823
|
+
for (const value of new Set(pick(post))) {
|
|
824
|
+
const entry = values.get(value.toLowerCase()) ?? { value, count: 0 };
|
|
777
825
|
entry.count++;
|
|
778
|
-
|
|
826
|
+
values.set(value.toLowerCase(), entry);
|
|
779
827
|
}
|
|
780
828
|
}
|
|
781
|
-
return [...
|
|
829
|
+
return [...values.values()].sort((a, b) => a.value.localeCompare(b.value, void 0, { sensitivity: "base" }));
|
|
782
830
|
}
|
|
783
|
-
function
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
831
|
+
function collectTags(posts) {
|
|
832
|
+
return collectValues(posts, (post) => post.post.tags).map(({ value, count }) => ({ tag: value, count }));
|
|
833
|
+
}
|
|
834
|
+
function collectCategories(posts) {
|
|
835
|
+
return collectValues(posts, (post) => post.post.category ? [post.post.category] : []);
|
|
836
|
+
}
|
|
837
|
+
function collectArchive(posts, timeZone) {
|
|
838
|
+
const years = /* @__PURE__ */ new Map();
|
|
839
|
+
for (const post of posts) {
|
|
840
|
+
if (!post.post.date) continue;
|
|
841
|
+
const month = monthIn(post.post.date, timeZone);
|
|
842
|
+
const year = years.get(month.slice(0, 4)) ?? { value: month.slice(0, 4), count: 0, months: [] };
|
|
843
|
+
year.count++;
|
|
844
|
+
const entry = year.months.find((candidate) => candidate.value === month);
|
|
845
|
+
if (entry) entry.count++;
|
|
846
|
+
else year.months.push({ value: month, count: 1 });
|
|
847
|
+
years.set(year.value, year);
|
|
848
|
+
}
|
|
849
|
+
const newestFirst = (a, b) => b.value.localeCompare(a.value);
|
|
850
|
+
return [...years.values()].sort(newestFirst).map((year) => ({ ...year, months: [...year.months].sort(newestFirst) }));
|
|
851
|
+
}
|
|
852
|
+
function renderChip(name, count, url, title = "") {
|
|
853
|
+
const tooltip = title ? ` "${title.replace(/"/g, """)}"` : "";
|
|
854
|
+
return `[<span class="vp-blog-facet__name">${escapeMarkdown(name)}</span> <span class="vp-blog-facet__count">${count}</span>](${url}${tooltip})`;
|
|
855
|
+
}
|
|
856
|
+
function renderFacet(row) {
|
|
857
|
+
const { facet, label, chips, ariaLabel = label, limit = 0, attributes = "", aside = "" } = row;
|
|
858
|
+
const limited = limit && chips.length > limit ? ` data-limit="${limit}"` : "";
|
|
790
859
|
return [
|
|
791
|
-
`<
|
|
860
|
+
`<div class="vp-blog-facet vp-blog-facet--${facet}" role="group" aria-label="${escapeHtml(ariaLabel)}" data-vp-blog-facet="${facet}"${limited}${attributes}>`,
|
|
861
|
+
`<span class="vp-blog-facet__label" aria-hidden="true">${escapeTemplateText(label)}</span>`,
|
|
862
|
+
'<div class="vp-blog-facet__chips">',
|
|
792
863
|
"",
|
|
793
|
-
|
|
864
|
+
chips.join(" "),
|
|
794
865
|
"",
|
|
866
|
+
"</div>",
|
|
867
|
+
...block("vp-blog-facet__aside", aside),
|
|
868
|
+
"</div>"
|
|
869
|
+
];
|
|
870
|
+
}
|
|
871
|
+
function canNarrow(values, total) {
|
|
872
|
+
return values.length > 1 || values.length === 1 && values[0].count < total;
|
|
873
|
+
}
|
|
874
|
+
function byUse(a, b) {
|
|
875
|
+
return b.count - a.count || a.value.localeCompare(b.value, void 0, { sensitivity: "base" });
|
|
876
|
+
}
|
|
877
|
+
function renderBrowse(posts, index, context) {
|
|
878
|
+
const { labels } = context;
|
|
879
|
+
const { locale, timeZone } = context.dates;
|
|
880
|
+
const { categoryList, categoryLimit, tagList, tagLimit, tagMinCount, archive: showArchive } = index.index;
|
|
881
|
+
const blog = index.routePath;
|
|
882
|
+
const rows = [];
|
|
883
|
+
const categories = categoryList ? collectCategories(posts).sort(byUse) : [];
|
|
884
|
+
if (canNarrow(categories, posts.length)) {
|
|
885
|
+
const chips = categories.map(({ value, count }) => renderChip(value, count, toFilterUrl(blog, "category", value)));
|
|
886
|
+
rows.push({ facet: "category", label: labels.categories, chips, limit: categoryLimit });
|
|
887
|
+
}
|
|
888
|
+
const tags = tagList ? collectTags(posts).map(({ tag, count }) => ({ value: tag, count })).filter(({ count }) => count >= tagMinCount).sort(byUse) : [];
|
|
889
|
+
if (canNarrow(tags, posts.length)) {
|
|
890
|
+
const chips = tags.map(({ value, count }) => renderChip(value, count, toFilterUrl(blog, "tag", value)));
|
|
891
|
+
rows.push({ facet: "tag", label: labels.tags, chips, limit: tagLimit });
|
|
892
|
+
}
|
|
893
|
+
const archive = showArchive ? collectArchive(posts, timeZone) : [];
|
|
894
|
+
const monthChips = (months) => months.map(
|
|
895
|
+
({ value, count }) => renderChip(
|
|
896
|
+
formatMonth(value, locale, { month: "short" }),
|
|
897
|
+
count,
|
|
898
|
+
toFilterUrl(blog, "month", value),
|
|
899
|
+
formatMonth(value, locale, { month: "long", year: "numeric" })
|
|
900
|
+
)
|
|
901
|
+
);
|
|
902
|
+
if (archive.length === 1 && canNarrow(archive[0].months, posts.length)) {
|
|
903
|
+
const [year] = archive;
|
|
904
|
+
const ariaLabel = `${labels.archive} ${year.value}`;
|
|
905
|
+
const attributes = ` data-year="${year.value}"`;
|
|
906
|
+
rows.push({ facet: "month", label: labels.archive, chips: monthChips(year.months), ariaLabel, attributes });
|
|
907
|
+
} else if (archive.length > 1) {
|
|
908
|
+
const years = archive.map(({ value, count }) => renderChip(value, count, toFilterUrl(blog, "year", value)));
|
|
909
|
+
rows.push({ facet: "year", label: labels.archive, chips: years });
|
|
910
|
+
for (const year of archive) {
|
|
911
|
+
const ariaLabel = `${labels.archive} ${year.value}`;
|
|
912
|
+
const attributes = ` data-year="${year.value}" hidden`;
|
|
913
|
+
rows.push({ facet: "month", label: year.value, chips: monthChips(year.months), ariaLabel, attributes });
|
|
914
|
+
}
|
|
915
|
+
}
|
|
916
|
+
if (rows.length === 0) return [];
|
|
917
|
+
rows[0].aside = renderRssLink(index, context);
|
|
918
|
+
return [
|
|
919
|
+
`<nav class="vp-blog-browse" aria-label="${escapeHtml(labels.browse)}">`,
|
|
920
|
+
...rows.flatMap(renderFacet),
|
|
795
921
|
"</nav>"
|
|
796
922
|
];
|
|
797
923
|
}
|
|
@@ -799,14 +925,23 @@ function renderClientAttributes(index, context) {
|
|
|
799
925
|
const { labels } = context;
|
|
800
926
|
const attributes = {
|
|
801
927
|
"data-page-size": String(index.index.pageSize),
|
|
802
|
-
"
|
|
928
|
+
// For writing a `?month=` filter's name: "October 2026".
|
|
929
|
+
"data-locale": context.locale,
|
|
803
930
|
"data-label-show-all": labels.showAll,
|
|
804
|
-
"data-label-no-tagged": labels.noTagged,
|
|
805
931
|
"data-label-previous": labels.previous,
|
|
806
932
|
"data-label-next": labels.next,
|
|
807
933
|
"data-label-pages": labels.pages,
|
|
808
934
|
"data-label-announcements": labels.announcements,
|
|
809
|
-
"data-label-
|
|
935
|
+
"data-label-category": labels.category,
|
|
936
|
+
"data-label-tag": labels.tag,
|
|
937
|
+
"data-label-post-count": labels.postCount,
|
|
938
|
+
"data-label-post-count-one": labels.postCountOne,
|
|
939
|
+
"data-label-no-matches": labels.noMatches,
|
|
940
|
+
"data-label-remove-filter": labels.removeFilter,
|
|
941
|
+
"data-label-more": labels.more,
|
|
942
|
+
"data-label-less": labels.less,
|
|
943
|
+
"data-label-filter": labels.filter,
|
|
944
|
+
"data-label-no-filter-matches": labels.noFilterMatches
|
|
810
945
|
};
|
|
811
946
|
return Object.entries(attributes).map(([name, value]) => `${name}="${escapeHtml(value)}"`).join(" ");
|
|
812
947
|
}
|
|
@@ -814,20 +949,19 @@ function renderPostList(pinned, rest, index, context) {
|
|
|
814
949
|
const all = [...pinned, ...rest];
|
|
815
950
|
if (all.length === 0) return `<p class="vp-blog-empty">${escapeTemplateText(context.labels.noPosts)}</p>
|
|
816
951
|
`;
|
|
952
|
+
const browse = renderBrowse(all, index, context);
|
|
817
953
|
const lines = [
|
|
818
954
|
`<div class="vp-blog" data-vp-blog ${renderClientAttributes(index, context)}>`,
|
|
819
|
-
...block("vp-blog-subscribe", renderRssLink(index, context)),
|
|
820
|
-
|
|
821
|
-
'<div class="vp-blog-filter" data-vp-blog-filter hidden></div>'
|
|
955
|
+
...browse.length ? browse : block("vp-blog-subscribe", renderRssLink(index, context)),
|
|
956
|
+
'<div class="vp-blog-filter" data-vp-blog-filter role="status" hidden></div>'
|
|
822
957
|
];
|
|
823
958
|
if (pinned.length)
|
|
824
959
|
lines.push(
|
|
825
|
-
'<section class="vp-blog-
|
|
960
|
+
'<section class="vp-blog-pinned" data-vp-blog-pinned>',
|
|
826
961
|
...renderGroup(pinned, index, context),
|
|
827
962
|
"</section>"
|
|
828
963
|
);
|
|
829
|
-
if (rest.length)
|
|
830
|
-
lines.push(...index.index.groupByYear ? renderYears(rest, index, context) : renderGroup(rest, index, context));
|
|
964
|
+
if (rest.length) lines.push(...renderGroups(rest, index, context));
|
|
831
965
|
lines.push(`<nav class="vp-blog-pagination" data-vp-blog-pagination hidden></nav>`, "</div>");
|
|
832
966
|
return `${lines.join("\n")}
|
|
833
967
|
`;
|
|
@@ -836,8 +970,12 @@ function renderPostHeader(post, context, show, blogRoutePath = null) {
|
|
|
836
970
|
const lines = [];
|
|
837
971
|
if (show.byline) {
|
|
838
972
|
const meta = renderMeta(post, context, { authors: true, updated: true, badges: true });
|
|
839
|
-
const
|
|
840
|
-
|
|
973
|
+
const { category, tags } = post.post;
|
|
974
|
+
const chips = [
|
|
975
|
+
category && `<span class="vp-blog-byline__category">${renderFilterLink(category, "category", blogRoutePath, "vp-blog-category")}</span>`,
|
|
976
|
+
tags.length && `<span class="vp-blog-byline__tags">${renderTags(tags, blogRoutePath)}</span>`
|
|
977
|
+
].filter(Boolean).join(" ");
|
|
978
|
+
lines.push(...block("vp-blog-byline", [meta, chips].filter(Boolean).join("\n\n")));
|
|
841
979
|
}
|
|
842
980
|
if (show.cover && post.post.image) {
|
|
843
981
|
const image = /^[./@~]/.test(post.post.image) || isExternalUrl(post.post.image) ? post.post.image : `./${post.post.image}`;
|
|
@@ -928,7 +1066,7 @@ function toOptionsKey(options) {
|
|
|
928
1066
|
options.wordsPerMinute,
|
|
929
1067
|
options.stripTags,
|
|
930
1068
|
options.pageSize,
|
|
931
|
-
options.stickyLimit,
|
|
1069
|
+
options.pinLimit ?? options.stickyLimit,
|
|
932
1070
|
options.rssLink,
|
|
933
1071
|
options.timeZone
|
|
934
1072
|
]);
|
|
@@ -1017,10 +1155,13 @@ function resolveWidgetConfig(body) {
|
|
|
1017
1155
|
config: {
|
|
1018
1156
|
blog: typeof data.blog === "string" ? data.blog.trim() : "",
|
|
1019
1157
|
limit: toCount2(data.limit) ?? DEFAULT_LIMIT,
|
|
1020
|
-
stickyLimit
|
|
1158
|
+
// `stickyLimit` is this setting's old name.
|
|
1159
|
+
pinLimit: toCount2(data.pinLimit ?? data.stickyLimit),
|
|
1021
1160
|
layout,
|
|
1022
1161
|
showTags: typeof data.showTags === "boolean" ? data.showTags : void 0,
|
|
1162
|
+
showCategory: typeof data.showCategory === "boolean" ? data.showCategory : void 0,
|
|
1023
1163
|
tags: toStringList(data.tags),
|
|
1164
|
+
categories: toStringList(data.categories),
|
|
1024
1165
|
more: data.more === false ? null : typeof data.more === "string" && data.more.trim() ? data.more.trim() : "",
|
|
1025
1166
|
rssLink: typeof data.rssLink === "boolean" ? data.rssLink : void 0
|
|
1026
1167
|
},
|
|
@@ -1036,19 +1177,19 @@ function renderWidget(body, environment) {
|
|
|
1036
1177
|
warn(config.blog ? `no blog page found at "${config.blog}"` : 'no blog covers this page: name one with "blog:"');
|
|
1037
1178
|
return "";
|
|
1038
1179
|
}
|
|
1039
|
-
const
|
|
1040
|
-
const
|
|
1041
|
-
|
|
1042
|
-
(post) => wanted.size === 0 || post.post.tags.some((tag) => wanted.has(tag.toLowerCase()))
|
|
1180
|
+
const pinLimit = config.pinLimit ?? blog.index.pinLimit;
|
|
1181
|
+
const posts = listBlogPosts(pages, blog, { drafts, pinnedFirst: false, applyLimit: false }).filter(
|
|
1182
|
+
(post) => hasAnyTag(post, config.tags) && inAnyCategory(post, config.categories)
|
|
1043
1183
|
);
|
|
1044
|
-
const { pinned, rest } =
|
|
1184
|
+
const { pinned, rest } = pinPosts(posts, pinLimit);
|
|
1045
1185
|
const shown = [...pinned, ...rest].slice(0, config.limit || void 0);
|
|
1046
1186
|
const view = {
|
|
1047
1187
|
...blog,
|
|
1048
1188
|
index: {
|
|
1049
1189
|
...blog.index,
|
|
1050
1190
|
layout: config.layout ?? blog.index.layout,
|
|
1051
|
-
showTags: config.showTags ?? blog.index.showTags
|
|
1191
|
+
showTags: config.showTags ?? blog.index.showTags,
|
|
1192
|
+
showCategory: config.showCategory ?? blog.index.showCategory
|
|
1052
1193
|
}
|
|
1053
1194
|
};
|
|
1054
1195
|
const lines = [`<div class="vp-blog-widget">`];
|
|
@@ -1121,8 +1262,8 @@ function hasFeeds(options) {
|
|
|
1121
1262
|
}
|
|
1122
1263
|
function withPostList(source, page, pages, options) {
|
|
1123
1264
|
if (!isIndex(page)) return source;
|
|
1124
|
-
const posts = listBlogPosts(pages, page, { drafts: options.drafts ?? false,
|
|
1125
|
-
const { pinned, rest } =
|
|
1265
|
+
const posts = listBlogPosts(pages, page, { drafts: options.drafts ?? false, pinnedFirst: true, applyLimit: true });
|
|
1266
|
+
const { pinned, rest } = pinPosts(posts, page.index.pinLimit);
|
|
1126
1267
|
const context = createRenderContext(options, import_node_path6.default.dirname(page.absolutePath), hasFeeds(options));
|
|
1127
1268
|
const generated = renderPostList(pinned, rest, page, context);
|
|
1128
1269
|
return source.includes(BLOG_POSTS_PLACEHOLDER) ? source.replace(BLOG_POSTS_PLACEHOLDER, () => `
|
|
@@ -1145,7 +1286,7 @@ function withPostDecorations(source, page, pages, options) {
|
|
|
1145
1286
|
let result = header ? insertAfterTitle(source, header) : source;
|
|
1146
1287
|
if (page.post.postNav ?? options.postNav ?? true) {
|
|
1147
1288
|
if (blog) {
|
|
1148
|
-
const posts = listBlogPosts(pages, blog, { drafts,
|
|
1289
|
+
const posts = listBlogPosts(pages, blog, { drafts, pinnedFirst: false, applyLimit: false });
|
|
1149
1290
|
result = `${result.replace(/\s*$/, "")}
|
|
1150
1291
|
|
|
1151
1292
|
${renderPostNav(page, posts, blog, context)}
|
|
@@ -1222,7 +1363,7 @@ function attachAnnouncements(siteConfig, options = {}) {
|
|
|
1222
1363
|
}
|
|
1223
1364
|
function listFeedPosts(pages, index, options) {
|
|
1224
1365
|
const limit = index.index.feed?.limit ?? options.feedLimit ?? DEFAULT_FEED_LIMIT;
|
|
1225
|
-
return listBlogPosts(pages, index, { drafts: false,
|
|
1366
|
+
return listBlogPosts(pages, index, { drafts: false, pinnedFirst: false, applyLimit: false }).slice(0, limit);
|
|
1226
1367
|
}
|
|
1227
1368
|
function renderIndexFeed(pages, index, site, siteConfig, options) {
|
|
1228
1369
|
return renderFeed({
|