@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
|
@@ -30,6 +30,11 @@ function hasAnyTag(post, tags) {
|
|
|
30
30
|
const wanted = new Set(tags.map((tag) => tag.toLowerCase()));
|
|
31
31
|
return post.post.tags.some((tag) => wanted.has(tag.toLowerCase()));
|
|
32
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
|
+
}
|
|
33
38
|
function blogCoversPost(index, post, sections) {
|
|
34
39
|
if (index.index.scope === "section") return isInFolders(post.relativePath, index.index.folders);
|
|
35
40
|
if (!isInFolders(post.relativePath, index.index.folders)) return false;
|
|
@@ -43,22 +48,22 @@ function compareDates(a, b) {
|
|
|
43
48
|
function compareByDate(a, b) {
|
|
44
49
|
return compareDates(a.post.date, b.post.date) || a.post.title.localeCompare(b.post.title);
|
|
45
50
|
}
|
|
46
|
-
function
|
|
47
|
-
const
|
|
48
|
-
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);
|
|
49
54
|
return { pinned: [...pinned], rest: posts.filter((post) => !pinned.has(post)) };
|
|
50
55
|
}
|
|
51
56
|
function listBlogPosts(pages, index, options) {
|
|
52
57
|
const sections = pages.filter(isIndex).filter((page) => page.index.scope === "section");
|
|
53
|
-
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);
|
|
54
|
-
if (options.
|
|
55
|
-
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);
|
|
56
61
|
posts = [...pinned, ...rest];
|
|
57
62
|
}
|
|
58
63
|
return options.applyLimit && index.index.limit ? posts.slice(0, index.index.limit) : posts;
|
|
59
64
|
}
|
|
60
65
|
function findHomeBlog(pages, post, drafts) {
|
|
61
|
-
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));
|
|
62
67
|
const sections = listing.filter((index) => index.index.scope === "section");
|
|
63
68
|
if (sections.length) return deepestFirst(sections, post.relativePath)[0];
|
|
64
69
|
return listing[0] ?? null;
|
|
@@ -77,7 +82,7 @@ function findBlogForPage(pages, relativePath) {
|
|
|
77
82
|
}
|
|
78
83
|
function findBlogsListing(pages, post) {
|
|
79
84
|
return pages.filter(isIndex).filter(
|
|
80
|
-
(index) => listBlogPosts(pages, index, { drafts: false,
|
|
85
|
+
(index) => listBlogPosts(pages, index, { drafts: false, pinnedFirst: false, applyLimit: false }).includes(post)
|
|
81
86
|
);
|
|
82
87
|
}
|
|
83
88
|
function listAnnouncements(pages, drafts) {
|
|
@@ -86,7 +91,7 @@ function listAnnouncements(pages, drafts) {
|
|
|
86
91
|
function findAnnouncementBlog(pages, announcements, drafts) {
|
|
87
92
|
if (announcements.length === 0) return null;
|
|
88
93
|
const listingAll = pages.filter(isIndex).filter((index) => {
|
|
89
|
-
const listed = listBlogPosts(pages, index, { drafts,
|
|
94
|
+
const listed = listBlogPosts(pages, index, { drafts, pinnedFirst: false, applyLimit: false });
|
|
90
95
|
return announcements.every((post) => listed.includes(post));
|
|
91
96
|
});
|
|
92
97
|
return listingAll.find((index) => index.index.scope === "site") ?? listingAll[0] ?? findHomeBlog(pages, announcements[0], drafts);
|
|
@@ -197,18 +202,28 @@ var DEFAULT_LABELS = {
|
|
|
197
202
|
older: "Older post",
|
|
198
203
|
allPosts: "All posts in {blog}",
|
|
199
204
|
draft: "Draft",
|
|
200
|
-
|
|
205
|
+
pinned: "Pinned",
|
|
201
206
|
announcement: "Announcement",
|
|
202
207
|
announcements: "Announcements",
|
|
203
|
-
noAnnouncements: "No announcements.",
|
|
204
208
|
announcementCount: "{n} announcements",
|
|
205
209
|
dismiss: "Dismiss",
|
|
206
210
|
noPosts: "No posts yet.",
|
|
207
211
|
undated: "Undated",
|
|
212
|
+
browse: "Browse posts",
|
|
213
|
+
categories: "Categories",
|
|
208
214
|
tags: "Keywords",
|
|
209
|
-
|
|
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}",
|
|
210
222
|
showAll: "Show all posts",
|
|
211
|
-
|
|
223
|
+
more: "+{n} more",
|
|
224
|
+
less: "Show fewer",
|
|
225
|
+
filter: "Filter {row}",
|
|
226
|
+
noFilterMatches: "Nothing matches.",
|
|
212
227
|
previous: "Previous",
|
|
213
228
|
next: "Next",
|
|
214
229
|
pages: "Pages",
|
|
@@ -314,6 +329,13 @@ function toDateTimeAttribute({ date, hasTime }, timeZone) {
|
|
|
314
329
|
function yearIn(date, timeZone) {
|
|
315
330
|
return Number(toZonedIsoDate(date, timeZone).slice(0, 4));
|
|
316
331
|
}
|
|
332
|
+
function monthIn(date, timeZone) {
|
|
333
|
+
return toZonedIsoDate(date, timeZone).slice(0, 7);
|
|
334
|
+
}
|
|
335
|
+
function formatMonth(month, locale, format) {
|
|
336
|
+
const [year, number] = month.split("-").map(Number);
|
|
337
|
+
return new Intl.DateTimeFormat(locale, { timeZone: "UTC", ...format }).format(Date.UTC(year, number - 1, 1, 12));
|
|
338
|
+
}
|
|
317
339
|
|
|
318
340
|
// src/render.ts
|
|
319
341
|
var BLOG_POSTS_PLACEHOLDER = "<!-- blog-posts -->";
|
|
@@ -339,7 +361,8 @@ function renderRssLink(index, context, show = index.index.rssLink) {
|
|
|
339
361
|
if (!show || !context.feeds || !index.index.feed) return "";
|
|
340
362
|
const title = fill(context.labels.rssFeed, { blog: index.title }).replace(/"/g, """);
|
|
341
363
|
const url = toMarkdownUrl(`/${toFeedPath(index)}`);
|
|
342
|
-
|
|
364
|
+
const text = `<span class="vp-blog-rss__text">${escapeMarkdown(context.labels.subscribe)}</span>`;
|
|
365
|
+
return `<span class="vp-blog-rss">[${RSS_ICON} ${text}](${url} "${title}")</span>`;
|
|
343
366
|
}
|
|
344
367
|
function fill(template, values) {
|
|
345
368
|
return template.replace(/\{(\w+)\}/g, (match, key) => values[key] ?? match);
|
|
@@ -369,27 +392,33 @@ function renderAuthors(authors, context) {
|
|
|
369
392
|
function renderBadge(kind, context) {
|
|
370
393
|
return `<span class="vp-blog-badge vp-blog-badge--${kind}">${escapeTemplateText(context.labels[kind])}</span>`;
|
|
371
394
|
}
|
|
372
|
-
function
|
|
373
|
-
const
|
|
395
|
+
function toFilterUrl(blogRoutePath, param, value) {
|
|
396
|
+
const encoded = encodeURIComponent(value.toLowerCase()).replace(/[()]/g, (c) => c === "(" ? "%28" : "%29");
|
|
374
397
|
const page = blogRoutePath.replace(/(^|\/)index\.md$/, "$1");
|
|
375
|
-
return `${toMarkdownUrl(`/${page}`)}
|
|
398
|
+
return `${toMarkdownUrl(`/${page}`)}?${param}=${encoded}`;
|
|
399
|
+
}
|
|
400
|
+
function renderFilterLink(text, param, blogRoutePath, className) {
|
|
401
|
+
return blogRoutePath ? `[${escapeMarkdown(text)}](${toFilterUrl(blogRoutePath, param, text)})` : `<span class="${className}">${escapeTemplateText(text)}</span>`;
|
|
376
402
|
}
|
|
377
403
|
function renderTags(tags, blogRoutePath) {
|
|
378
|
-
return tags.map(
|
|
379
|
-
(tag) => blogRoutePath ? `[${escapeMarkdown(tag)}](${toTagUrl(blogRoutePath, tag)})` : `<span class="vp-blog-tag">${escapeTemplateText(tag)}</span>`
|
|
380
|
-
).join(" ");
|
|
404
|
+
return tags.map((tag) => renderFilterLink(tag, "tag", blogRoutePath, "vp-blog-tag")).join(" ");
|
|
381
405
|
}
|
|
382
406
|
function toTagsAttribute(post) {
|
|
383
407
|
return escapeHtml(post.post.tags.map((tag) => tag.toLowerCase()).join("|"));
|
|
384
408
|
}
|
|
385
|
-
function toItemAttributes(post) {
|
|
386
|
-
|
|
409
|
+
function toItemAttributes(post, context) {
|
|
410
|
+
const { category, date, announcement } = post.post;
|
|
411
|
+
const attributes = [`data-vp-blog-item data-tags="${toTagsAttribute(post)}"`];
|
|
412
|
+
if (category) attributes.push(`data-category="${escapeHtml(category.toLowerCase())}"`);
|
|
413
|
+
if (date) attributes.push(`data-month="${monthIn(date, context.dates.timeZone)}"`);
|
|
414
|
+
if (announcement) attributes.push("data-announcement");
|
|
415
|
+
return attributes.join(" ");
|
|
387
416
|
}
|
|
388
417
|
function renderBadges(post, context) {
|
|
389
|
-
const { draft,
|
|
418
|
+
const { draft, pinned, announcement } = post.post;
|
|
390
419
|
return [
|
|
391
420
|
draft && renderBadge("draft", context),
|
|
392
|
-
|
|
421
|
+
pinned && renderBadge("pinned", context),
|
|
393
422
|
announcement && renderBadge("announcement", context)
|
|
394
423
|
].filter(Boolean).join(" ");
|
|
395
424
|
}
|
|
@@ -424,7 +453,7 @@ function renderCard(post, context, index) {
|
|
|
424
453
|
const link = `/${post.routePath}`;
|
|
425
454
|
const image = toImageSource(post, context.pageDirectory);
|
|
426
455
|
const modifiers = [
|
|
427
|
-
post.post.
|
|
456
|
+
post.post.pinned && "pinned",
|
|
428
457
|
post.post.draft && "draft",
|
|
429
458
|
post.post.announcement && "announcement",
|
|
430
459
|
image && "with-image",
|
|
@@ -432,9 +461,10 @@ function renderCard(post, context, index) {
|
|
|
432
461
|
].filter(Boolean).map((modifier) => ` vp-blog-card--${modifier}`).join("");
|
|
433
462
|
const alt = escapeMarkdown(post.post.imageAlt);
|
|
434
463
|
return [
|
|
435
|
-
`<article class="vp-blog-card${modifiers}"${imageStyle(post)} ${toItemAttributes(post)}>`,
|
|
464
|
+
`<article class="vp-blog-card${modifiers}"${imageStyle(post)} ${toItemAttributes(post, context)}>`,
|
|
436
465
|
...image ? block("vp-blog-card__image", `[})](${toMarkdownUrl(link)})`) : [],
|
|
437
466
|
'<div class="vp-blog-card__body">',
|
|
467
|
+
...index.index.showCategory && post.post.category ? block("vp-blog-card__category", renderFilterLink(post.post.category, "category", index.routePath, "")) : [],
|
|
438
468
|
...block("vp-blog-card__title", toPageLink(post.post.title, post.routePath)),
|
|
439
469
|
...block("vp-blog-card__meta", renderMeta(post, context, { authors: true, updated: false, badges: true })),
|
|
440
470
|
...block("vp-blog-card__summary", escapeMarkdown(post.post.summary)),
|
|
@@ -446,7 +476,7 @@ function renderCard(post, context, index) {
|
|
|
446
476
|
function renderCompactItem(post, context) {
|
|
447
477
|
const date = post.post.date ? `${renderTime(post.post.date, post.post.dateHasTime, context)} ` : "";
|
|
448
478
|
const badges = renderBadges(post, context);
|
|
449
|
-
const marker = `<span hidden ${toItemAttributes(post)}></span>`;
|
|
479
|
+
const marker = `<span hidden ${toItemAttributes(post, context)}></span>`;
|
|
450
480
|
return `- ${marker}${date}${toPageLink(post.post.title, post.routePath)}${badges ? ` ${badges}` : ""}`;
|
|
451
481
|
}
|
|
452
482
|
function renderGroup(posts, index, context) {
|
|
@@ -466,18 +496,23 @@ function renderGroup(posts, index, context) {
|
|
|
466
496
|
"</div>"
|
|
467
497
|
];
|
|
468
498
|
}
|
|
469
|
-
function
|
|
470
|
-
const
|
|
499
|
+
function renderGroups(posts, index, context) {
|
|
500
|
+
const { groupBy } = index.index;
|
|
501
|
+
if (!groupBy) return renderGroup(posts, index, context);
|
|
502
|
+
const { timeZone, locale } = context.dates;
|
|
503
|
+
const groups = /* @__PURE__ */ new Map();
|
|
471
504
|
for (const post of posts) {
|
|
472
|
-
const
|
|
473
|
-
|
|
505
|
+
const { date } = post.post;
|
|
506
|
+
const key = !date ? "" : groupBy === "year" ? String(yearIn(date, timeZone)) : monthIn(date, timeZone);
|
|
507
|
+
groups.set(key, [...groups.get(key) ?? [], post]);
|
|
474
508
|
}
|
|
475
509
|
const lines = [];
|
|
476
|
-
for (const [
|
|
510
|
+
for (const [key, group] of groups) {
|
|
511
|
+
const heading = !key ? context.labels.undated : groupBy === "year" ? key : formatMonth(key, locale, { month: "long", year: "numeric" });
|
|
477
512
|
lines.push(
|
|
478
|
-
|
|
513
|
+
`<section class="vp-blog-group vp-blog-${groupBy}">`,
|
|
479
514
|
"",
|
|
480
|
-
`## ${escapeMarkdown(
|
|
515
|
+
`## ${escapeMarkdown(heading)}`,
|
|
481
516
|
"",
|
|
482
517
|
...renderGroup(group, index, context),
|
|
483
518
|
"",
|
|
@@ -486,29 +521,107 @@ function renderYears(posts, index, context) {
|
|
|
486
521
|
}
|
|
487
522
|
return lines;
|
|
488
523
|
}
|
|
489
|
-
function
|
|
490
|
-
const
|
|
524
|
+
function collectValues(posts, pick) {
|
|
525
|
+
const values = /* @__PURE__ */ new Map();
|
|
491
526
|
for (const post of posts) {
|
|
492
|
-
for (const
|
|
493
|
-
const entry =
|
|
527
|
+
for (const value of new Set(pick(post))) {
|
|
528
|
+
const entry = values.get(value.toLowerCase()) ?? { value, count: 0 };
|
|
494
529
|
entry.count++;
|
|
495
|
-
|
|
530
|
+
values.set(value.toLowerCase(), entry);
|
|
496
531
|
}
|
|
497
532
|
}
|
|
498
|
-
return [...
|
|
533
|
+
return [...values.values()].sort((a, b) => a.value.localeCompare(b.value, void 0, { sensitivity: "base" }));
|
|
499
534
|
}
|
|
500
|
-
function
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
535
|
+
function collectTags(posts) {
|
|
536
|
+
return collectValues(posts, (post) => post.post.tags).map(({ value, count }) => ({ tag: value, count }));
|
|
537
|
+
}
|
|
538
|
+
function collectCategories(posts) {
|
|
539
|
+
return collectValues(posts, (post) => post.post.category ? [post.post.category] : []);
|
|
540
|
+
}
|
|
541
|
+
function collectArchive(posts, timeZone) {
|
|
542
|
+
const years = /* @__PURE__ */ new Map();
|
|
543
|
+
for (const post of posts) {
|
|
544
|
+
if (!post.post.date) continue;
|
|
545
|
+
const month = monthIn(post.post.date, timeZone);
|
|
546
|
+
const year = years.get(month.slice(0, 4)) ?? { value: month.slice(0, 4), count: 0, months: [] };
|
|
547
|
+
year.count++;
|
|
548
|
+
const entry = year.months.find((candidate) => candidate.value === month);
|
|
549
|
+
if (entry) entry.count++;
|
|
550
|
+
else year.months.push({ value: month, count: 1 });
|
|
551
|
+
years.set(year.value, year);
|
|
552
|
+
}
|
|
553
|
+
const newestFirst = (a, b) => b.value.localeCompare(a.value);
|
|
554
|
+
return [...years.values()].sort(newestFirst).map((year) => ({ ...year, months: [...year.months].sort(newestFirst) }));
|
|
555
|
+
}
|
|
556
|
+
function renderChip(name, count, url, title = "") {
|
|
557
|
+
const tooltip = title ? ` "${title.replace(/"/g, """)}"` : "";
|
|
558
|
+
return `[<span class="vp-blog-facet__name">${escapeMarkdown(name)}</span> <span class="vp-blog-facet__count">${count}</span>](${url}${tooltip})`;
|
|
559
|
+
}
|
|
560
|
+
function renderFacet(row) {
|
|
561
|
+
const { facet, label, chips, ariaLabel = label, limit = 0, attributes = "", aside = "" } = row;
|
|
562
|
+
const limited = limit && chips.length > limit ? ` data-limit="${limit}"` : "";
|
|
507
563
|
return [
|
|
508
|
-
`<
|
|
564
|
+
`<div class="vp-blog-facet vp-blog-facet--${facet}" role="group" aria-label="${escapeHtml(ariaLabel)}" data-vp-blog-facet="${facet}"${limited}${attributes}>`,
|
|
565
|
+
`<span class="vp-blog-facet__label" aria-hidden="true">${escapeTemplateText(label)}</span>`,
|
|
566
|
+
'<div class="vp-blog-facet__chips">',
|
|
509
567
|
"",
|
|
510
|
-
|
|
568
|
+
chips.join(" "),
|
|
511
569
|
"",
|
|
570
|
+
"</div>",
|
|
571
|
+
...block("vp-blog-facet__aside", aside),
|
|
572
|
+
"</div>"
|
|
573
|
+
];
|
|
574
|
+
}
|
|
575
|
+
function canNarrow(values, total) {
|
|
576
|
+
return values.length > 1 || values.length === 1 && values[0].count < total;
|
|
577
|
+
}
|
|
578
|
+
function byUse(a, b) {
|
|
579
|
+
return b.count - a.count || a.value.localeCompare(b.value, void 0, { sensitivity: "base" });
|
|
580
|
+
}
|
|
581
|
+
function renderBrowse(posts, index, context) {
|
|
582
|
+
const { labels } = context;
|
|
583
|
+
const { locale, timeZone } = context.dates;
|
|
584
|
+
const { categoryList, categoryLimit, tagList, tagLimit, tagMinCount, archive: showArchive } = index.index;
|
|
585
|
+
const blog = index.routePath;
|
|
586
|
+
const rows = [];
|
|
587
|
+
const categories = categoryList ? collectCategories(posts).sort(byUse) : [];
|
|
588
|
+
if (canNarrow(categories, posts.length)) {
|
|
589
|
+
const chips = categories.map(({ value, count }) => renderChip(value, count, toFilterUrl(blog, "category", value)));
|
|
590
|
+
rows.push({ facet: "category", label: labels.categories, chips, limit: categoryLimit });
|
|
591
|
+
}
|
|
592
|
+
const tags = tagList ? collectTags(posts).map(({ tag, count }) => ({ value: tag, count })).filter(({ count }) => count >= tagMinCount).sort(byUse) : [];
|
|
593
|
+
if (canNarrow(tags, posts.length)) {
|
|
594
|
+
const chips = tags.map(({ value, count }) => renderChip(value, count, toFilterUrl(blog, "tag", value)));
|
|
595
|
+
rows.push({ facet: "tag", label: labels.tags, chips, limit: tagLimit });
|
|
596
|
+
}
|
|
597
|
+
const archive = showArchive ? collectArchive(posts, timeZone) : [];
|
|
598
|
+
const monthChips = (months) => months.map(
|
|
599
|
+
({ value, count }) => renderChip(
|
|
600
|
+
formatMonth(value, locale, { month: "short" }),
|
|
601
|
+
count,
|
|
602
|
+
toFilterUrl(blog, "month", value),
|
|
603
|
+
formatMonth(value, locale, { month: "long", year: "numeric" })
|
|
604
|
+
)
|
|
605
|
+
);
|
|
606
|
+
if (archive.length === 1 && canNarrow(archive[0].months, posts.length)) {
|
|
607
|
+
const [year] = archive;
|
|
608
|
+
const ariaLabel = `${labels.archive} ${year.value}`;
|
|
609
|
+
const attributes = ` data-year="${year.value}"`;
|
|
610
|
+
rows.push({ facet: "month", label: labels.archive, chips: monthChips(year.months), ariaLabel, attributes });
|
|
611
|
+
} else if (archive.length > 1) {
|
|
612
|
+
const years = archive.map(({ value, count }) => renderChip(value, count, toFilterUrl(blog, "year", value)));
|
|
613
|
+
rows.push({ facet: "year", label: labels.archive, chips: years });
|
|
614
|
+
for (const year of archive) {
|
|
615
|
+
const ariaLabel = `${labels.archive} ${year.value}`;
|
|
616
|
+
const attributes = ` data-year="${year.value}" hidden`;
|
|
617
|
+
rows.push({ facet: "month", label: year.value, chips: monthChips(year.months), ariaLabel, attributes });
|
|
618
|
+
}
|
|
619
|
+
}
|
|
620
|
+
if (rows.length === 0) return [];
|
|
621
|
+
rows[0].aside = renderRssLink(index, context);
|
|
622
|
+
return [
|
|
623
|
+
`<nav class="vp-blog-browse" aria-label="${escapeHtml(labels.browse)}">`,
|
|
624
|
+
...rows.flatMap(renderFacet),
|
|
512
625
|
"</nav>"
|
|
513
626
|
];
|
|
514
627
|
}
|
|
@@ -516,14 +629,23 @@ function renderClientAttributes(index, context) {
|
|
|
516
629
|
const { labels } = context;
|
|
517
630
|
const attributes = {
|
|
518
631
|
"data-page-size": String(index.index.pageSize),
|
|
519
|
-
"
|
|
632
|
+
// For writing a `?month=` filter's name: "October 2026".
|
|
633
|
+
"data-locale": context.locale,
|
|
520
634
|
"data-label-show-all": labels.showAll,
|
|
521
|
-
"data-label-no-tagged": labels.noTagged,
|
|
522
635
|
"data-label-previous": labels.previous,
|
|
523
636
|
"data-label-next": labels.next,
|
|
524
637
|
"data-label-pages": labels.pages,
|
|
525
638
|
"data-label-announcements": labels.announcements,
|
|
526
|
-
"data-label-
|
|
639
|
+
"data-label-category": labels.category,
|
|
640
|
+
"data-label-tag": labels.tag,
|
|
641
|
+
"data-label-post-count": labels.postCount,
|
|
642
|
+
"data-label-post-count-one": labels.postCountOne,
|
|
643
|
+
"data-label-no-matches": labels.noMatches,
|
|
644
|
+
"data-label-remove-filter": labels.removeFilter,
|
|
645
|
+
"data-label-more": labels.more,
|
|
646
|
+
"data-label-less": labels.less,
|
|
647
|
+
"data-label-filter": labels.filter,
|
|
648
|
+
"data-label-no-filter-matches": labels.noFilterMatches
|
|
527
649
|
};
|
|
528
650
|
return Object.entries(attributes).map(([name, value]) => `${name}="${escapeHtml(value)}"`).join(" ");
|
|
529
651
|
}
|
|
@@ -531,20 +653,19 @@ function renderPostList(pinned, rest, index, context) {
|
|
|
531
653
|
const all = [...pinned, ...rest];
|
|
532
654
|
if (all.length === 0) return `<p class="vp-blog-empty">${escapeTemplateText(context.labels.noPosts)}</p>
|
|
533
655
|
`;
|
|
656
|
+
const browse = renderBrowse(all, index, context);
|
|
534
657
|
const lines = [
|
|
535
658
|
`<div class="vp-blog" data-vp-blog ${renderClientAttributes(index, context)}>`,
|
|
536
|
-
...block("vp-blog-subscribe", renderRssLink(index, context)),
|
|
537
|
-
|
|
538
|
-
'<div class="vp-blog-filter" data-vp-blog-filter hidden></div>'
|
|
659
|
+
...browse.length ? browse : block("vp-blog-subscribe", renderRssLink(index, context)),
|
|
660
|
+
'<div class="vp-blog-filter" data-vp-blog-filter role="status" hidden></div>'
|
|
539
661
|
];
|
|
540
662
|
if (pinned.length)
|
|
541
663
|
lines.push(
|
|
542
|
-
'<section class="vp-blog-
|
|
664
|
+
'<section class="vp-blog-pinned" data-vp-blog-pinned>',
|
|
543
665
|
...renderGroup(pinned, index, context),
|
|
544
666
|
"</section>"
|
|
545
667
|
);
|
|
546
|
-
if (rest.length)
|
|
547
|
-
lines.push(...index.index.groupByYear ? renderYears(rest, index, context) : renderGroup(rest, index, context));
|
|
668
|
+
if (rest.length) lines.push(...renderGroups(rest, index, context));
|
|
548
669
|
lines.push(`<nav class="vp-blog-pagination" data-vp-blog-pagination hidden></nav>`, "</div>");
|
|
549
670
|
return `${lines.join("\n")}
|
|
550
671
|
`;
|
|
@@ -553,8 +674,12 @@ function renderPostHeader(post, context, show, blogRoutePath = null) {
|
|
|
553
674
|
const lines = [];
|
|
554
675
|
if (show.byline) {
|
|
555
676
|
const meta = renderMeta(post, context, { authors: true, updated: true, badges: true });
|
|
556
|
-
const
|
|
557
|
-
|
|
677
|
+
const { category, tags } = post.post;
|
|
678
|
+
const chips = [
|
|
679
|
+
category && `<span class="vp-blog-byline__category">${renderFilterLink(category, "category", blogRoutePath, "vp-blog-category")}</span>`,
|
|
680
|
+
tags.length && `<span class="vp-blog-byline__tags">${renderTags(tags, blogRoutePath)}</span>`
|
|
681
|
+
].filter(Boolean).join(" ");
|
|
682
|
+
lines.push(...block("vp-blog-byline", [meta, chips].filter(Boolean).join("\n\n")));
|
|
558
683
|
}
|
|
559
684
|
if (show.cover && post.post.image) {
|
|
560
685
|
const image = /^[./@~]/.test(post.post.image) || isExternalUrl(post.post.image) ? post.post.image : `./${post.post.image}`;
|
|
@@ -592,6 +717,8 @@ var FENCED_CODE = /^[ \t]*(`{3,}|~{3,})[^\n]*\n[\s\S]*?(?:^[ \t]*\1[`~]*[ \t]*$|
|
|
|
592
717
|
var HEADING_1 = /^ {0,3}#[ \t]+(.*?)(?:[ \t]+#+)?[ \t]*$/m;
|
|
593
718
|
var TAG_SYNTAX = /\(\(<?tag([|/])[^|/)\r\n]*(?:\1[^|/)\r\n]*){0,2}\)\)/g;
|
|
594
719
|
var MAX_SUMMARY_LENGTH = 200;
|
|
720
|
+
var DEFAULT_CATEGORY_LIMIT = 8;
|
|
721
|
+
var DEFAULT_TAG_LIMIT = 12;
|
|
595
722
|
function readFrontmatter(raw) {
|
|
596
723
|
const source = raw.replace(/^/, "");
|
|
597
724
|
const match = FRONTMATTER.exec(source);
|
|
@@ -705,7 +832,8 @@ function resolvePostData(data, content, fallbackTitle, options = {}) {
|
|
|
705
832
|
tags: toStringList(pick("tags")),
|
|
706
833
|
category: toText(pick("category")),
|
|
707
834
|
scope: toScope(post.scope),
|
|
708
|
-
|
|
835
|
+
// `sticky` is this setting's old name.
|
|
836
|
+
pinned: (post.pinned ?? post.sticky) === true,
|
|
709
837
|
draft: post.draft === true,
|
|
710
838
|
announcement: post.announcement === true || !!announcementText,
|
|
711
839
|
announcementText,
|
|
@@ -718,6 +846,9 @@ function resolvePostData(data, content, fallbackTitle, options = {}) {
|
|
|
718
846
|
function toLayout(value) {
|
|
719
847
|
return value === "grid" || value === "compact" ? value : "list";
|
|
720
848
|
}
|
|
849
|
+
function toGroupBy(value) {
|
|
850
|
+
return value === "year" || value === "month" ? value : "";
|
|
851
|
+
}
|
|
721
852
|
function defaultFeedPath(relativePath) {
|
|
722
853
|
const name = path3.posix.basename(relativePath, ".md");
|
|
723
854
|
return name === "index" ? "feed.xml" : `${name}-feed.xml`;
|
|
@@ -751,13 +882,20 @@ function resolveIndexConfig(data, relativePath, defaults = {}) {
|
|
|
751
882
|
scope,
|
|
752
883
|
folders: folders.length ? resolveFolders(folders, relativePath) : scope === "section" ? [ownFolder] : [],
|
|
753
884
|
tags: toStringList(options.tags),
|
|
885
|
+
categories: toStringList(options.categories),
|
|
754
886
|
limit: typeof options.limit === "number" && options.limit > 0 ? Math.floor(options.limit) : 0,
|
|
755
887
|
layout: toLayout(options.layout),
|
|
756
|
-
|
|
888
|
+
groupBy: toGroupBy(options.groupBy),
|
|
757
889
|
showTags: options.showTags !== false,
|
|
890
|
+
showCategory: options.showCategory !== false,
|
|
891
|
+
categoryList: options.categoryList !== false,
|
|
892
|
+
categoryLimit: toCount(options.categoryLimit, DEFAULT_CATEGORY_LIMIT),
|
|
758
893
|
tagList: options.tagList !== false,
|
|
894
|
+
tagLimit: toCount(options.tagLimit, DEFAULT_TAG_LIMIT),
|
|
895
|
+
tagMinCount: toCount(options.tagMinCount, 1),
|
|
896
|
+
archive: options.archive !== false,
|
|
759
897
|
pageSize: toCount(options.pageSize, toCount(defaults.pageSize, 10)),
|
|
760
|
-
|
|
898
|
+
pinLimit: toCount(options.pinLimit ?? options.stickyLimit, toCount(defaults.pinLimit ?? defaults.stickyLimit, 0)),
|
|
761
899
|
rssLink: typeof options.rssLink === "boolean" ? options.rssLink : defaults.rssLink ?? true,
|
|
762
900
|
feed: toFeed(options.feed, relativePath)
|
|
763
901
|
};
|
|
@@ -827,7 +965,7 @@ function toOptionsKey(options) {
|
|
|
827
965
|
options.wordsPerMinute,
|
|
828
966
|
options.stripTags,
|
|
829
967
|
options.pageSize,
|
|
830
|
-
options.stickyLimit,
|
|
968
|
+
options.pinLimit ?? options.stickyLimit,
|
|
831
969
|
options.rssLink,
|
|
832
970
|
options.timeZone
|
|
833
971
|
]);
|
|
@@ -916,10 +1054,13 @@ function resolveWidgetConfig(body) {
|
|
|
916
1054
|
config: {
|
|
917
1055
|
blog: typeof data.blog === "string" ? data.blog.trim() : "",
|
|
918
1056
|
limit: toCount2(data.limit) ?? DEFAULT_LIMIT,
|
|
919
|
-
stickyLimit
|
|
1057
|
+
// `stickyLimit` is this setting's old name.
|
|
1058
|
+
pinLimit: toCount2(data.pinLimit ?? data.stickyLimit),
|
|
920
1059
|
layout,
|
|
921
1060
|
showTags: typeof data.showTags === "boolean" ? data.showTags : void 0,
|
|
1061
|
+
showCategory: typeof data.showCategory === "boolean" ? data.showCategory : void 0,
|
|
922
1062
|
tags: toStringList(data.tags),
|
|
1063
|
+
categories: toStringList(data.categories),
|
|
923
1064
|
more: data.more === false ? null : typeof data.more === "string" && data.more.trim() ? data.more.trim() : "",
|
|
924
1065
|
rssLink: typeof data.rssLink === "boolean" ? data.rssLink : void 0
|
|
925
1066
|
},
|
|
@@ -935,19 +1076,19 @@ function renderWidget(body, environment) {
|
|
|
935
1076
|
warn(config.blog ? `no blog page found at "${config.blog}"` : 'no blog covers this page: name one with "blog:"');
|
|
936
1077
|
return "";
|
|
937
1078
|
}
|
|
938
|
-
const
|
|
939
|
-
const
|
|
940
|
-
|
|
941
|
-
(post) => wanted.size === 0 || post.post.tags.some((tag) => wanted.has(tag.toLowerCase()))
|
|
1079
|
+
const pinLimit = config.pinLimit ?? blog.index.pinLimit;
|
|
1080
|
+
const posts = listBlogPosts(pages, blog, { drafts, pinnedFirst: false, applyLimit: false }).filter(
|
|
1081
|
+
(post) => hasAnyTag(post, config.tags) && inAnyCategory(post, config.categories)
|
|
942
1082
|
);
|
|
943
|
-
const { pinned, rest } =
|
|
1083
|
+
const { pinned, rest } = pinPosts(posts, pinLimit);
|
|
944
1084
|
const shown = [...pinned, ...rest].slice(0, config.limit || void 0);
|
|
945
1085
|
const view = {
|
|
946
1086
|
...blog,
|
|
947
1087
|
index: {
|
|
948
1088
|
...blog.index,
|
|
949
1089
|
layout: config.layout ?? blog.index.layout,
|
|
950
|
-
showTags: config.showTags ?? blog.index.showTags
|
|
1090
|
+
showTags: config.showTags ?? blog.index.showTags,
|
|
1091
|
+
showCategory: config.showCategory ?? blog.index.showCategory
|
|
951
1092
|
}
|
|
952
1093
|
};
|
|
953
1094
|
const lines = [`<div class="vp-blog-widget">`];
|
|
@@ -1021,8 +1162,8 @@ function hasFeeds(options) {
|
|
|
1021
1162
|
}
|
|
1022
1163
|
function withPostList(source, page, pages, options) {
|
|
1023
1164
|
if (!isIndex(page)) return source;
|
|
1024
|
-
const posts = listBlogPosts(pages, page, { drafts: options.drafts ?? false,
|
|
1025
|
-
const { pinned, rest } =
|
|
1165
|
+
const posts = listBlogPosts(pages, page, { drafts: options.drafts ?? false, pinnedFirst: true, applyLimit: true });
|
|
1166
|
+
const { pinned, rest } = pinPosts(posts, page.index.pinLimit);
|
|
1026
1167
|
const context = createRenderContext(options, path6.dirname(page.absolutePath), hasFeeds(options));
|
|
1027
1168
|
const generated = renderPostList(pinned, rest, page, context);
|
|
1028
1169
|
return source.includes(BLOG_POSTS_PLACEHOLDER) ? source.replace(BLOG_POSTS_PLACEHOLDER, () => `
|
|
@@ -1045,7 +1186,7 @@ function withPostDecorations(source, page, pages, options) {
|
|
|
1045
1186
|
let result = header ? insertAfterTitle(source, header) : source;
|
|
1046
1187
|
if (page.post.postNav ?? options.postNav ?? true) {
|
|
1047
1188
|
if (blog) {
|
|
1048
|
-
const posts = listBlogPosts(pages, blog, { drafts,
|
|
1189
|
+
const posts = listBlogPosts(pages, blog, { drafts, pinnedFirst: false, applyLimit: false });
|
|
1049
1190
|
result = `${result.replace(/\s*$/, "")}
|
|
1050
1191
|
|
|
1051
1192
|
${renderPostNav(page, posts, blog, context)}
|
|
@@ -1114,4 +1255,4 @@ export {
|
|
|
1114
1255
|
hasWidget,
|
|
1115
1256
|
blogMarkdown
|
|
1116
1257
|
};
|
|
1117
|
-
//# sourceMappingURL=chunk-
|
|
1258
|
+
//# sourceMappingURL=chunk-FPFH7ORO.js.map
|