@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/index.cjs
CHANGED
|
@@ -63,6 +63,11 @@ function hasAnyTag(post, tags) {
|
|
|
63
63
|
const wanted = new Set(tags.map((tag) => tag.toLowerCase()));
|
|
64
64
|
return post.post.tags.some((tag) => wanted.has(tag.toLowerCase()));
|
|
65
65
|
}
|
|
66
|
+
function inAnyCategory(post, categories) {
|
|
67
|
+
if (categories.length === 0) return true;
|
|
68
|
+
const category = post.post.category.toLowerCase();
|
|
69
|
+
return !!category && categories.some((wanted) => wanted.toLowerCase() === category);
|
|
70
|
+
}
|
|
66
71
|
function blogCoversPost(index, post, sections) {
|
|
67
72
|
if (index.index.scope === "section") return isInFolders(post.relativePath, index.index.folders);
|
|
68
73
|
if (!isInFolders(post.relativePath, index.index.folders)) return false;
|
|
@@ -76,22 +81,22 @@ function compareDates(a, b) {
|
|
|
76
81
|
function compareByDate(a, b) {
|
|
77
82
|
return compareDates(a.post.date, b.post.date) || a.post.title.localeCompare(b.post.title);
|
|
78
83
|
}
|
|
79
|
-
function
|
|
80
|
-
const
|
|
81
|
-
const pinned = new Set(limit ?
|
|
84
|
+
function pinPosts(posts, limit) {
|
|
85
|
+
const candidates = posts.filter((post) => post.post.pinned);
|
|
86
|
+
const pinned = new Set(limit ? candidates.slice(0, limit) : candidates);
|
|
82
87
|
return { pinned: [...pinned], rest: posts.filter((post) => !pinned.has(post)) };
|
|
83
88
|
}
|
|
84
89
|
function listBlogPosts(pages, index, options) {
|
|
85
90
|
const sections = pages.filter(isIndex).filter((page) => page.index.scope === "section");
|
|
86
|
-
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);
|
|
87
|
-
if (options.
|
|
88
|
-
const { pinned, rest } =
|
|
91
|
+
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);
|
|
92
|
+
if (options.pinnedFirst) {
|
|
93
|
+
const { pinned, rest } = pinPosts(posts, index.index.pinLimit);
|
|
89
94
|
posts = [...pinned, ...rest];
|
|
90
95
|
}
|
|
91
96
|
return options.applyLimit && index.index.limit ? posts.slice(0, index.index.limit) : posts;
|
|
92
97
|
}
|
|
93
98
|
function findHomeBlog(pages, post, drafts) {
|
|
94
|
-
const listing = pages.filter(isIndex).filter((index) => listBlogPosts(pages, index, { drafts,
|
|
99
|
+
const listing = pages.filter(isIndex).filter((index) => listBlogPosts(pages, index, { drafts, pinnedFirst: false, applyLimit: false }).includes(post));
|
|
95
100
|
const sections = listing.filter((index) => index.index.scope === "section");
|
|
96
101
|
if (sections.length) return deepestFirst(sections, post.relativePath)[0];
|
|
97
102
|
return listing[0] ?? null;
|
|
@@ -207,6 +212,13 @@ function toDateTimeAttribute({ date, hasTime }, timeZone) {
|
|
|
207
212
|
function yearIn(date, timeZone) {
|
|
208
213
|
return Number(toZonedIsoDate(date, timeZone).slice(0, 4));
|
|
209
214
|
}
|
|
215
|
+
function monthIn(date, timeZone) {
|
|
216
|
+
return toZonedIsoDate(date, timeZone).slice(0, 7);
|
|
217
|
+
}
|
|
218
|
+
function formatMonth(month, locale, format) {
|
|
219
|
+
const [year, number] = month.split("-").map(Number);
|
|
220
|
+
return new Intl.DateTimeFormat(locale, { timeZone: "UTC", ...format }).format(Date.UTC(year, number - 1, 1, 12));
|
|
221
|
+
}
|
|
210
222
|
|
|
211
223
|
// src/feed.ts
|
|
212
224
|
var import_node_crypto = require("crypto");
|
|
@@ -258,18 +270,28 @@ var DEFAULT_LABELS = {
|
|
|
258
270
|
older: "Older post",
|
|
259
271
|
allPosts: "All posts in {blog}",
|
|
260
272
|
draft: "Draft",
|
|
261
|
-
|
|
273
|
+
pinned: "Pinned",
|
|
262
274
|
announcement: "Announcement",
|
|
263
275
|
announcements: "Announcements",
|
|
264
|
-
noAnnouncements: "No announcements.",
|
|
265
276
|
announcementCount: "{n} announcements",
|
|
266
277
|
dismiss: "Dismiss",
|
|
267
278
|
noPosts: "No posts yet.",
|
|
268
279
|
undated: "Undated",
|
|
280
|
+
browse: "Browse posts",
|
|
281
|
+
categories: "Categories",
|
|
269
282
|
tags: "Keywords",
|
|
270
|
-
|
|
283
|
+
archive: "Archive",
|
|
284
|
+
category: "Category",
|
|
285
|
+
tag: "Keyword",
|
|
286
|
+
postCount: "{n} posts",
|
|
287
|
+
postCountOne: "{n} post",
|
|
288
|
+
noMatches: "No posts match these filters.",
|
|
289
|
+
removeFilter: "Remove filter: {filter}",
|
|
271
290
|
showAll: "Show all posts",
|
|
272
|
-
|
|
291
|
+
more: "+{n} more",
|
|
292
|
+
less: "Show fewer",
|
|
293
|
+
filter: "Filter {row}",
|
|
294
|
+
noFilterMatches: "Nothing matches.",
|
|
273
295
|
previous: "Previous",
|
|
274
296
|
next: "Next",
|
|
275
297
|
pages: "Pages",
|
|
@@ -301,7 +323,8 @@ function renderRssLink(index, context, show = index.index.rssLink) {
|
|
|
301
323
|
if (!show || !context.feeds || !index.index.feed) return "";
|
|
302
324
|
const title = fill(context.labels.rssFeed, { blog: index.title }).replace(/"/g, """);
|
|
303
325
|
const url = toMarkdownUrl(`/${toFeedPath(index)}`);
|
|
304
|
-
|
|
326
|
+
const text = `<span class="vp-blog-rss__text">${escapeMarkdown(context.labels.subscribe)}</span>`;
|
|
327
|
+
return `<span class="vp-blog-rss">[${RSS_ICON} ${text}](${url} "${title}")</span>`;
|
|
305
328
|
}
|
|
306
329
|
function fill(template, values) {
|
|
307
330
|
return template.replace(/\{(\w+)\}/g, (match, key) => values[key] ?? match);
|
|
@@ -331,27 +354,33 @@ function renderAuthors(authors, context) {
|
|
|
331
354
|
function renderBadge(kind, context) {
|
|
332
355
|
return `<span class="vp-blog-badge vp-blog-badge--${kind}">${escapeTemplateText(context.labels[kind])}</span>`;
|
|
333
356
|
}
|
|
334
|
-
function
|
|
335
|
-
const
|
|
357
|
+
function toFilterUrl(blogRoutePath, param, value) {
|
|
358
|
+
const encoded = encodeURIComponent(value.toLowerCase()).replace(/[()]/g, (c) => c === "(" ? "%28" : "%29");
|
|
336
359
|
const page = blogRoutePath.replace(/(^|\/)index\.md$/, "$1");
|
|
337
|
-
return `${toMarkdownUrl(`/${page}`)}
|
|
360
|
+
return `${toMarkdownUrl(`/${page}`)}?${param}=${encoded}`;
|
|
361
|
+
}
|
|
362
|
+
function renderFilterLink(text, param, blogRoutePath, className) {
|
|
363
|
+
return blogRoutePath ? `[${escapeMarkdown(text)}](${toFilterUrl(blogRoutePath, param, text)})` : `<span class="${className}">${escapeTemplateText(text)}</span>`;
|
|
338
364
|
}
|
|
339
365
|
function renderTags(tags, blogRoutePath) {
|
|
340
|
-
return tags.map(
|
|
341
|
-
(tag) => blogRoutePath ? `[${escapeMarkdown(tag)}](${toTagUrl(blogRoutePath, tag)})` : `<span class="vp-blog-tag">${escapeTemplateText(tag)}</span>`
|
|
342
|
-
).join(" ");
|
|
366
|
+
return tags.map((tag) => renderFilterLink(tag, "tag", blogRoutePath, "vp-blog-tag")).join(" ");
|
|
343
367
|
}
|
|
344
368
|
function toTagsAttribute(post) {
|
|
345
369
|
return escapeHtml(post.post.tags.map((tag) => tag.toLowerCase()).join("|"));
|
|
346
370
|
}
|
|
347
|
-
function toItemAttributes(post) {
|
|
348
|
-
|
|
371
|
+
function toItemAttributes(post, context) {
|
|
372
|
+
const { category, date, announcement } = post.post;
|
|
373
|
+
const attributes = [`data-vp-blog-item data-tags="${toTagsAttribute(post)}"`];
|
|
374
|
+
if (category) attributes.push(`data-category="${escapeHtml(category.toLowerCase())}"`);
|
|
375
|
+
if (date) attributes.push(`data-month="${monthIn(date, context.dates.timeZone)}"`);
|
|
376
|
+
if (announcement) attributes.push("data-announcement");
|
|
377
|
+
return attributes.join(" ");
|
|
349
378
|
}
|
|
350
379
|
function renderBadges(post, context) {
|
|
351
|
-
const { draft,
|
|
380
|
+
const { draft, pinned, announcement } = post.post;
|
|
352
381
|
return [
|
|
353
382
|
draft && renderBadge("draft", context),
|
|
354
|
-
|
|
383
|
+
pinned && renderBadge("pinned", context),
|
|
355
384
|
announcement && renderBadge("announcement", context)
|
|
356
385
|
].filter(Boolean).join(" ");
|
|
357
386
|
}
|
|
@@ -386,7 +415,7 @@ function renderCard(post, context, index) {
|
|
|
386
415
|
const link = `/${post.routePath}`;
|
|
387
416
|
const image = toImageSource(post, context.pageDirectory);
|
|
388
417
|
const modifiers = [
|
|
389
|
-
post.post.
|
|
418
|
+
post.post.pinned && "pinned",
|
|
390
419
|
post.post.draft && "draft",
|
|
391
420
|
post.post.announcement && "announcement",
|
|
392
421
|
image && "with-image",
|
|
@@ -394,9 +423,10 @@ function renderCard(post, context, index) {
|
|
|
394
423
|
].filter(Boolean).map((modifier) => ` vp-blog-card--${modifier}`).join("");
|
|
395
424
|
const alt = escapeMarkdown(post.post.imageAlt);
|
|
396
425
|
return [
|
|
397
|
-
`<article class="vp-blog-card${modifiers}"${imageStyle(post)} ${toItemAttributes(post)}>`,
|
|
426
|
+
`<article class="vp-blog-card${modifiers}"${imageStyle(post)} ${toItemAttributes(post, context)}>`,
|
|
398
427
|
...image ? block("vp-blog-card__image", `[})](${toMarkdownUrl(link)})`) : [],
|
|
399
428
|
'<div class="vp-blog-card__body">',
|
|
429
|
+
...index.index.showCategory && post.post.category ? block("vp-blog-card__category", renderFilterLink(post.post.category, "category", index.routePath, "")) : [],
|
|
400
430
|
...block("vp-blog-card__title", toPageLink(post.post.title, post.routePath)),
|
|
401
431
|
...block("vp-blog-card__meta", renderMeta(post, context, { authors: true, updated: false, badges: true })),
|
|
402
432
|
...block("vp-blog-card__summary", escapeMarkdown(post.post.summary)),
|
|
@@ -408,7 +438,7 @@ function renderCard(post, context, index) {
|
|
|
408
438
|
function renderCompactItem(post, context) {
|
|
409
439
|
const date = post.post.date ? `${renderTime(post.post.date, post.post.dateHasTime, context)} ` : "";
|
|
410
440
|
const badges = renderBadges(post, context);
|
|
411
|
-
const marker = `<span hidden ${toItemAttributes(post)}></span>`;
|
|
441
|
+
const marker = `<span hidden ${toItemAttributes(post, context)}></span>`;
|
|
412
442
|
return `- ${marker}${date}${toPageLink(post.post.title, post.routePath)}${badges ? ` ${badges}` : ""}`;
|
|
413
443
|
}
|
|
414
444
|
function renderGroup(posts, index, context) {
|
|
@@ -428,18 +458,23 @@ function renderGroup(posts, index, context) {
|
|
|
428
458
|
"</div>"
|
|
429
459
|
];
|
|
430
460
|
}
|
|
431
|
-
function
|
|
432
|
-
const
|
|
461
|
+
function renderGroups(posts, index, context) {
|
|
462
|
+
const { groupBy } = index.index;
|
|
463
|
+
if (!groupBy) return renderGroup(posts, index, context);
|
|
464
|
+
const { timeZone, locale } = context.dates;
|
|
465
|
+
const groups = /* @__PURE__ */ new Map();
|
|
433
466
|
for (const post of posts) {
|
|
434
|
-
const
|
|
435
|
-
|
|
467
|
+
const { date } = post.post;
|
|
468
|
+
const key = !date ? "" : groupBy === "year" ? String(yearIn(date, timeZone)) : monthIn(date, timeZone);
|
|
469
|
+
groups.set(key, [...groups.get(key) ?? [], post]);
|
|
436
470
|
}
|
|
437
471
|
const lines = [];
|
|
438
|
-
for (const [
|
|
472
|
+
for (const [key, group] of groups) {
|
|
473
|
+
const heading = !key ? context.labels.undated : groupBy === "year" ? key : formatMonth(key, locale, { month: "long", year: "numeric" });
|
|
439
474
|
lines.push(
|
|
440
|
-
|
|
475
|
+
`<section class="vp-blog-group vp-blog-${groupBy}">`,
|
|
441
476
|
"",
|
|
442
|
-
`## ${escapeMarkdown(
|
|
477
|
+
`## ${escapeMarkdown(heading)}`,
|
|
443
478
|
"",
|
|
444
479
|
...renderGroup(group, index, context),
|
|
445
480
|
"",
|
|
@@ -448,29 +483,107 @@ function renderYears(posts, index, context) {
|
|
|
448
483
|
}
|
|
449
484
|
return lines;
|
|
450
485
|
}
|
|
451
|
-
function
|
|
452
|
-
const
|
|
486
|
+
function collectValues(posts, pick) {
|
|
487
|
+
const values = /* @__PURE__ */ new Map();
|
|
453
488
|
for (const post of posts) {
|
|
454
|
-
for (const
|
|
455
|
-
const entry =
|
|
489
|
+
for (const value of new Set(pick(post))) {
|
|
490
|
+
const entry = values.get(value.toLowerCase()) ?? { value, count: 0 };
|
|
456
491
|
entry.count++;
|
|
457
|
-
|
|
492
|
+
values.set(value.toLowerCase(), entry);
|
|
458
493
|
}
|
|
459
494
|
}
|
|
460
|
-
return [...
|
|
495
|
+
return [...values.values()].sort((a, b) => a.value.localeCompare(b.value, void 0, { sensitivity: "base" }));
|
|
461
496
|
}
|
|
462
|
-
function
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
497
|
+
function collectTags(posts) {
|
|
498
|
+
return collectValues(posts, (post) => post.post.tags).map(({ value, count }) => ({ tag: value, count }));
|
|
499
|
+
}
|
|
500
|
+
function collectCategories(posts) {
|
|
501
|
+
return collectValues(posts, (post) => post.post.category ? [post.post.category] : []);
|
|
502
|
+
}
|
|
503
|
+
function collectArchive(posts, timeZone) {
|
|
504
|
+
const years = /* @__PURE__ */ new Map();
|
|
505
|
+
for (const post of posts) {
|
|
506
|
+
if (!post.post.date) continue;
|
|
507
|
+
const month = monthIn(post.post.date, timeZone);
|
|
508
|
+
const year = years.get(month.slice(0, 4)) ?? { value: month.slice(0, 4), count: 0, months: [] };
|
|
509
|
+
year.count++;
|
|
510
|
+
const entry = year.months.find((candidate) => candidate.value === month);
|
|
511
|
+
if (entry) entry.count++;
|
|
512
|
+
else year.months.push({ value: month, count: 1 });
|
|
513
|
+
years.set(year.value, year);
|
|
514
|
+
}
|
|
515
|
+
const newestFirst = (a, b) => b.value.localeCompare(a.value);
|
|
516
|
+
return [...years.values()].sort(newestFirst).map((year) => ({ ...year, months: [...year.months].sort(newestFirst) }));
|
|
517
|
+
}
|
|
518
|
+
function renderChip(name, count, url, title = "") {
|
|
519
|
+
const tooltip = title ? ` "${title.replace(/"/g, """)}"` : "";
|
|
520
|
+
return `[<span class="vp-blog-facet__name">${escapeMarkdown(name)}</span> <span class="vp-blog-facet__count">${count}</span>](${url}${tooltip})`;
|
|
521
|
+
}
|
|
522
|
+
function renderFacet(row) {
|
|
523
|
+
const { facet, label, chips, ariaLabel = label, limit = 0, attributes = "", aside = "" } = row;
|
|
524
|
+
const limited = limit && chips.length > limit ? ` data-limit="${limit}"` : "";
|
|
469
525
|
return [
|
|
470
|
-
`<
|
|
526
|
+
`<div class="vp-blog-facet vp-blog-facet--${facet}" role="group" aria-label="${escapeHtml(ariaLabel)}" data-vp-blog-facet="${facet}"${limited}${attributes}>`,
|
|
527
|
+
`<span class="vp-blog-facet__label" aria-hidden="true">${escapeTemplateText(label)}</span>`,
|
|
528
|
+
'<div class="vp-blog-facet__chips">',
|
|
471
529
|
"",
|
|
472
|
-
|
|
530
|
+
chips.join(" "),
|
|
473
531
|
"",
|
|
532
|
+
"</div>",
|
|
533
|
+
...block("vp-blog-facet__aside", aside),
|
|
534
|
+
"</div>"
|
|
535
|
+
];
|
|
536
|
+
}
|
|
537
|
+
function canNarrow(values, total) {
|
|
538
|
+
return values.length > 1 || values.length === 1 && values[0].count < total;
|
|
539
|
+
}
|
|
540
|
+
function byUse(a, b) {
|
|
541
|
+
return b.count - a.count || a.value.localeCompare(b.value, void 0, { sensitivity: "base" });
|
|
542
|
+
}
|
|
543
|
+
function renderBrowse(posts, index, context) {
|
|
544
|
+
const { labels } = context;
|
|
545
|
+
const { locale, timeZone } = context.dates;
|
|
546
|
+
const { categoryList, categoryLimit, tagList, tagLimit, tagMinCount, archive: showArchive } = index.index;
|
|
547
|
+
const blog = index.routePath;
|
|
548
|
+
const rows = [];
|
|
549
|
+
const categories = categoryList ? collectCategories(posts).sort(byUse) : [];
|
|
550
|
+
if (canNarrow(categories, posts.length)) {
|
|
551
|
+
const chips = categories.map(({ value, count }) => renderChip(value, count, toFilterUrl(blog, "category", value)));
|
|
552
|
+
rows.push({ facet: "category", label: labels.categories, chips, limit: categoryLimit });
|
|
553
|
+
}
|
|
554
|
+
const tags = tagList ? collectTags(posts).map(({ tag, count }) => ({ value: tag, count })).filter(({ count }) => count >= tagMinCount).sort(byUse) : [];
|
|
555
|
+
if (canNarrow(tags, posts.length)) {
|
|
556
|
+
const chips = tags.map(({ value, count }) => renderChip(value, count, toFilterUrl(blog, "tag", value)));
|
|
557
|
+
rows.push({ facet: "tag", label: labels.tags, chips, limit: tagLimit });
|
|
558
|
+
}
|
|
559
|
+
const archive = showArchive ? collectArchive(posts, timeZone) : [];
|
|
560
|
+
const monthChips = (months) => months.map(
|
|
561
|
+
({ value, count }) => renderChip(
|
|
562
|
+
formatMonth(value, locale, { month: "short" }),
|
|
563
|
+
count,
|
|
564
|
+
toFilterUrl(blog, "month", value),
|
|
565
|
+
formatMonth(value, locale, { month: "long", year: "numeric" })
|
|
566
|
+
)
|
|
567
|
+
);
|
|
568
|
+
if (archive.length === 1 && canNarrow(archive[0].months, posts.length)) {
|
|
569
|
+
const [year] = archive;
|
|
570
|
+
const ariaLabel = `${labels.archive} ${year.value}`;
|
|
571
|
+
const attributes = ` data-year="${year.value}"`;
|
|
572
|
+
rows.push({ facet: "month", label: labels.archive, chips: monthChips(year.months), ariaLabel, attributes });
|
|
573
|
+
} else if (archive.length > 1) {
|
|
574
|
+
const years = archive.map(({ value, count }) => renderChip(value, count, toFilterUrl(blog, "year", value)));
|
|
575
|
+
rows.push({ facet: "year", label: labels.archive, chips: years });
|
|
576
|
+
for (const year of archive) {
|
|
577
|
+
const ariaLabel = `${labels.archive} ${year.value}`;
|
|
578
|
+
const attributes = ` data-year="${year.value}" hidden`;
|
|
579
|
+
rows.push({ facet: "month", label: year.value, chips: monthChips(year.months), ariaLabel, attributes });
|
|
580
|
+
}
|
|
581
|
+
}
|
|
582
|
+
if (rows.length === 0) return [];
|
|
583
|
+
rows[0].aside = renderRssLink(index, context);
|
|
584
|
+
return [
|
|
585
|
+
`<nav class="vp-blog-browse" aria-label="${escapeHtml(labels.browse)}">`,
|
|
586
|
+
...rows.flatMap(renderFacet),
|
|
474
587
|
"</nav>"
|
|
475
588
|
];
|
|
476
589
|
}
|
|
@@ -478,14 +591,23 @@ function renderClientAttributes(index, context) {
|
|
|
478
591
|
const { labels } = context;
|
|
479
592
|
const attributes = {
|
|
480
593
|
"data-page-size": String(index.index.pageSize),
|
|
481
|
-
"
|
|
594
|
+
// For writing a `?month=` filter's name: "October 2026".
|
|
595
|
+
"data-locale": context.locale,
|
|
482
596
|
"data-label-show-all": labels.showAll,
|
|
483
|
-
"data-label-no-tagged": labels.noTagged,
|
|
484
597
|
"data-label-previous": labels.previous,
|
|
485
598
|
"data-label-next": labels.next,
|
|
486
599
|
"data-label-pages": labels.pages,
|
|
487
600
|
"data-label-announcements": labels.announcements,
|
|
488
|
-
"data-label-
|
|
601
|
+
"data-label-category": labels.category,
|
|
602
|
+
"data-label-tag": labels.tag,
|
|
603
|
+
"data-label-post-count": labels.postCount,
|
|
604
|
+
"data-label-post-count-one": labels.postCountOne,
|
|
605
|
+
"data-label-no-matches": labels.noMatches,
|
|
606
|
+
"data-label-remove-filter": labels.removeFilter,
|
|
607
|
+
"data-label-more": labels.more,
|
|
608
|
+
"data-label-less": labels.less,
|
|
609
|
+
"data-label-filter": labels.filter,
|
|
610
|
+
"data-label-no-filter-matches": labels.noFilterMatches
|
|
489
611
|
};
|
|
490
612
|
return Object.entries(attributes).map(([name, value]) => `${name}="${escapeHtml(value)}"`).join(" ");
|
|
491
613
|
}
|
|
@@ -493,20 +615,19 @@ function renderPostList(pinned, rest, index, context) {
|
|
|
493
615
|
const all = [...pinned, ...rest];
|
|
494
616
|
if (all.length === 0) return `<p class="vp-blog-empty">${escapeTemplateText(context.labels.noPosts)}</p>
|
|
495
617
|
`;
|
|
618
|
+
const browse = renderBrowse(all, index, context);
|
|
496
619
|
const lines = [
|
|
497
620
|
`<div class="vp-blog" data-vp-blog ${renderClientAttributes(index, context)}>`,
|
|
498
|
-
...block("vp-blog-subscribe", renderRssLink(index, context)),
|
|
499
|
-
|
|
500
|
-
'<div class="vp-blog-filter" data-vp-blog-filter hidden></div>'
|
|
621
|
+
...browse.length ? browse : block("vp-blog-subscribe", renderRssLink(index, context)),
|
|
622
|
+
'<div class="vp-blog-filter" data-vp-blog-filter role="status" hidden></div>'
|
|
501
623
|
];
|
|
502
624
|
if (pinned.length)
|
|
503
625
|
lines.push(
|
|
504
|
-
'<section class="vp-blog-
|
|
626
|
+
'<section class="vp-blog-pinned" data-vp-blog-pinned>',
|
|
505
627
|
...renderGroup(pinned, index, context),
|
|
506
628
|
"</section>"
|
|
507
629
|
);
|
|
508
|
-
if (rest.length)
|
|
509
|
-
lines.push(...index.index.groupByYear ? renderYears(rest, index, context) : renderGroup(rest, index, context));
|
|
630
|
+
if (rest.length) lines.push(...renderGroups(rest, index, context));
|
|
510
631
|
lines.push(`<nav class="vp-blog-pagination" data-vp-blog-pagination hidden></nav>`, "</div>");
|
|
511
632
|
return `${lines.join("\n")}
|
|
512
633
|
`;
|
|
@@ -515,8 +636,12 @@ function renderPostHeader(post, context, show, blogRoutePath = null) {
|
|
|
515
636
|
const lines = [];
|
|
516
637
|
if (show.byline) {
|
|
517
638
|
const meta = renderMeta(post, context, { authors: true, updated: true, badges: true });
|
|
518
|
-
const
|
|
519
|
-
|
|
639
|
+
const { category, tags } = post.post;
|
|
640
|
+
const chips = [
|
|
641
|
+
category && `<span class="vp-blog-byline__category">${renderFilterLink(category, "category", blogRoutePath, "vp-blog-category")}</span>`,
|
|
642
|
+
tags.length && `<span class="vp-blog-byline__tags">${renderTags(tags, blogRoutePath)}</span>`
|
|
643
|
+
].filter(Boolean).join(" ");
|
|
644
|
+
lines.push(...block("vp-blog-byline", [meta, chips].filter(Boolean).join("\n\n")));
|
|
520
645
|
}
|
|
521
646
|
if (show.cover && post.post.image) {
|
|
522
647
|
const image = /^[./@~]/.test(post.post.image) || isExternalUrl(post.post.image) ? post.post.image : `./${post.post.image}`;
|
|
@@ -559,6 +684,8 @@ var FENCED_CODE = /^[ \t]*(`{3,}|~{3,})[^\n]*\n[\s\S]*?(?:^[ \t]*\1[`~]*[ \t]*$|
|
|
|
559
684
|
var HEADING_1 = /^ {0,3}#[ \t]+(.*?)(?:[ \t]+#+)?[ \t]*$/m;
|
|
560
685
|
var TAG_SYNTAX = /\(\(<?tag([|/])[^|/)\r\n]*(?:\1[^|/)\r\n]*){0,2}\)\)/g;
|
|
561
686
|
var MAX_SUMMARY_LENGTH = 200;
|
|
687
|
+
var DEFAULT_CATEGORY_LIMIT = 8;
|
|
688
|
+
var DEFAULT_TAG_LIMIT = 12;
|
|
562
689
|
function readFrontmatter(raw) {
|
|
563
690
|
const source = raw.replace(/^/, "");
|
|
564
691
|
const match = FRONTMATTER.exec(source);
|
|
@@ -672,7 +799,8 @@ function resolvePostData(data, content, fallbackTitle, options = {}) {
|
|
|
672
799
|
tags: toStringList(pick("tags")),
|
|
673
800
|
category: toText(pick("category")),
|
|
674
801
|
scope: toScope(post.scope),
|
|
675
|
-
|
|
802
|
+
// `sticky` is this setting's old name.
|
|
803
|
+
pinned: (post.pinned ?? post.sticky) === true,
|
|
676
804
|
draft: post.draft === true,
|
|
677
805
|
announcement: post.announcement === true || !!announcementText,
|
|
678
806
|
announcementText,
|
|
@@ -685,6 +813,9 @@ function resolvePostData(data, content, fallbackTitle, options = {}) {
|
|
|
685
813
|
function toLayout(value) {
|
|
686
814
|
return value === "grid" || value === "compact" ? value : "list";
|
|
687
815
|
}
|
|
816
|
+
function toGroupBy(value) {
|
|
817
|
+
return value === "year" || value === "month" ? value : "";
|
|
818
|
+
}
|
|
688
819
|
function defaultFeedPath(relativePath) {
|
|
689
820
|
const name = import_node_path3.default.posix.basename(relativePath, ".md");
|
|
690
821
|
return name === "index" ? "feed.xml" : `${name}-feed.xml`;
|
|
@@ -718,13 +849,20 @@ function resolveIndexConfig(data, relativePath, defaults = {}) {
|
|
|
718
849
|
scope,
|
|
719
850
|
folders: folders.length ? resolveFolders(folders, relativePath) : scope === "section" ? [ownFolder] : [],
|
|
720
851
|
tags: toStringList(options.tags),
|
|
852
|
+
categories: toStringList(options.categories),
|
|
721
853
|
limit: typeof options.limit === "number" && options.limit > 0 ? Math.floor(options.limit) : 0,
|
|
722
854
|
layout: toLayout(options.layout),
|
|
723
|
-
|
|
855
|
+
groupBy: toGroupBy(options.groupBy),
|
|
724
856
|
showTags: options.showTags !== false,
|
|
857
|
+
showCategory: options.showCategory !== false,
|
|
858
|
+
categoryList: options.categoryList !== false,
|
|
859
|
+
categoryLimit: toCount(options.categoryLimit, DEFAULT_CATEGORY_LIMIT),
|
|
725
860
|
tagList: options.tagList !== false,
|
|
861
|
+
tagLimit: toCount(options.tagLimit, DEFAULT_TAG_LIMIT),
|
|
862
|
+
tagMinCount: toCount(options.tagMinCount, 1),
|
|
863
|
+
archive: options.archive !== false,
|
|
726
864
|
pageSize: toCount(options.pageSize, toCount(defaults.pageSize, 10)),
|
|
727
|
-
|
|
865
|
+
pinLimit: toCount(options.pinLimit ?? options.stickyLimit, toCount(defaults.pinLimit ?? defaults.stickyLimit, 0)),
|
|
728
866
|
rssLink: typeof options.rssLink === "boolean" ? options.rssLink : defaults.rssLink ?? true,
|
|
729
867
|
feed: toFeed(options.feed, relativePath)
|
|
730
868
|
};
|
|
@@ -791,7 +929,7 @@ function toOptionsKey(options) {
|
|
|
791
929
|
options.wordsPerMinute,
|
|
792
930
|
options.stripTags,
|
|
793
931
|
options.pageSize,
|
|
794
|
-
options.stickyLimit,
|
|
932
|
+
options.pinLimit ?? options.stickyLimit,
|
|
795
933
|
options.rssLink,
|
|
796
934
|
options.timeZone
|
|
797
935
|
]);
|
|
@@ -871,10 +1009,13 @@ function resolveWidgetConfig(body) {
|
|
|
871
1009
|
config: {
|
|
872
1010
|
blog: typeof data.blog === "string" ? data.blog.trim() : "",
|
|
873
1011
|
limit: toCount2(data.limit) ?? DEFAULT_LIMIT,
|
|
874
|
-
stickyLimit
|
|
1012
|
+
// `stickyLimit` is this setting's old name.
|
|
1013
|
+
pinLimit: toCount2(data.pinLimit ?? data.stickyLimit),
|
|
875
1014
|
layout,
|
|
876
1015
|
showTags: typeof data.showTags === "boolean" ? data.showTags : void 0,
|
|
1016
|
+
showCategory: typeof data.showCategory === "boolean" ? data.showCategory : void 0,
|
|
877
1017
|
tags: toStringList(data.tags),
|
|
1018
|
+
categories: toStringList(data.categories),
|
|
878
1019
|
more: data.more === false ? null : typeof data.more === "string" && data.more.trim() ? data.more.trim() : "",
|
|
879
1020
|
rssLink: typeof data.rssLink === "boolean" ? data.rssLink : void 0
|
|
880
1021
|
},
|
|
@@ -890,19 +1031,19 @@ function renderWidget(body, environment) {
|
|
|
890
1031
|
warn(config.blog ? `no blog page found at "${config.blog}"` : 'no blog covers this page: name one with "blog:"');
|
|
891
1032
|
return "";
|
|
892
1033
|
}
|
|
893
|
-
const
|
|
894
|
-
const
|
|
895
|
-
|
|
896
|
-
(post) => wanted.size === 0 || post.post.tags.some((tag) => wanted.has(tag.toLowerCase()))
|
|
1034
|
+
const pinLimit = config.pinLimit ?? blog.index.pinLimit;
|
|
1035
|
+
const posts = listBlogPosts(pages, blog, { drafts, pinnedFirst: false, applyLimit: false }).filter(
|
|
1036
|
+
(post) => hasAnyTag(post, config.tags) && inAnyCategory(post, config.categories)
|
|
897
1037
|
);
|
|
898
|
-
const { pinned, rest } =
|
|
1038
|
+
const { pinned, rest } = pinPosts(posts, pinLimit);
|
|
899
1039
|
const shown = [...pinned, ...rest].slice(0, config.limit || void 0);
|
|
900
1040
|
const view = {
|
|
901
1041
|
...blog,
|
|
902
1042
|
index: {
|
|
903
1043
|
...blog.index,
|
|
904
1044
|
layout: config.layout ?? blog.index.layout,
|
|
905
|
-
showTags: config.showTags ?? blog.index.showTags
|
|
1045
|
+
showTags: config.showTags ?? blog.index.showTags,
|
|
1046
|
+
showCategory: config.showCategory ?? blog.index.showCategory
|
|
906
1047
|
}
|
|
907
1048
|
};
|
|
908
1049
|
const lines = [`<div class="vp-blog-widget">`];
|
|
@@ -975,8 +1116,8 @@ function hasFeeds(options) {
|
|
|
975
1116
|
}
|
|
976
1117
|
function withPostList(source, page, pages, options) {
|
|
977
1118
|
if (!isIndex(page)) return source;
|
|
978
|
-
const posts = listBlogPosts(pages, page, { drafts: options.drafts ?? false,
|
|
979
|
-
const { pinned, rest } =
|
|
1119
|
+
const posts = listBlogPosts(pages, page, { drafts: options.drafts ?? false, pinnedFirst: true, applyLimit: true });
|
|
1120
|
+
const { pinned, rest } = pinPosts(posts, page.index.pinLimit);
|
|
980
1121
|
const context = createRenderContext(options, import_node_path6.default.dirname(page.absolutePath), hasFeeds(options));
|
|
981
1122
|
const generated = renderPostList(pinned, rest, page, context);
|
|
982
1123
|
return source.includes(BLOG_POSTS_PLACEHOLDER) ? source.replace(BLOG_POSTS_PLACEHOLDER, () => `
|
|
@@ -999,7 +1140,7 @@ function withPostDecorations(source, page, pages, options) {
|
|
|
999
1140
|
let result = header ? insertAfterTitle(source, header) : source;
|
|
1000
1141
|
if (page.post.postNav ?? options.postNav ?? true) {
|
|
1001
1142
|
if (blog) {
|
|
1002
|
-
const posts = listBlogPosts(pages, blog, { drafts,
|
|
1143
|
+
const posts = listBlogPosts(pages, blog, { drafts, pinnedFirst: false, applyLimit: false });
|
|
1003
1144
|
result = `${result.replace(/\s*$/, "")}
|
|
1004
1145
|
|
|
1005
1146
|
${renderPostNav(page, posts, blog, context)}
|