From b8e3b2f52a400d544cf19d9631d42316462ab433 Mon Sep 17 00:00:00 2001 From: Dustella Date: Sun, 27 Oct 2024 00:43:36 +0800 Subject: [PATCH] feat: add read time and word count --- src/.vitepress/config.ts | 62 +++++++++++++++++++++- src/.vitepress/env.d.ts | 16 +++--- src/.vitepress/theme/Layout.vue | 15 ++++-- src/.vitepress/theme/PageInfo.vue | 78 ++++++++++++++++++++++++++++ src/.vitepress/theme/ReadingTime.vue | 43 +++++++++++++++ 5 files changed, 202 insertions(+), 12 deletions(-) create mode 100644 src/.vitepress/theme/PageInfo.vue create mode 100644 src/.vitepress/theme/ReadingTime.vue diff --git a/src/.vitepress/config.ts b/src/.vitepress/config.ts index e8720fb..d5b9915 100644 --- a/src/.vitepress/config.ts +++ b/src/.vitepress/config.ts @@ -1,9 +1,43 @@ import genConfig from "@project-trans/vitepress-theme-project-trans/config"; import type { ThemeContext } from "@project-trans/vitepress-theme-project-trans/utils"; import { withThemeContext } from "@project-trans/vitepress-theme-project-trans/utils"; +import path from "path"; import type { DefaultTheme } from "vitepress"; +import fs from "fs"; type NavConfig = DefaultTheme.Config["nav"]; +function countWords(content: string): number { + const cleanedContent = content + .replace(/```[\s\S]*?```/g, "") // 移除代码块 + .replace(/!\[.*?\]\(.*?\)/g, "") // 移除图片链接 + .replace(/\[.*?\]\(.*?\)/g, "") // 移除普通链接 + .replace(/<[^>]+(>|$)/g, "") // 移除 HTML 标签 + .replace(/[.,/#!$%^&*;:{}=\-_`~()]/g, "") // 移除标点符号 + .replace(/\s+/g, " ") // 将多余的空格归为一个空格 + .trim(); // 去除首尾空格 + + const chineseCharacters = + cleanedContent.match(/[\u4E00-\u9FFF\uFF01-\uFFE5]/g) || []; + const words = cleanedContent.split(/\s+/).filter(Boolean); + + return chineseCharacters.length + words.length; +} + +function readMarkdownFileContent(filePath: string): string { + if (fs.existsSync(filePath)) { + return fs.readFileSync(filePath, "utf-8"); + } + return ""; +} + +function searchForFirstTitle(filePath: string): string { + const content = readMarkdownFileContent(filePath); + const title = content.match(/# (.*)/); + if (title) { + return title[1]; + } + return ""; +} const nav = [ { text: "正文", link: "/foreword" }, @@ -41,6 +75,7 @@ const sidebarOptions = [ const themeConfig: ThemeContext = { siteTitle: "药娘的天空", + SiteTitle: "药娘的天空", siteDescription: "一个 2000 年代的跨性别者的故事。", siteLogo: "/progynova.png", /** Repo */ @@ -58,4 +93,29 @@ const themeConfig: ThemeContext = { }; // https://vitepress.dev/reference/site-config -export default withThemeContext(themeConfig, genConfig); +export default withThemeContext(themeConfig, () => { + const config = genConfig(); + return { + ...config, + transformPageData(pageData) { + // 构建 Markdown 文件路径 + const markdownFile = `${pageData.relativePath}`; + const filePath = path.join(process.cwd(), "src", markdownFile); + + // 从文件系统读取文件内容 + const content = readMarkdownFileContent(filePath); + const title = searchForFirstTitle(filePath); + + // 统计字数并插入到 Frontmatter + const wordCount = countWords(content); + + return { + frontmatter: { + ...pageData.frontmatter, + wordCount, // 将字数写入 Frontmatter + autoTitle: title, + }, + }; + }, + }; +}); diff --git a/src/.vitepress/env.d.ts b/src/.vitepress/env.d.ts index 8749e75..53e640c 100644 --- a/src/.vitepress/env.d.ts +++ b/src/.vitepress/env.d.ts @@ -1,13 +1,13 @@ -declare module 'markdown-it-pangu' { - import type { PluginSimple } from 'markdown-it' +declare module "markdown-it-pangu" { + import type { PluginSimple } from "markdown-it"; - const pangu: PluginSimple - export default pangu + const pangu: PluginSimple; + export default pangu; } -declare module 'markdown-it-katex' { - import type { PluginSimple } from 'markdown-it' +declare module "markdown-it-katex" { + import type { PluginSimple } from "markdown-it"; - const katex: PluginSimple - export default katex + const katex: PluginSimple; + export default katex; } diff --git a/src/.vitepress/theme/Layout.vue b/src/.vitepress/theme/Layout.vue index 1bf0b8f..1a6ff09 100644 --- a/src/.vitepress/theme/Layout.vue +++ b/src/.vitepress/theme/Layout.vue @@ -13,6 +13,7 @@ import { } from "@project-trans/vitepress-theme-project-trans/components"; import CopyrightInfo from "./CopyrightInfo.vue"; +import PageInfo from "./PageInfo.vue"; const { Layout } = DefaultTheme; @@ -22,6 +23,7 @@ const { Layout } = DefaultTheme;