diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 68a5b94b3..4dad767fc 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -511,17 +511,20 @@ importers: specifier: 4.2.5 version: 4.2.5(vue@3.3.13) vuepress-plugin-comment2: - specifier: 2.0.0-rc.5 - version: 2.0.0-rc.5(typescript@5.3.3) + specifier: 2.0.0-rc.6 + version: 2.0.0-rc.6(typescript@5.3.3) vuepress-plugin-md-enhance: - specifier: 2.0.0-rc.5 - version: 2.0.0-rc.5(markdown-it@13.0.2)(typescript@5.3.3) + specifier: 2.0.0-rc.6 + version: 2.0.0-rc.6(markdown-it@13.0.2)(typescript@5.3.3) + vuepress-plugin-reading-time2: + specifier: 2.0.0-rc.6 + version: 2.0.0-rc.6(typescript@5.3.3) vuepress-plugin-seo2: - specifier: 2.0.0-rc.5 - version: 2.0.0-rc.5(typescript@5.3.3) + specifier: 2.0.0-rc.6 + version: 2.0.0-rc.6(typescript@5.3.3) vuepress-plugin-sitemap2: - specifier: 2.0.0-rc.5 - version: 2.0.0-rc.5(typescript@5.3.3) + specifier: 2.0.0-rc.6 + version: 2.0.0-rc.6(typescript@5.3.3) devDependencies: '@types/lodash.merge': specifier: ^4.6.9 @@ -1956,14 +1959,14 @@ packages: resolution: {integrity: sha512-Hcv+nVC0kZnQ3tD9GVu5xSMR4VVYOteQIr/hwFPVEvPdlXqgGEuRjiheChHgdM+JyqdgNcmzZOX/tnl0JOiI7A==} dev: false - /@lit-labs/ssr-dom-shim@1.1.0: - resolution: {integrity: sha512-92uQ5ARf7UXYrzaFcAX3T2rTvaS9Z1//ukV+DqjACM4c8s0ZBQd7ayJU5Dh2AFLD/Ayuyz4uMmxQec8q3U4Ong==} + /@lit-labs/ssr-dom-shim@1.1.2: + resolution: {integrity: sha512-jnOD+/+dSrfTWYfSXBXlo5l5f0q1UuJo3tkbMDCYA2lKUYq79jaxqtGEvnRoh049nt1vdo1+45RinipU6FGY2g==} dev: false - /@lit/reactive-element@1.6.1: - resolution: {integrity: sha512-va15kYZr7KZNNPZdxONGQzpUr+4sxVu7V/VG7a8mRfPPXUyhEYj5RzXCQmGrlP3tAh0L3HHm5AjBMFYRqlM9SA==} + /@lit/reactive-element@2.0.2: + resolution: {integrity: sha512-SVOwLAWUQg3Ji1egtOt1UiFe4zdDpnWHyc5qctSceJ5XIu0Uc76YmGpIjZgx9YJ0XtdW0Jm507sDvjOu+HnB8w==} dependencies: - '@lit-labs/ssr-dom-shim': 1.1.0 + '@lit-labs/ssr-dom-shim': 1.1.2 dev: false /@lukeed/ms@2.0.1: @@ -9425,10 +9428,10 @@ packages: - supports-color dev: true - /giscus@1.3.0: - resolution: {integrity: sha512-A3tVLgSmpnh2sX9uGjo9MbzmTTEJirSyFUPRvkipvy37y9rhxUYDoh9kO37QVrP7Sc7QuJ+gihB6apkO0yDyTw==} + /giscus@1.4.0: + resolution: {integrity: sha512-Pll+pcclTx47NcFDw8nuka2Ja85Gc4XWpzSgL0rszOQaMQRQIV8UMR+zP4a+/N3tV2TXc1SZ537kWlsN6EsAaw==} dependencies: - lit: 2.7.6 + lit: 3.1.0 dev: false /git-hooks-list@3.0.0: @@ -11280,26 +11283,26 @@ packages: wrap-ansi: 9.0.0 dev: true - /lit-element@3.3.1: - resolution: {integrity: sha512-Gl+2409uXWbf7n6cCl7Kzasm7zjT9xmdwi2BhLNi70sRKAgRkqueDu5mSIH3hPYMM0/vqBCdPXod3NbGkRA2ww==} + /lit-element@4.0.2: + resolution: {integrity: sha512-/W6WQZUa5VEXwC7H9tbtDMdSs9aWil3Ou8hU6z2cOKWbsm/tXPAcsoaHVEtrDo0zcOIE5GF6QgU55tlGL2Nihg==} dependencies: - '@lit-labs/ssr-dom-shim': 1.1.0 - '@lit/reactive-element': 1.6.1 - lit-html: 2.7.2 + '@lit-labs/ssr-dom-shim': 1.1.2 + '@lit/reactive-element': 2.0.2 + lit-html: 3.1.0 dev: false - /lit-html@2.7.2: - resolution: {integrity: sha512-ZJCfKlA2XELu5tn7XuzOziGFGvf1SeQm+ngLWoJ8bXtSkRrrR3ms6SWy+gsdxeYwySLij5xAhdd2C3EX0ftxdQ==} + /lit-html@3.1.0: + resolution: {integrity: sha512-FwAjq3iNsaO6SOZXEIpeROlJLUlrbyMkn4iuv4f4u1H40Jw8wkeR/OUXZUHUoiYabGk8Y4Y0F/rgq+R4MrOLmA==} dependencies: '@types/trusted-types': 2.0.2 dev: false - /lit@2.7.6: - resolution: {integrity: sha512-1amFHA7t4VaaDe+vdQejSVBklwtH9svGoG6/dZi9JhxtJBBlqY5D1RV7iLUYY0trCqQc4NfhYYZilZiVHt7Hxg==} + /lit@3.1.0: + resolution: {integrity: sha512-rzo/hmUqX8zmOdamDAeydfjsGXbbdtAFqMhmocnh2j9aDYqbu0fjXygjCa0T99Od9VQ/2itwaGrjZz/ZELVl7w==} dependencies: - '@lit/reactive-element': 1.6.1 - lit-element: 3.3.1 - lit-html: 2.7.2 + '@lit/reactive-element': 2.0.2 + lit-element: 4.0.2 + lit-html: 3.1.0 dev: false /load-json-file@4.0.0: @@ -16383,14 +16386,14 @@ packages: typescript: 5.3.3 dev: false - /vuepress-plugin-comment2@2.0.0-rc.5(typescript@5.3.3): - resolution: {integrity: sha512-EVYsUIIWLFUQoxrALdVcBPnSDPJfXWrsrpbryuWUFDkpjstm7gMHgNGJr6vkaqmP92BkysXcwJ2EuN4OweMtcA==} + /vuepress-plugin-comment2@2.0.0-rc.6(typescript@5.3.3): + resolution: {integrity: sha512-NOGhWS9jrmcFgkaNd5AGtuERok3mbSPBQQ5CV8Uegs0409Zm1L0kmhOPB959Z1KwLxfoNvhD97XruWcQ29bhUw==} engines: {node: '>=18.16.0', npm: '>=8', pnpm: '>=7', yarn: '>=2'} peerDependencies: - '@waline/client': ^2.15.8 || ^3.0.0-alpha.7 - artalk: ^2.6.4 + '@waline/client': ^2.15.8 || ^3.0.0-alpha.8 + artalk: ^2.7.2 sass-loader: ^13.3.2 - twikoo: ^1.6.26 + twikoo: ^1.5.0 vuepress: 2.0.0-rc.0 vuepress-vite: 2.0.0-rc.0 vuepress-webpack: 2.0.0-rc.0 @@ -16413,34 +16416,34 @@ packages: '@vuepress/client': 2.0.0-rc.0(typescript@5.3.3) '@vuepress/shared': 2.0.0-rc.0 '@vuepress/utils': 2.0.0-rc.0 - giscus: 1.3.0 + giscus: 1.4.0 vue: 3.3.13(typescript@5.3.3) vue-router: 4.2.5(vue@3.3.13) - vuepress-plugin-sass-palette: 2.0.0-rc.5(typescript@5.3.3) - vuepress-shared: 2.0.0-rc.5(typescript@5.3.3) + vuepress-plugin-sass-palette: 2.0.0-rc.6(typescript@5.3.3) + vuepress-shared: 2.0.0-rc.6(typescript@5.3.3) transitivePeerDependencies: - '@vue/composition-api' - supports-color - typescript dev: false - /vuepress-plugin-md-enhance@2.0.0-rc.5(markdown-it@13.0.2)(typescript@5.3.3): - resolution: {integrity: sha512-e7xiodPfKLIG8vVHZ9Guk97sc+7w4t0FzBlye6YYKTOd1Csm40hRqvBMp69WmDgzmp6zYdprifw1mC9do17dhw==} + /vuepress-plugin-md-enhance@2.0.0-rc.6(markdown-it@13.0.2)(typescript@5.3.3): + resolution: {integrity: sha512-cEsMccjqdNFq4UjnFbg9OlBwrIF9Ducr2MX2G+6p4h17yJ8yf7NctpGZwnVMh2FGmQ7oYjcIpkuUDrkLn4elzw==} engines: {node: '>=18.16.0', npm: '>=8', pnpm: '>=7', yarn: '>=2'} peerDependencies: '@types/reveal.js': ^4.4.5 '@vue/repl': ^3.0.0 - chart.js: ^4.4.0 - echarts: ^5.4.3 - flowchart.ts: ^2.0.0 - katex: ^0.16.9 - kotlin-playground: ^1.29.0 - markmap-lib: ^0.15.7 - markmap-toolbar: ^0.15.6 - markmap-view: ^0.15.6 + chart.js: ^4.0.0 + echarts: ^5.0.0 + flowchart.ts: ^2.0.0 || ^3.0.0 + katex: ^0.16.0 + kotlin-playground: ^1.23.0 + markmap-lib: ^0.15.5 + markmap-toolbar: ^0.15.5 + markmap-view: ^0.15.5 mathjax-full: ^3.2.2 - mermaid: ^10.6.1 - reveal.js: ^5.0.2 + mermaid: ^10.6.0 + reveal.js: ^5.0.0 sass-loader: ^13.3.2 vuepress: 2.0.0-rc.0 vuepress-vite: 2.0.0-rc.0 @@ -16511,8 +16514,8 @@ packages: js-yaml: 4.1.0 vue: 3.3.13(typescript@5.3.3) vue-router: 4.2.5(vue@3.3.13) - vuepress-plugin-sass-palette: 2.0.0-rc.5(typescript@5.3.3) - vuepress-shared: 2.0.0-rc.5(typescript@5.3.3) + vuepress-plugin-sass-palette: 2.0.0-rc.6(typescript@5.3.3) + vuepress-shared: 2.0.0-rc.6(typescript@5.3.3) transitivePeerDependencies: - '@vue/composition-api' - markdown-it @@ -16520,8 +16523,32 @@ packages: - typescript dev: false - /vuepress-plugin-sass-palette@2.0.0-rc.5(typescript@5.3.3): - resolution: {integrity: sha512-ovAv5xGSu5T2+VlszjZpstDKKVH0wedVm13Vvt6RI6VJujh1fpQy+/g4yNK09U+HNd+sDMZoYCY7epF8CWXOXA==} + /vuepress-plugin-reading-time2@2.0.0-rc.6(typescript@5.3.3): + resolution: {integrity: sha512-GgyKWS66QvlrXV2zMF0qhgtEpELvN0kSOEAqgGn8mR+01TgLirdq+k9rExp1zab7UH7h8FjtRgWlcYgfPUbGEA==} + engines: {node: '>=18.16.0', npm: '>=8', pnpm: '>=7', yarn: '>=2'} + peerDependencies: + vuepress: 2.0.0-rc.0 + vuepress-vite: 2.0.0-rc.0 + vuepress-webpack: 2.0.0-rc.0 + peerDependenciesMeta: + vuepress: + optional: true + vuepress-vite: + optional: true + vuepress-webpack: + optional: true + dependencies: + '@vuepress/client': 2.0.0-rc.0(typescript@5.3.3) + vue: 3.3.13(typescript@5.3.3) + vuepress-shared: 2.0.0-rc.6(typescript@5.3.3) + transitivePeerDependencies: + - '@vue/composition-api' + - supports-color + - typescript + dev: false + + /vuepress-plugin-sass-palette@2.0.0-rc.6(typescript@5.3.3): + resolution: {integrity: sha512-/LAcJvznI5C/cSO0IM2btGivA9IgOON+YwpEzHGhkZKwBKx/lLqOpHH4ghaKmC/hX72QISyM1pjQJ/9pjOQEOg==} engines: {node: '>=18.16.0', npm: '>=8', pnpm: '>=7', yarn: '>=2'} peerDependencies: sass-loader: ^13.3.2 @@ -16542,15 +16569,15 @@ packages: '@vuepress/utils': 2.0.0-rc.0 chokidar: 3.5.3 sass: 1.69.5 - vuepress-shared: 2.0.0-rc.5(typescript@5.3.3) + vuepress-shared: 2.0.0-rc.6(typescript@5.3.3) transitivePeerDependencies: - '@vue/composition-api' - supports-color - typescript dev: false - /vuepress-plugin-seo2@2.0.0-rc.5(typescript@5.3.3): - resolution: {integrity: sha512-VwnBF1zuIjwrIQzAKQQm2l0QucZ/MqEnm3l5Hdg/katGuSna4AjEnhVDswwr4plHliNYWNzcu9V2o0tqEaRouw==} + /vuepress-plugin-seo2@2.0.0-rc.6(typescript@5.3.3): + resolution: {integrity: sha512-ZvYQNV/aZV5iCi7D7qg1Bz7rrQ00f9exPQ+m/JEsJr+uH0g1frAbCI46Ilu0Y42+XTU6id/wO23yKoS0GFQ8jw==} engines: {node: '>=18.16.0', npm: '>=8', pnpm: '>=7', yarn: '>=2'} peerDependencies: vuepress: 2.0.0-rc.0 @@ -16566,15 +16593,15 @@ packages: dependencies: '@vuepress/shared': 2.0.0-rc.0 '@vuepress/utils': 2.0.0-rc.0 - vuepress-shared: 2.0.0-rc.5(typescript@5.3.3) + vuepress-shared: 2.0.0-rc.6(typescript@5.3.3) transitivePeerDependencies: - '@vue/composition-api' - supports-color - typescript dev: false - /vuepress-plugin-sitemap2@2.0.0-rc.5(typescript@5.3.3): - resolution: {integrity: sha512-b1ylNdPLsmhQ+R0NzyaIXHaAybFf0sB47TnYc43X9O8Ql+O8TI6jpBcF8DID7pWp5scQIdf3BIQ1yd/Q0wQgnw==} + /vuepress-plugin-sitemap2@2.0.0-rc.6(typescript@5.3.3): + resolution: {integrity: sha512-b9gNdmaUsXRBpE1OKkTmp/WhCBKQgkHQHhEK1oK9oZiCEAP4Xhmnob5UhtS7WeBK9HBsjCCCUwAEBbNZ8WdiEw==} engines: {node: '>=18.16.0', npm: '>=8', pnpm: '>=7', yarn: '>=2'} peerDependencies: vuepress: 2.0.0-rc.0 @@ -16591,15 +16618,15 @@ packages: '@vuepress/shared': 2.0.0-rc.0 '@vuepress/utils': 2.0.0-rc.0 sitemap: 7.1.1 - vuepress-shared: 2.0.0-rc.5(typescript@5.3.3) + vuepress-shared: 2.0.0-rc.6(typescript@5.3.3) transitivePeerDependencies: - '@vue/composition-api' - supports-color - typescript dev: false - /vuepress-shared@2.0.0-rc.5(typescript@5.3.3): - resolution: {integrity: sha512-J+N3/sb966uI5wSs4k6QLrwxSE78Z77hBRdNKNN9HrhTXQX5SqIV5gqqnS24OfI5GxEWrl4Eba40ECwJk9hKlA==} + /vuepress-shared@2.0.0-rc.6(typescript@5.3.3): + resolution: {integrity: sha512-bcEBxOX0ulWtCeRCBOdIpvFC+m4HvyWGLm6CPXedPiHaSl6avp/S6akYNcj2dyBPXxC5v3WfiGJDumis0fqYbg==} engines: {node: '>=18.16.0', npm: '>=8', pnpm: '>=7', yarn: '>=2'} peerDependencies: vuepress: 2.0.0-rc.0 diff --git a/theme/package.json b/theme/package.json index a6363c727..113f2eae7 100644 --- a/theme/package.json +++ b/theme/package.json @@ -73,10 +73,11 @@ "ts-debounce": "^4.0.0", "vue": "^3.3.13", "vue-router": "4.2.5", - "vuepress-plugin-comment2": "2.0.0-rc.5", - "vuepress-plugin-md-enhance": "2.0.0-rc.5", - "vuepress-plugin-seo2": "2.0.0-rc.5", - "vuepress-plugin-sitemap2": "2.0.0-rc.5" + "vuepress-plugin-comment2": "2.0.0-rc.6", + "vuepress-plugin-md-enhance": "2.0.0-rc.6", + "vuepress-plugin-reading-time2": "2.0.0-rc.6", + "vuepress-plugin-seo2": "2.0.0-rc.6", + "vuepress-plugin-sitemap2": "2.0.0-rc.6" }, "devDependencies": { "@types/lodash.merge": "^4.6.9" diff --git a/theme/src/client/components/PageMeta.vue b/theme/src/client/components/PageMeta.vue index 3c1f0173a..4a7114431 100644 --- a/theme/src/client/components/PageMeta.vue +++ b/theme/src/client/components/PageMeta.vue @@ -5,11 +5,14 @@ import type { PlumeThemePageData, PlumeThemePostFrontmatter, } from '../../shared/index.js' +import { useReadingTime } from '../composables/index.js' +import IconBooks from './icons/IconBooks.vue' import IconClock from './icons/IconClock.vue' import IconTag from './icons/IconTag.vue' const page = usePageData() const matter = usePageFrontmatter() +const readingTime = useReadingTime() const createTime = computed(() => { if (matter.value.createTime) { @@ -48,13 +51,19 @@ const hasMeta = computed(() => tags.value.length || createTime.value) {{ page.title }}
+

+ + {{ readingTime.words }} + {{ readingTime.times }} +

- + {{ tag }} +

-

+

{{ createTime }}

@@ -95,7 +104,8 @@ const hasMeta = computed(() => tags.value.length || createTime.value) .page-meta-wrapper { display: flex; align-items: center; - justify-content: space-between; + justify-content: flex-start; + flex-wrap: wrap; padding: 1rem 0 0.5rem; margin-bottom: 2rem; color: var(--vp-c-text-3); @@ -118,8 +128,7 @@ const hasMeta = computed(() => tags.value.length || createTime.value) .page-meta-wrapper .tag { display: inline-block; line-height: 1; - margin-right: 0.3rem; - padding: 3px 6px; + padding: 3px 3px; color: var(--vp-c-text-2); background-color: var(--vp-c-mute); border-radius: 4px; @@ -128,4 +137,20 @@ const hasMeta = computed(() => tags.value.length || createTime.value) .page-meta-wrapper .tag:last-of-type { margin-right: 0; } + +.page-meta-wrapper .reading-time span { + margin-right: 0.5rem; +} + +.page-meta-wrapper .reading-time span:last-of-type { + margin-right: 0; +} + +.page-meta-wrapper .create-time { + flex: 1; + min-width: 110px; + justify-content: right; + text-align: right; + margin-right: 0; +} diff --git a/theme/src/client/components/icons/IconBooks.vue b/theme/src/client/components/icons/IconBooks.vue new file mode 100644 index 000000000..581dc0834 --- /dev/null +++ b/theme/src/client/components/icons/IconBooks.vue @@ -0,0 +1,3 @@ + diff --git a/theme/src/client/composables/index.ts b/theme/src/client/composables/index.ts index ae7aa372d..e28eca864 100644 --- a/theme/src/client/composables/index.ts +++ b/theme/src/client/composables/index.ts @@ -5,3 +5,4 @@ export * from './useResolveRouteWithRedirect.js' export * from './sidebar.js' export * from './aside.js' export * from './page.js' +export * from './readingTime.js' diff --git a/theme/src/client/composables/readingTime.ts b/theme/src/client/composables/readingTime.ts new file mode 100644 index 000000000..99d70ae0c --- /dev/null +++ b/theme/src/client/composables/readingTime.ts @@ -0,0 +1,153 @@ +import { usePageData } from '@vuepress/client' +import { computed } from 'vue' +import type { + PlumeThemePageData, +} from '../../shared/index.js' + +/** + * Default locale config for `vuepress-plugin-reading-time2` plugin + */ +export const readingTimeLocales = { + "en": { + word: "About $word words", + less1Minute: "Less than 1 minute", + time: "About $time min", + }, + + "zh": { + word: "约 $word 字", + less1Minute: "小于 1 分钟", + time: "大约 $time 分钟", + }, + + "zh-tw": { + word: "約 $word 字", + less1Minute: "小於 1 分鐘", + time: "大约 $time 分鐘", + }, + + "de": { + word: "Ungefähr $word Wörter", + less1Minute: "Weniger als eine Minute", + time: "Ungefähr $time min", + }, + + "de-at": { + word: "Um die $word Wörter", + less1Minute: "Weniger als eine Minute", + time: "Ungefähr $time min", + }, + + "vi": { + word: "Khoảng $word từ", + less1Minute: "Ít hơn 1 phút", + time: "Khoảng $time phút", + }, + + "uk": { + word: "Про $word слова", + less1Minute: "Менше 1 хвилини", + time: "Приблизно $time хв", + }, + + "ru": { + word: "Около $word слов", + less1Minute: "Меньше 1 минуты", + time: "Около $time мин", + }, + + "br": { + word: "Por volta de $word palavras", + less1Minute: "Menos de 1 minuto", + time: "Por volta de $time min", + }, + + "pl": { + word: "Około $word słów", + less1Minute: "Mniej niż 1 minuta", + time: "Około $time minut", + }, + + "sk": { + word: "Okolo $word slov", + less1Minute: "Menej ako 1 minúta", + time: "Okolo $time minút", + }, + + "fr": { + word: "Environ $word mots", + less1Minute: "Moins de 1 minute", + time: "Environ $time min", + }, + + "es": { + word: "Alrededor de $word palabras", + less1Minute: "Menos de 1 minuto", + time: "Alrededor de $time min", + }, + + "ja": { + word: "$word字程度", + less1Minute: "1分以内", + time: "約$time分", + }, + + "tr": { + word: "Yaklaşık $word kelime", + less1Minute: "1 dakikadan az", + time: "Yaklaşık $time dakika", + }, + + "ko": { + word: "약 $word 단어", + less1Minute: "1분 미만", + time: "약 $time 분", + }, + + "fi": { + word: "Noin $word sanaa", + less1Minute: "Alle minuutti", + time: "Noin $time minuuttia", + }, + + "hu": { + word: "Körülbelül $word szó", + less1Minute: "Kevesebb, mint 1 perc", + time: "Körülbelül $time perc", + }, + + "id": { + word: "Sekitar $word kata", + less1Minute: "Kurang dari 1 menit", + time: "Sekitar $time menit", + }, + + "nl": { + word: "Ongeveer $word woorden", + less1Minute: "Minder dan 1 minuut", + time: "Ongeveer $time minuten", + }, +}; + +export const useReadingTime = () => { + const page = usePageData() + + return computed(() => { + if (!page.value.readingTime) return { times: '', words: '' } + + const locale = readingTimeLocales[page.value.lang] ?? readingTimeLocales.en + + const minutes = page.value.readingTime.minutes + const words = page.value.readingTime.words + + const times = (minutes < 1 ? locale.less1Minute : locale.time).replace( + '$time', + Math.round(minutes) + ) + + return { + times, + words: locale.word.replace('$word', words), + } + }) +} diff --git a/theme/src/node/plugins.ts b/theme/src/node/plugins.ts index 63233d6db..9e7a1dac2 100644 --- a/theme/src/node/plugins.ts +++ b/theme/src/node/plugins.ts @@ -19,6 +19,7 @@ import { notesDataPlugin } from '@vuepress-plume/plugin-notes-data' import { shikijiPlugin } from '@vuepress-plume/plugin-shikiji' import { commentPlugin } from 'vuepress-plugin-comment2' import { mdEnhancePlugin } from 'vuepress-plugin-md-enhance' +import { useReadingTimePlugin } from 'vuepress-plugin-reading-time2' import { seoPlugin } from 'vuepress-plugin-seo2' import { sitemapPlugin } from 'vuepress-plugin-sitemap2' import type { @@ -44,6 +45,10 @@ export const setupPlugins = ( }) .filter(Boolean) + if (options.readingTime !== false) { + useReadingTimePlugin(app, options.readingTime || {}, true) + } + return [ palettePlugin({ preset: 'sass' }), themeDataPlugin({ diff --git a/theme/src/shared/options/plugins.ts b/theme/src/shared/options/plugins.ts index 2f095abf7..cdd53aee0 100644 --- a/theme/src/shared/options/plugins.ts +++ b/theme/src/shared/options/plugins.ts @@ -7,6 +7,7 @@ import type { CopyCodeOptions } from '@vuepress-plume/plugin-copy-code' import type { ShikijiPluginOptions } from '@vuepress-plume/plugin-shikiji' import type { CommentPluginOptions } from 'vuepress-plugin-comment2' import type { MarkdownEnhanceOptions } from 'vuepress-plugin-md-enhance' +import type { ReadingTimeOptions } from 'vuepress-plugin-reading-time2' export interface PlumeThemePluginOptions { /** @@ -50,4 +51,6 @@ export interface PlumeThemePluginOptions { baiduTongji?: false | BaiduTongjiOptions frontmatter?: AutoFrontmatterOptions + + readingTime?: false | ReadingTimeOptions } diff --git a/theme/src/shared/page.ts b/theme/src/shared/page.ts index 90e4c1b37..b38bd16bb 100644 --- a/theme/src/shared/page.ts +++ b/theme/src/shared/page.ts @@ -1,10 +1,18 @@ import type { GitPluginPageData } from '@vuepress/plugin-git' +interface ReadingTime { + /** 分钟数 */ + minutes: number; + /** 字数 */ + words: number; +} + export interface PlumeThemePageData extends GitPluginPageData { isBlogPost: boolean type: 'blog' | 'product' categoryList?: PageCategoryData[] filePathRelative: string | null + readingTime?: ReadingTime } export interface PageCategoryData {