@binarynoir/vitepress-blog 0.2.0 → 0.4.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 +238 -88
- package/dist/chunk-42Z6H7IN.js +52 -0
- package/dist/chunk-42Z6H7IN.js.map +1 -0
- package/dist/{chunk-PWNWSYI4.js → chunk-ZM5FOUT4.js} +229 -106
- package/dist/chunk-ZM5FOUT4.js.map +1 -0
- package/dist/client.cjs +280 -64
- package/dist/client.cjs.map +1 -1
- package/dist/client.d.cts +56 -13
- package/dist/client.d.ts +56 -13
- package/dist/client.js +275 -62
- package/dist/client.js.map +1 -1
- package/dist/index.cjs +205 -70
- 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 +2 -1
- package/dist/{plugin-BlfpqyX4.d.cts → plugin-DUTUA_t4.d.cts} +73 -19
- package/dist/{plugin-BlfpqyX4.d.ts → plugin-DUTUA_t4.d.ts} +73 -19
- package/dist/style.css +270 -25
- package/dist/vitepress.cjs +341 -138
- package/dist/vitepress.cjs.map +1 -1
- package/dist/vitepress.d.cts +17 -7
- package/dist/vitepress.d.ts +17 -7
- package/dist/vitepress.js +76 -8
- package/dist/vitepress.js.map +1 -1
- package/package.json +1 -1
- package/dist/chunk-PWNWSYI4.js.map +0 -1
|
@@ -1,3 +1,14 @@
|
|
|
1
|
+
import {
|
|
2
|
+
escapeHtml,
|
|
3
|
+
escapeMarkdown,
|
|
4
|
+
escapeTemplateText,
|
|
5
|
+
isExternalUrl,
|
|
6
|
+
joinUrl,
|
|
7
|
+
toMarkdownUrl,
|
|
8
|
+
toPageLink,
|
|
9
|
+
toUrlPath
|
|
10
|
+
} from "./chunk-42Z6H7IN.js";
|
|
11
|
+
|
|
1
12
|
// src/collect.ts
|
|
2
13
|
function isPost(page) {
|
|
3
14
|
return page.post !== null;
|
|
@@ -19,6 +30,11 @@ function hasAnyTag(post, tags) {
|
|
|
19
30
|
const wanted = new Set(tags.map((tag) => tag.toLowerCase()));
|
|
20
31
|
return post.post.tags.some((tag) => wanted.has(tag.toLowerCase()));
|
|
21
32
|
}
|
|
33
|
+
function inAnyCategory(post, categories) {
|
|
34
|
+
if (categories.length === 0) return true;
|
|
35
|
+
const category = post.post.category.toLowerCase();
|
|
36
|
+
return !!category && categories.some((wanted) => wanted.toLowerCase() === category);
|
|
37
|
+
}
|
|
22
38
|
function blogCoversPost(index, post, sections) {
|
|
23
39
|
if (index.index.scope === "section") return isInFolders(post.relativePath, index.index.folders);
|
|
24
40
|
if (!isInFolders(post.relativePath, index.index.folders)) return false;
|
|
@@ -32,22 +48,22 @@ function compareDates(a, b) {
|
|
|
32
48
|
function compareByDate(a, b) {
|
|
33
49
|
return compareDates(a.post.date, b.post.date) || a.post.title.localeCompare(b.post.title);
|
|
34
50
|
}
|
|
35
|
-
function
|
|
36
|
-
const
|
|
37
|
-
const pinned = new Set(limit ?
|
|
51
|
+
function pinPosts(posts, limit) {
|
|
52
|
+
const candidates = posts.filter((post) => post.post.pinned);
|
|
53
|
+
const pinned = new Set(limit ? candidates.slice(0, limit) : candidates);
|
|
38
54
|
return { pinned: [...pinned], rest: posts.filter((post) => !pinned.has(post)) };
|
|
39
55
|
}
|
|
40
56
|
function listBlogPosts(pages, index, options) {
|
|
41
57
|
const sections = pages.filter(isIndex).filter((page) => page.index.scope === "section");
|
|
42
|
-
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);
|
|
43
|
-
if (options.
|
|
44
|
-
const { pinned, rest } =
|
|
58
|
+
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);
|
|
59
|
+
if (options.pinnedFirst) {
|
|
60
|
+
const { pinned, rest } = pinPosts(posts, index.index.pinLimit);
|
|
45
61
|
posts = [...pinned, ...rest];
|
|
46
62
|
}
|
|
47
63
|
return options.applyLimit && index.index.limit ? posts.slice(0, index.index.limit) : posts;
|
|
48
64
|
}
|
|
49
65
|
function findHomeBlog(pages, post, drafts) {
|
|
50
|
-
const listing = pages.filter(isIndex).filter((index) => listBlogPosts(pages, index, { drafts,
|
|
66
|
+
const listing = pages.filter(isIndex).filter((index) => listBlogPosts(pages, index, { drafts, pinnedFirst: false, applyLimit: false }).includes(post));
|
|
51
67
|
const sections = listing.filter((index) => index.index.scope === "section");
|
|
52
68
|
if (sections.length) return deepestFirst(sections, post.relativePath)[0];
|
|
53
69
|
return listing[0] ?? null;
|
|
@@ -66,43 +82,19 @@ function findBlogForPage(pages, relativePath) {
|
|
|
66
82
|
}
|
|
67
83
|
function findBlogsListing(pages, post) {
|
|
68
84
|
return pages.filter(isIndex).filter(
|
|
69
|
-
(index) => listBlogPosts(pages, index, { drafts: false,
|
|
85
|
+
(index) => listBlogPosts(pages, index, { drafts: false, pinnedFirst: false, applyLimit: false }).includes(post)
|
|
70
86
|
);
|
|
71
87
|
}
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
var HTML_ESCAPES = {
|
|
75
|
-
"&": "&",
|
|
76
|
-
"<": "<",
|
|
77
|
-
">": ">",
|
|
78
|
-
'"': """,
|
|
79
|
-
"'": "'"
|
|
80
|
-
};
|
|
81
|
-
function escapeHtml(value) {
|
|
82
|
-
return value.replace(/[&<>"']/g, (character) => HTML_ESCAPES[character]);
|
|
83
|
-
}
|
|
84
|
-
function escapeMarkdown(text) {
|
|
85
|
-
const escaped = text.replace(/[\\`*_[\]#|~]/g, "\\$&").replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
86
|
-
return /[{}]/.test(escaped) ? `<span v-pre>${escaped}</span>` : escaped;
|
|
87
|
-
}
|
|
88
|
-
function escapeTemplateText(text) {
|
|
89
|
-
const escaped = escapeHtml(text);
|
|
90
|
-
return /[{}]/.test(escaped) ? `<span v-pre>${escaped}</span>` : escaped;
|
|
91
|
-
}
|
|
92
|
-
function isExternalUrl(url) {
|
|
93
|
-
return /^(?:[a-z][a-z\d+.-]*:|\/\/)/i.test(url);
|
|
94
|
-
}
|
|
95
|
-
function toMarkdownUrl(url) {
|
|
96
|
-
return encodeURI(url).replace(/[()]/g, (character) => character === "(" ? "%28" : "%29");
|
|
97
|
-
}
|
|
98
|
-
function toPageLink(text, routePath) {
|
|
99
|
-
return `[${escapeMarkdown(text)}](${toMarkdownUrl(`/${routePath}`)})`;
|
|
88
|
+
function listAnnouncements(pages, drafts) {
|
|
89
|
+
return pages.filter(isPost).filter((post) => post.post.announcement && (drafts || !post.post.draft)).sort(compareByDate);
|
|
100
90
|
}
|
|
101
|
-
function
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
91
|
+
function findAnnouncementBlog(pages, announcements, drafts) {
|
|
92
|
+
if (announcements.length === 0) return null;
|
|
93
|
+
const listingAll = pages.filter(isIndex).filter((index) => {
|
|
94
|
+
const listed = listBlogPosts(pages, index, { drafts, pinnedFirst: false, applyLimit: false });
|
|
95
|
+
return announcements.every((post) => listed.includes(post));
|
|
96
|
+
});
|
|
97
|
+
return listingAll.find((index) => index.index.scope === "site") ?? listingAll[0] ?? findHomeBlog(pages, announcements[0], drafts);
|
|
106
98
|
}
|
|
107
99
|
|
|
108
100
|
// src/feed.ts
|
|
@@ -210,13 +202,24 @@ var DEFAULT_LABELS = {
|
|
|
210
202
|
older: "Older post",
|
|
211
203
|
allPosts: "All posts in {blog}",
|
|
212
204
|
draft: "Draft",
|
|
213
|
-
|
|
205
|
+
pinned: "Pinned",
|
|
206
|
+
announcement: "Announcement",
|
|
207
|
+
announcements: "Announcements",
|
|
208
|
+
announcementCount: "{n} announcements",
|
|
209
|
+
dismiss: "Dismiss",
|
|
214
210
|
noPosts: "No posts yet.",
|
|
215
211
|
undated: "Undated",
|
|
212
|
+
browse: "Browse posts",
|
|
213
|
+
categories: "Categories",
|
|
216
214
|
tags: "Keywords",
|
|
217
|
-
|
|
215
|
+
archive: "Archive",
|
|
216
|
+
category: "Category",
|
|
217
|
+
tag: "Keyword",
|
|
218
|
+
postCount: "{n} posts",
|
|
219
|
+
postCountOne: "{n} post",
|
|
220
|
+
noMatches: "No posts match these filters.",
|
|
221
|
+
removeFilter: "Remove filter: {filter}",
|
|
218
222
|
showAll: "Show all posts",
|
|
219
|
-
noTagged: "No posts tagged {tag}.",
|
|
220
223
|
previous: "Previous",
|
|
221
224
|
next: "Next",
|
|
222
225
|
pages: "Pages",
|
|
@@ -322,6 +325,13 @@ function toDateTimeAttribute({ date, hasTime }, timeZone) {
|
|
|
322
325
|
function yearIn(date, timeZone) {
|
|
323
326
|
return Number(toZonedIsoDate(date, timeZone).slice(0, 4));
|
|
324
327
|
}
|
|
328
|
+
function monthIn(date, timeZone) {
|
|
329
|
+
return toZonedIsoDate(date, timeZone).slice(0, 7);
|
|
330
|
+
}
|
|
331
|
+
function formatMonth(month, locale, format) {
|
|
332
|
+
const [year, number] = month.split("-").map(Number);
|
|
333
|
+
return new Intl.DateTimeFormat(locale, { timeZone: "UTC", ...format }).format(Date.UTC(year, number - 1, 1, 12));
|
|
334
|
+
}
|
|
325
335
|
|
|
326
336
|
// src/render.ts
|
|
327
337
|
var BLOG_POSTS_PLACEHOLDER = "<!-- blog-posts -->";
|
|
@@ -377,25 +387,41 @@ function renderAuthors(authors, context) {
|
|
|
377
387
|
function renderBadge(kind, context) {
|
|
378
388
|
return `<span class="vp-blog-badge vp-blog-badge--${kind}">${escapeTemplateText(context.labels[kind])}</span>`;
|
|
379
389
|
}
|
|
380
|
-
function
|
|
381
|
-
const
|
|
390
|
+
function toFilterUrl(blogRoutePath, param, value) {
|
|
391
|
+
const encoded = encodeURIComponent(value.toLowerCase()).replace(/[()]/g, (c) => c === "(" ? "%28" : "%29");
|
|
382
392
|
const page = blogRoutePath.replace(/(^|\/)index\.md$/, "$1");
|
|
383
|
-
return `${toMarkdownUrl(`/${page}`)}
|
|
393
|
+
return `${toMarkdownUrl(`/${page}`)}?${param}=${encoded}`;
|
|
394
|
+
}
|
|
395
|
+
function renderFilterLink(text, param, blogRoutePath, className) {
|
|
396
|
+
return blogRoutePath ? `[${escapeMarkdown(text)}](${toFilterUrl(blogRoutePath, param, text)})` : `<span class="${className}">${escapeTemplateText(text)}</span>`;
|
|
384
397
|
}
|
|
385
398
|
function renderTags(tags, blogRoutePath) {
|
|
386
|
-
return tags.map(
|
|
387
|
-
(tag) => blogRoutePath ? `[${escapeMarkdown(tag)}](${toTagUrl(blogRoutePath, tag)})` : `<span class="vp-blog-tag">${escapeTemplateText(tag)}</span>`
|
|
388
|
-
).join(" ");
|
|
399
|
+
return tags.map((tag) => renderFilterLink(tag, "tag", blogRoutePath, "vp-blog-tag")).join(" ");
|
|
389
400
|
}
|
|
390
401
|
function toTagsAttribute(post) {
|
|
391
402
|
return escapeHtml(post.post.tags.map((tag) => tag.toLowerCase()).join("|"));
|
|
392
403
|
}
|
|
404
|
+
function toItemAttributes(post, context) {
|
|
405
|
+
const { category, date, announcement } = post.post;
|
|
406
|
+
const attributes = [`data-vp-blog-item data-tags="${toTagsAttribute(post)}"`];
|
|
407
|
+
if (category) attributes.push(`data-category="${escapeHtml(category.toLowerCase())}"`);
|
|
408
|
+
if (date) attributes.push(`data-month="${monthIn(date, context.dates.timeZone)}"`);
|
|
409
|
+
if (announcement) attributes.push("data-announcement");
|
|
410
|
+
return attributes.join(" ");
|
|
411
|
+
}
|
|
412
|
+
function renderBadges(post, context) {
|
|
413
|
+
const { draft, pinned, announcement } = post.post;
|
|
414
|
+
return [
|
|
415
|
+
draft && renderBadge("draft", context),
|
|
416
|
+
pinned && renderBadge("pinned", context),
|
|
417
|
+
announcement && renderBadge("announcement", context)
|
|
418
|
+
].filter(Boolean).join(" ");
|
|
419
|
+
}
|
|
393
420
|
function renderMeta(post, context, include) {
|
|
394
|
-
const { date, dateHasTime, updated, updatedHasTime, readingMinutes, authors
|
|
421
|
+
const { date, dateHasTime, updated, updatedHasTime, readingMinutes, authors } = post.post;
|
|
395
422
|
const parts = [];
|
|
396
423
|
if (include.badges) {
|
|
397
|
-
const
|
|
398
|
-
const shown = badges.filter(Boolean).join(" ");
|
|
424
|
+
const shown = renderBadges(post, context);
|
|
399
425
|
if (shown) parts.push(shown);
|
|
400
426
|
}
|
|
401
427
|
if (date) {
|
|
@@ -422,16 +448,18 @@ function renderCard(post, context, index) {
|
|
|
422
448
|
const link = `/${post.routePath}`;
|
|
423
449
|
const image = toImageSource(post, context.pageDirectory);
|
|
424
450
|
const modifiers = [
|
|
425
|
-
post.post.
|
|
451
|
+
post.post.pinned && "pinned",
|
|
426
452
|
post.post.draft && "draft",
|
|
453
|
+
post.post.announcement && "announcement",
|
|
427
454
|
image && "with-image",
|
|
428
455
|
image && post.post.imageFit === "contain" && "contain"
|
|
429
456
|
].filter(Boolean).map((modifier) => ` vp-blog-card--${modifier}`).join("");
|
|
430
457
|
const alt = escapeMarkdown(post.post.imageAlt);
|
|
431
458
|
return [
|
|
432
|
-
`<article class="vp-blog-card${modifiers}"${imageStyle(post)}
|
|
459
|
+
`<article class="vp-blog-card${modifiers}"${imageStyle(post)} ${toItemAttributes(post, context)}>`,
|
|
433
460
|
...image ? block("vp-blog-card__image", `[})](${toMarkdownUrl(link)})`) : [],
|
|
434
461
|
'<div class="vp-blog-card__body">',
|
|
462
|
+
...index.index.showCategory && post.post.category ? block("vp-blog-card__category", renderFilterLink(post.post.category, "category", index.routePath, "")) : [],
|
|
435
463
|
...block("vp-blog-card__title", toPageLink(post.post.title, post.routePath)),
|
|
436
464
|
...block("vp-blog-card__meta", renderMeta(post, context, { authors: true, updated: false, badges: true })),
|
|
437
465
|
...block("vp-blog-card__summary", escapeMarkdown(post.post.summary)),
|
|
@@ -442,8 +470,8 @@ function renderCard(post, context, index) {
|
|
|
442
470
|
}
|
|
443
471
|
function renderCompactItem(post, context) {
|
|
444
472
|
const date = post.post.date ? `${renderTime(post.post.date, post.post.dateHasTime, context)} ` : "";
|
|
445
|
-
const badges =
|
|
446
|
-
const marker = `<span hidden
|
|
473
|
+
const badges = renderBadges(post, context);
|
|
474
|
+
const marker = `<span hidden ${toItemAttributes(post, context)}></span>`;
|
|
447
475
|
return `- ${marker}${date}${toPageLink(post.post.title, post.routePath)}${badges ? ` ${badges}` : ""}`;
|
|
448
476
|
}
|
|
449
477
|
function renderGroup(posts, index, context) {
|
|
@@ -463,18 +491,23 @@ function renderGroup(posts, index, context) {
|
|
|
463
491
|
"</div>"
|
|
464
492
|
];
|
|
465
493
|
}
|
|
466
|
-
function
|
|
467
|
-
const
|
|
494
|
+
function renderGroups(posts, index, context) {
|
|
495
|
+
const { groupBy } = index.index;
|
|
496
|
+
if (!groupBy) return renderGroup(posts, index, context);
|
|
497
|
+
const { timeZone, locale } = context.dates;
|
|
498
|
+
const groups = /* @__PURE__ */ new Map();
|
|
468
499
|
for (const post of posts) {
|
|
469
|
-
const
|
|
470
|
-
|
|
500
|
+
const { date } = post.post;
|
|
501
|
+
const key = !date ? "" : groupBy === "year" ? String(yearIn(date, timeZone)) : monthIn(date, timeZone);
|
|
502
|
+
groups.set(key, [...groups.get(key) ?? [], post]);
|
|
471
503
|
}
|
|
472
504
|
const lines = [];
|
|
473
|
-
for (const [
|
|
505
|
+
for (const [key, group] of groups) {
|
|
506
|
+
const heading = !key ? context.labels.undated : groupBy === "year" ? key : formatMonth(key, locale, { month: "long", year: "numeric" });
|
|
474
507
|
lines.push(
|
|
475
|
-
|
|
508
|
+
`<section class="vp-blog-group vp-blog-${groupBy}">`,
|
|
476
509
|
"",
|
|
477
|
-
`## ${escapeMarkdown(
|
|
510
|
+
`## ${escapeMarkdown(heading)}`,
|
|
478
511
|
"",
|
|
479
512
|
...renderGroup(group, index, context),
|
|
480
513
|
"",
|
|
@@ -483,42 +516,114 @@ function renderYears(posts, index, context) {
|
|
|
483
516
|
}
|
|
484
517
|
return lines;
|
|
485
518
|
}
|
|
486
|
-
function
|
|
487
|
-
const
|
|
519
|
+
function collectValues(posts, pick) {
|
|
520
|
+
const values = /* @__PURE__ */ new Map();
|
|
488
521
|
for (const post of posts) {
|
|
489
|
-
for (const
|
|
490
|
-
const entry =
|
|
522
|
+
for (const value of new Set(pick(post))) {
|
|
523
|
+
const entry = values.get(value.toLowerCase()) ?? { value, count: 0 };
|
|
491
524
|
entry.count++;
|
|
492
|
-
|
|
525
|
+
values.set(value.toLowerCase(), entry);
|
|
493
526
|
}
|
|
494
527
|
}
|
|
495
|
-
return [...
|
|
528
|
+
return [...values.values()].sort((a, b) => a.value.localeCompare(b.value, void 0, { sensitivity: "base" }));
|
|
496
529
|
}
|
|
497
|
-
function
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
530
|
+
function collectTags(posts) {
|
|
531
|
+
return collectValues(posts, (post) => post.post.tags).map(({ value, count }) => ({ tag: value, count }));
|
|
532
|
+
}
|
|
533
|
+
function collectCategories(posts) {
|
|
534
|
+
return collectValues(posts, (post) => post.post.category ? [post.post.category] : []);
|
|
535
|
+
}
|
|
536
|
+
function collectArchive(posts, timeZone) {
|
|
537
|
+
const years = /* @__PURE__ */ new Map();
|
|
538
|
+
for (const post of posts) {
|
|
539
|
+
if (!post.post.date) continue;
|
|
540
|
+
const month = monthIn(post.post.date, timeZone);
|
|
541
|
+
const year = years.get(month.slice(0, 4)) ?? { value: month.slice(0, 4), count: 0, months: [] };
|
|
542
|
+
year.count++;
|
|
543
|
+
const entry = year.months.find((candidate) => candidate.value === month);
|
|
544
|
+
if (entry) entry.count++;
|
|
545
|
+
else year.months.push({ value: month, count: 1 });
|
|
546
|
+
years.set(year.value, year);
|
|
547
|
+
}
|
|
548
|
+
const newestFirst = (a, b) => b.value.localeCompare(a.value);
|
|
549
|
+
return [...years.values()].sort(newestFirst).map((year) => ({ ...year, months: [...year.months].sort(newestFirst) }));
|
|
550
|
+
}
|
|
551
|
+
function renderChip(name, count, url, title = "") {
|
|
552
|
+
const tooltip = title ? ` "${title.replace(/"/g, """)}"` : "";
|
|
553
|
+
return `[<span class="vp-blog-facet__name">${escapeMarkdown(name)}</span> <span class="vp-blog-facet__count">${count}</span>](${url}${tooltip})`;
|
|
554
|
+
}
|
|
555
|
+
function renderFacet(facet, label, chips, ariaLabel = label, attributes = "") {
|
|
504
556
|
return [
|
|
505
|
-
`<
|
|
557
|
+
`<div class="vp-blog-facet vp-blog-facet--${facet}" role="group" aria-label="${escapeHtml(ariaLabel)}" data-vp-blog-facet="${facet}"${attributes}>`,
|
|
558
|
+
`<span class="vp-blog-facet__label" aria-hidden="true">${escapeTemplateText(label)}</span>`,
|
|
559
|
+
'<div class="vp-blog-facet__chips">',
|
|
506
560
|
"",
|
|
507
|
-
|
|
561
|
+
chips.join(" "),
|
|
508
562
|
"",
|
|
509
|
-
"</
|
|
563
|
+
"</div>",
|
|
564
|
+
"</div>"
|
|
510
565
|
];
|
|
511
566
|
}
|
|
567
|
+
function renderBrowse(posts, index, context) {
|
|
568
|
+
const { labels } = context;
|
|
569
|
+
const { locale, timeZone } = context.dates;
|
|
570
|
+
const blog = index.routePath;
|
|
571
|
+
const rows = [];
|
|
572
|
+
const categories = index.index.categoryList ? collectCategories(posts) : [];
|
|
573
|
+
if (categories.length) {
|
|
574
|
+
const chips = categories.map(({ value, count }) => renderChip(value, count, toFilterUrl(blog, "category", value)));
|
|
575
|
+
rows.push(...renderFacet("category", labels.categories, chips));
|
|
576
|
+
}
|
|
577
|
+
const tags = index.index.tagList ? collectTags(posts) : [];
|
|
578
|
+
if (tags.length) {
|
|
579
|
+
const chips = tags.map(({ tag, count }) => renderChip(tag, count, toFilterUrl(blog, "tag", tag)));
|
|
580
|
+
rows.push(...renderFacet("tag", labels.tags, chips));
|
|
581
|
+
}
|
|
582
|
+
const archive = index.index.archive ? collectArchive(posts, timeZone) : [];
|
|
583
|
+
if (archive.reduce((months, year) => months + year.months.length, 0) > 1) {
|
|
584
|
+
const years = archive.map(({ value, count }) => renderChip(value, count, toFilterUrl(blog, "year", value)));
|
|
585
|
+
rows.push(...renderFacet("year", labels.archive, years));
|
|
586
|
+
for (const year of archive) {
|
|
587
|
+
const months = year.months.map(
|
|
588
|
+
({ value, count }) => renderChip(
|
|
589
|
+
formatMonth(value, locale, { month: "short" }),
|
|
590
|
+
count,
|
|
591
|
+
toFilterUrl(blog, "month", value),
|
|
592
|
+
formatMonth(value, locale, { month: "long", year: "numeric" })
|
|
593
|
+
)
|
|
594
|
+
);
|
|
595
|
+
const shown = archive.length === 1 ? "" : " hidden";
|
|
596
|
+
rows.push(
|
|
597
|
+
...renderFacet(
|
|
598
|
+
"month",
|
|
599
|
+
year.value,
|
|
600
|
+
months,
|
|
601
|
+
`${labels.archive} ${year.value}`,
|
|
602
|
+
` data-year="${year.value}"${shown}`
|
|
603
|
+
)
|
|
604
|
+
);
|
|
605
|
+
}
|
|
606
|
+
}
|
|
607
|
+
if (rows.length === 0) return [];
|
|
608
|
+
return [`<nav class="vp-blog-browse" aria-label="${escapeHtml(labels.browse)}">`, ...rows, "</nav>"];
|
|
609
|
+
}
|
|
512
610
|
function renderClientAttributes(index, context) {
|
|
513
611
|
const { labels } = context;
|
|
514
612
|
const attributes = {
|
|
515
613
|
"data-page-size": String(index.index.pageSize),
|
|
516
|
-
"
|
|
614
|
+
// For writing a `?month=` filter's name: "October 2026".
|
|
615
|
+
"data-locale": context.locale,
|
|
517
616
|
"data-label-show-all": labels.showAll,
|
|
518
|
-
"data-label-no-tagged": labels.noTagged,
|
|
519
617
|
"data-label-previous": labels.previous,
|
|
520
618
|
"data-label-next": labels.next,
|
|
521
|
-
"data-label-pages": labels.pages
|
|
619
|
+
"data-label-pages": labels.pages,
|
|
620
|
+
"data-label-announcements": labels.announcements,
|
|
621
|
+
"data-label-category": labels.category,
|
|
622
|
+
"data-label-tag": labels.tag,
|
|
623
|
+
"data-label-post-count": labels.postCount,
|
|
624
|
+
"data-label-post-count-one": labels.postCountOne,
|
|
625
|
+
"data-label-no-matches": labels.noMatches,
|
|
626
|
+
"data-label-remove-filter": labels.removeFilter
|
|
522
627
|
};
|
|
523
628
|
return Object.entries(attributes).map(([name, value]) => `${name}="${escapeHtml(value)}"`).join(" ");
|
|
524
629
|
}
|
|
@@ -529,17 +634,16 @@ function renderPostList(pinned, rest, index, context) {
|
|
|
529
634
|
const lines = [
|
|
530
635
|
`<div class="vp-blog" data-vp-blog ${renderClientAttributes(index, context)}>`,
|
|
531
636
|
...block("vp-blog-subscribe", renderRssLink(index, context)),
|
|
532
|
-
...
|
|
533
|
-
'<div class="vp-blog-filter" data-vp-blog-filter hidden></div>'
|
|
637
|
+
...renderBrowse(all, index, context),
|
|
638
|
+
'<div class="vp-blog-filter" data-vp-blog-filter role="status" hidden></div>'
|
|
534
639
|
];
|
|
535
640
|
if (pinned.length)
|
|
536
641
|
lines.push(
|
|
537
|
-
'<section class="vp-blog-
|
|
642
|
+
'<section class="vp-blog-pinned" data-vp-blog-pinned>',
|
|
538
643
|
...renderGroup(pinned, index, context),
|
|
539
644
|
"</section>"
|
|
540
645
|
);
|
|
541
|
-
if (rest.length)
|
|
542
|
-
lines.push(...index.index.groupByYear ? renderYears(rest, index, context) : renderGroup(rest, index, context));
|
|
646
|
+
if (rest.length) lines.push(...renderGroups(rest, index, context));
|
|
543
647
|
lines.push(`<nav class="vp-blog-pagination" data-vp-blog-pagination hidden></nav>`, "</div>");
|
|
544
648
|
return `${lines.join("\n")}
|
|
545
649
|
`;
|
|
@@ -548,8 +652,12 @@ function renderPostHeader(post, context, show, blogRoutePath = null) {
|
|
|
548
652
|
const lines = [];
|
|
549
653
|
if (show.byline) {
|
|
550
654
|
const meta = renderMeta(post, context, { authors: true, updated: true, badges: true });
|
|
551
|
-
const
|
|
552
|
-
|
|
655
|
+
const { category, tags } = post.post;
|
|
656
|
+
const chips = [
|
|
657
|
+
category && `<span class="vp-blog-byline__category">${renderFilterLink(category, "category", blogRoutePath, "vp-blog-category")}</span>`,
|
|
658
|
+
tags.length && `<span class="vp-blog-byline__tags">${renderTags(tags, blogRoutePath)}</span>`
|
|
659
|
+
].filter(Boolean).join(" ");
|
|
660
|
+
lines.push(...block("vp-blog-byline", [meta, chips].filter(Boolean).join("\n\n")));
|
|
553
661
|
}
|
|
554
662
|
if (show.cover && post.post.image) {
|
|
555
663
|
const image = /^[./@~]/.test(post.post.image) || isExternalUrl(post.post.image) ? post.post.image : `./${post.post.image}`;
|
|
@@ -684,6 +792,7 @@ function resolvePostData(data, content, fallbackTitle, options = {}) {
|
|
|
684
792
|
const timeZone = resolveTimeZone(options.timeZone);
|
|
685
793
|
const date = parsePostDate(pick("date"), timeZone);
|
|
686
794
|
const updated = parsePostDate(pick("updated"), timeZone);
|
|
795
|
+
const announcementText = typeof post.announcement === "string" ? clean(post.announcement.trim()) : "";
|
|
687
796
|
return {
|
|
688
797
|
title: clean(toText(post.title) || toText(data.title) || readHeading(content)) || fallbackTitle,
|
|
689
798
|
summary: clean(toText(post.summary) || toText(data.description) || readExcerpt(content)),
|
|
@@ -699,8 +808,11 @@ function resolvePostData(data, content, fallbackTitle, options = {}) {
|
|
|
699
808
|
tags: toStringList(pick("tags")),
|
|
700
809
|
category: toText(pick("category")),
|
|
701
810
|
scope: toScope(post.scope),
|
|
702
|
-
|
|
811
|
+
// `sticky` is this setting's old name.
|
|
812
|
+
pinned: (post.pinned ?? post.sticky) === true,
|
|
703
813
|
draft: post.draft === true,
|
|
814
|
+
announcement: post.announcement === true || !!announcementText,
|
|
815
|
+
announcementText,
|
|
704
816
|
byline: toBoolean(post.byline),
|
|
705
817
|
cover: toBoolean(post.cover),
|
|
706
818
|
postNav: toBoolean(post.postNav),
|
|
@@ -710,6 +822,9 @@ function resolvePostData(data, content, fallbackTitle, options = {}) {
|
|
|
710
822
|
function toLayout(value) {
|
|
711
823
|
return value === "grid" || value === "compact" ? value : "list";
|
|
712
824
|
}
|
|
825
|
+
function toGroupBy(value) {
|
|
826
|
+
return value === "year" || value === "month" ? value : "";
|
|
827
|
+
}
|
|
713
828
|
function defaultFeedPath(relativePath) {
|
|
714
829
|
const name = path3.posix.basename(relativePath, ".md");
|
|
715
830
|
return name === "index" ? "feed.xml" : `${name}-feed.xml`;
|
|
@@ -743,13 +858,17 @@ function resolveIndexConfig(data, relativePath, defaults = {}) {
|
|
|
743
858
|
scope,
|
|
744
859
|
folders: folders.length ? resolveFolders(folders, relativePath) : scope === "section" ? [ownFolder] : [],
|
|
745
860
|
tags: toStringList(options.tags),
|
|
861
|
+
categories: toStringList(options.categories),
|
|
746
862
|
limit: typeof options.limit === "number" && options.limit > 0 ? Math.floor(options.limit) : 0,
|
|
747
863
|
layout: toLayout(options.layout),
|
|
748
|
-
|
|
864
|
+
groupBy: toGroupBy(options.groupBy),
|
|
749
865
|
showTags: options.showTags !== false,
|
|
866
|
+
showCategory: options.showCategory !== false,
|
|
867
|
+
categoryList: options.categoryList !== false,
|
|
750
868
|
tagList: options.tagList !== false,
|
|
869
|
+
archive: options.archive !== false,
|
|
751
870
|
pageSize: toCount(options.pageSize, toCount(defaults.pageSize, 10)),
|
|
752
|
-
|
|
871
|
+
pinLimit: toCount(options.pinLimit ?? options.stickyLimit, toCount(defaults.pinLimit ?? defaults.stickyLimit, 0)),
|
|
753
872
|
rssLink: typeof options.rssLink === "boolean" ? options.rssLink : defaults.rssLink ?? true,
|
|
754
873
|
feed: toFeed(options.feed, relativePath)
|
|
755
874
|
};
|
|
@@ -819,7 +938,7 @@ function toOptionsKey(options) {
|
|
|
819
938
|
options.wordsPerMinute,
|
|
820
939
|
options.stripTags,
|
|
821
940
|
options.pageSize,
|
|
822
|
-
options.stickyLimit,
|
|
941
|
+
options.pinLimit ?? options.stickyLimit,
|
|
823
942
|
options.rssLink,
|
|
824
943
|
options.timeZone
|
|
825
944
|
]);
|
|
@@ -908,10 +1027,13 @@ function resolveWidgetConfig(body) {
|
|
|
908
1027
|
config: {
|
|
909
1028
|
blog: typeof data.blog === "string" ? data.blog.trim() : "",
|
|
910
1029
|
limit: toCount2(data.limit) ?? DEFAULT_LIMIT,
|
|
911
|
-
stickyLimit
|
|
1030
|
+
// `stickyLimit` is this setting's old name.
|
|
1031
|
+
pinLimit: toCount2(data.pinLimit ?? data.stickyLimit),
|
|
912
1032
|
layout,
|
|
913
1033
|
showTags: typeof data.showTags === "boolean" ? data.showTags : void 0,
|
|
1034
|
+
showCategory: typeof data.showCategory === "boolean" ? data.showCategory : void 0,
|
|
914
1035
|
tags: toStringList(data.tags),
|
|
1036
|
+
categories: toStringList(data.categories),
|
|
915
1037
|
more: data.more === false ? null : typeof data.more === "string" && data.more.trim() ? data.more.trim() : "",
|
|
916
1038
|
rssLink: typeof data.rssLink === "boolean" ? data.rssLink : void 0
|
|
917
1039
|
},
|
|
@@ -927,19 +1049,19 @@ function renderWidget(body, environment) {
|
|
|
927
1049
|
warn(config.blog ? `no blog page found at "${config.blog}"` : 'no blog covers this page: name one with "blog:"');
|
|
928
1050
|
return "";
|
|
929
1051
|
}
|
|
930
|
-
const
|
|
931
|
-
const
|
|
932
|
-
|
|
933
|
-
(post) => wanted.size === 0 || post.post.tags.some((tag) => wanted.has(tag.toLowerCase()))
|
|
1052
|
+
const pinLimit = config.pinLimit ?? blog.index.pinLimit;
|
|
1053
|
+
const posts = listBlogPosts(pages, blog, { drafts, pinnedFirst: false, applyLimit: false }).filter(
|
|
1054
|
+
(post) => hasAnyTag(post, config.tags) && inAnyCategory(post, config.categories)
|
|
934
1055
|
);
|
|
935
|
-
const { pinned, rest } =
|
|
1056
|
+
const { pinned, rest } = pinPosts(posts, pinLimit);
|
|
936
1057
|
const shown = [...pinned, ...rest].slice(0, config.limit || void 0);
|
|
937
1058
|
const view = {
|
|
938
1059
|
...blog,
|
|
939
1060
|
index: {
|
|
940
1061
|
...blog.index,
|
|
941
1062
|
layout: config.layout ?? blog.index.layout,
|
|
942
|
-
showTags: config.showTags ?? blog.index.showTags
|
|
1063
|
+
showTags: config.showTags ?? blog.index.showTags,
|
|
1064
|
+
showCategory: config.showCategory ?? blog.index.showCategory
|
|
943
1065
|
}
|
|
944
1066
|
};
|
|
945
1067
|
const lines = [`<div class="vp-blog-widget">`];
|
|
@@ -1013,8 +1135,8 @@ function hasFeeds(options) {
|
|
|
1013
1135
|
}
|
|
1014
1136
|
function withPostList(source, page, pages, options) {
|
|
1015
1137
|
if (!isIndex(page)) return source;
|
|
1016
|
-
const posts = listBlogPosts(pages, page, { drafts: options.drafts ?? false,
|
|
1017
|
-
const { pinned, rest } =
|
|
1138
|
+
const posts = listBlogPosts(pages, page, { drafts: options.drafts ?? false, pinnedFirst: true, applyLimit: true });
|
|
1139
|
+
const { pinned, rest } = pinPosts(posts, page.index.pinLimit);
|
|
1018
1140
|
const context = createRenderContext(options, path6.dirname(page.absolutePath), hasFeeds(options));
|
|
1019
1141
|
const generated = renderPostList(pinned, rest, page, context);
|
|
1020
1142
|
return source.includes(BLOG_POSTS_PLACEHOLDER) ? source.replace(BLOG_POSTS_PLACEHOLDER, () => `
|
|
@@ -1037,7 +1159,7 @@ function withPostDecorations(source, page, pages, options) {
|
|
|
1037
1159
|
let result = header ? insertAfterTitle(source, header) : source;
|
|
1038
1160
|
if (page.post.postNav ?? options.postNav ?? true) {
|
|
1039
1161
|
if (blog) {
|
|
1040
|
-
const posts = listBlogPosts(pages, blog, { drafts,
|
|
1162
|
+
const posts = listBlogPosts(pages, blog, { drafts, pinnedFirst: false, applyLimit: false });
|
|
1041
1163
|
result = `${result.replace(/\s*$/, "")}
|
|
1042
1164
|
|
|
1043
1165
|
${renderPostNav(page, posts, blog, context)}
|
|
@@ -1088,7 +1210,8 @@ export {
|
|
|
1088
1210
|
isIndex,
|
|
1089
1211
|
listBlogPosts,
|
|
1090
1212
|
findBlogsListing,
|
|
1091
|
-
|
|
1213
|
+
listAnnouncements,
|
|
1214
|
+
findAnnouncementBlog,
|
|
1092
1215
|
toPageUrl,
|
|
1093
1216
|
toFeedPath,
|
|
1094
1217
|
resolvePostImage,
|
|
@@ -1105,4 +1228,4 @@ export {
|
|
|
1105
1228
|
hasWidget,
|
|
1106
1229
|
blogMarkdown
|
|
1107
1230
|
};
|
|
1108
|
-
//# sourceMappingURL=chunk-
|
|
1231
|
+
//# sourceMappingURL=chunk-ZM5FOUT4.js.map
|