diff --git a/src/components/Icon.astro b/src/components/Icon.astro
index c6dbf169..a1698473 100644
--- a/src/components/Icon.astro
+++ b/src/components/Icon.astro
@@ -10,6 +10,7 @@ interface Props {
| "rss"
| "github"
| "email"
+ | "wechat"
| "bilibili";
size?: number;
}
@@ -26,6 +27,8 @@ const paths = {
"M9 19c-4 1-4-2-6-2m12 5v-3.4a3 3 0 0 0-.8-2.3c2.7-.3 5.5-1.3 5.5-6A4.7 4.7 0 0 0 18.4 7a4.3 4.3 0 0 0-.1-3.2s-1-.3-3.3 1.2a11.6 11.6 0 0 0-6 0C6.7 3.5 5.7 3.8 5.7 3.8A4.3 4.3 0 0 0 5.6 7a4.7 4.7 0 0 0-1.3 3.3c0 4.7 2.8 5.7 5.5 6a3 3 0 0 0-.8 2.3V22",
email:
"M3 7l9 6 9-6M5 5h14a2 2 0 0 1 2 2v10a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V7a2 2 0 0 1 2-2Z",
+ wechat:
+ "M14 13.5c0 3-2.7 5.5-6 5.5H5l-3 2 1-3c-1.2-1-2-2.6-2-4.5C1 10.5 3.7 8 7 8s7 2.5 7 5.5ZM11 8c.6-2.3 2.9-4 5.5-4C20.1 4 23 6.5 23 9.5c0 1.6-.7 3.1-1.9 4.1L22 17l-3-2h-2",
bilibili:
"m8 3 3 3m5-3-3 3M5 6h14a2 2 0 0 1 2 2v10a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2ZM8 11v3m8-3v3m-6 3h4",
};
diff --git a/src/components/SocialLinks.astro b/src/components/SocialLinks.astro
index 03156001..95927cd1 100644
--- a/src/components/SocialLinks.astro
+++ b/src/components/SocialLinks.astro
@@ -2,6 +2,7 @@
import Icon from "./Icon.astro";
import { site } from "../config/site";
import { ui, type Locale } from "../i18n";
+import { localePath, normalizeBase } from "../lib/paths.mjs";
interface Props {
locale?: Locale;
@@ -10,6 +11,7 @@ interface Props {
}
const { locale = "zh", variant = "icons", class: className } = Astro.props;
const t = ui(locale);
+const wechatHref = `${localePath("contact", "zh", normalizeBase(import.meta.env.BASE_URL))}#wechat`;
---
+ {locale === "zh" && (
+
+
+ 公众号
+
+ )}
{Object.values(site.socials).map((social) => (
+
+
微信公众号
+
在微信里,继续阅读。
+
关注「{site.wechat.name}」,一起交流技术与思考。
+
用微信扫一扫;手机上可长按识别,或保存二维码后从微信相册扫码。
+
+
+

+
保存二维码
+
{site.wechat.name}
+
+
+ 也可在微信「搜一搜 → 公众号」中搜索名称。
+
+
+
+
+
+
+
diff --git a/src/config/site.ts b/src/config/site.ts
index c592f015..e8d9e65c 100644
--- a/src/config/site.ts
+++ b/src/config/site.ts
@@ -5,6 +5,16 @@ export const site = {
displayName: { zh: "泯泷 Yihui", en: "Yihui" },
},
repositoryUrl: "https://github.com/MoMeak9/MyBlogDoc",
+ wechat: {
+ name: "泯泷ML",
+ qrImage: "images/wechat-ml-qr.jpg",
+ },
+ comments: {
+ repo: "MoMeak9/MyBlogDoc",
+ repoId: "R_kgDOG_ZV7A",
+ category: "Announcements",
+ categoryId: "DIC_kwDOG_ZV7M4DHZRJ",
+ },
socials: {
github: {
icon: "github",
diff --git a/tests/build.test.mjs b/tests/build.test.mjs
index 571519d6..c19d0a17 100644
--- a/tests/build.test.mjs
+++ b/tests/build.test.mjs
@@ -165,6 +165,60 @@ function locations(xml) {
);
}
+test("WeChat follow cards serve the original QR under the deployment base and only in Chinese", () => {
+ const qrPath = "images/wechat-ml-qr.jpg";
+ assert.deepEqual(
+ readFileSync(join(dist, qrPath)),
+ readFileSync(join(root, "public", qrPath)),
+ "The QR image must be published without modification.",
+ );
+ for (const page of regularPages) {
+ const html = artifact(page.file);
+ const qrTags = [...html.matchAll(/
]*>/g)]
+ .map(([tag]) => attributes(tag))
+ .filter((attrs) => attrs.class?.includes("wechat-qr"));
+ if (page.locale === "en") {
+ assert.equal(qrTags.length, 0, page.file);
+ assert.ok(!html.includes('class="social-link social-link--wechat"'), page.file);
+ } else if (["", "contact"].includes(page.route) || page.id) {
+ assert.equal(qrTags.length, 1, page.file);
+ assert.equal(qrTags[0].src, `${base}${qrPath}`, page.file);
+ assert.equal(qrTags[0].width, "430");
+ assert.equal(qrTags[0].height, "430");
+ assert.ok(html.includes('download="泯泷ML-公众号二维码.jpg"'), page.file);
+ }
+ }
+});
+
+test("public article comments share a discussion identity across locales and remain outside indexed prose", () => {
+ for (const article of contentIndex().articles) {
+ for (const locale of ["zh", "en"]) {
+ const file = join(locale === "en" ? "en" : "", "posts", article.id, "index.html");
+ const html = artifact(file);
+ const sections = [...html.matchAll(/]*>/g)]
+ .map(([tag]) => ({ tag, attrs: attributes(tag) }))
+ .filter(({ attrs }) => attrs.id === "comments");
+ assert.equal(sections.length, 1, file);
+ const { tag, attrs } = sections[0];
+ assert.equal(attrs["data-term"], article.id, file);
+ assert.equal(attrs["data-lang"], locale === "en" ? "en" : "zh-CN", file);
+ assert.equal(attrs["data-repo"], "MoMeak9/MyBlogDoc", file);
+ assert.match(tag, /\bdata-pagefind-ignore\b/, file);
+ if (attrs["data-comments-enabled"] === "true") {
+ assert.ok(attrs["data-repo-id"] && attrs["data-category-id"], file);
+ } else {
+ assert.ok(!html.includes("data-load-comments"), file);
+ }
+ const prose = html.match(/]*class="prose"[\s\S]*?<\/article>/)?.[0];
+ assert.ok(prose, file);
+ assert.ok(!prose.includes('id="comments"'), file);
+ }
+ }
+ for (const path of ["index.html", "contact/index.html", "en/index.html", "en/contact/index.html"]) {
+ assert.ok(!artifact(path).includes('id="comments"'), path);
+ }
+});
+
test("every Markdown article has both locale pages and a real legacy HTML entry", (t) => {
assert.ok(
notes.includes("README.md") && notes.includes("个人简介.md"),