From bbed475758cad75b5eacd58b415e81445195b297 Mon Sep 17 00:00:00 2001 From: Mister-Hope Date: Mon, 19 Feb 2024 18:07:20 +0000 Subject: [PATCH] =?UTF-8?q?Deploying=20to=20gh-pages=20from=20@=20vuepress?= =?UTF-8?q?/ecosystem@edc390b73ca0318f3e283d4f5da8d4c98fe19a0e=20?= =?UTF-8?q?=F0=9F=9A=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- 404.html | 37 +++ assets/404.html-Clxd7bFP.js | 1 + assets/NpmBadge-Bn7Nog-o.js | 1 + assets/active-header-links.html-BidXn9_T.js | 11 + assets/active-header-links.html-DkiUbm-s.js | 11 + assets/app-DI49vYHA.js | 32 +++ assets/back-to-top.html-DIJmeRQG.js | 15 + assets/back-to-top.html-DWZAoYSt.js | 15 + assets/baidu-analytics.html-AobDAH-_.js | 11 + assets/baidu-analytics.html-B5FBeNCL.js | 11 + assets/bundler.html-9zmlqkgM.js | 176 ++++++++++++ assets/bundler.html-lrm24igQ.js | 179 ++++++++++++ assets/catalog.html-DGD3v_PG.js | 93 ++++++ assets/catalog.html-ZgM6JxDU.js | 93 ++++++ assets/channel.html-CzU5ykfZ.js | 15 + assets/channel.html-MBD5TiIP.js | 15 + assets/client.html-DnXt2EtS.js | 22 ++ assets/client.html-GE5WwRt5.js | 22 ++ assets/components.html-BLsJeb2o.js | 32 +++ assets/components.html-D_m26OMv.js | 32 +++ assets/config.html-BLNioz4j.js | 140 +++++++++ assets/config.html-BLh0zTBN.js | 1 + assets/config.html-BiDaVbw2.js | 18 ++ assets/config.html-BqDWUA_G.js | 141 +++++++++ assets/config.html-D8ayWeGZ.js | 45 +++ assets/config.html-DGoed1yi.js | 140 +++++++++ assets/config.html-DYMVh00U.js | 45 +++ assets/config.html-Dc4epx8_.js | 143 ++++++++++ assets/config.html-DcZlIOB_.js | 18 ++ assets/config.html-DfnnBIqz.js | 1 + assets/config.html-FMBVZuNJ.js | 143 ++++++++++ assets/config.html-ixjaZyxV.js | 141 +++++++++ assets/container.html-CcAV80DR.js | 39 +++ assets/container.html-Fg-TBurh.js | 39 +++ assets/copy-code.html-DVC_b5GJ.js | 61 ++++ assets/copy-code.html-Dl2NXwK3.js | 61 ++++ assets/copyright.html-Cjv27u5V.js | 66 +++++ assets/copyright.html-xIj0WcRd.js | 66 +++++ assets/docsearch.html-B3tKWbdo.js | 205 ++++++++++++++ assets/docsearch.html-D42wByNJ.js | 203 +++++++++++++ assets/extending-a-theme-01-Cqco1E4_.js | 1 + assets/extending.html-B-2YhQDL.js | 66 +++++ assets/extending.html-C_vYwTyq.js | 65 +++++ assets/external-link-icon.html-CkGGjwAB.js | 28 ++ assets/external-link-icon.html-oHMFewKb.js | 28 ++ assets/frontmatter.html-BzPSllnx.js | 51 ++++ assets/frontmatter.html-CS29ziHh.js | 60 ++++ assets/frontmatter.html-CZ3v91x9.js | 60 ++++ assets/frontmatter.html-CjXlpGGX.js | 1 + assets/frontmatter.html-Dm0TiI4m.js | 1 + assets/frontmatter.html-JmG15wiP.js | 51 ++++ assets/getter.html-CRfx9SLW.js | 91 ++++++ assets/getter.html-DWNp6lVm.js | 91 ++++++ assets/git.html-CMUkyCND.js | 30 ++ assets/git.html-bRRpxIHY.js | 30 ++ assets/google-analytics.html-OhT-gEp5.js | 26 ++ assets/google-analytics.html-R2_6EU47.js | 26 ++ assets/guide.html-6nwdbxs_.js | 218 ++++++++++++++ assets/guide.html-BNXyZOh6.js | 23 ++ assets/guide.html-BVrSzIwm.js | 37 +++ assets/guide.html-BXx1nNKq.js | 1 + assets/guide.html-BguhT2bm.js | 1 + assets/guide.html-Bnwyqjr4.js | 9 + assets/guide.html-DMncVq9E.js | 215 ++++++++++++++ assets/guide.html-Dt9xe1YY.js | 37 +++ assets/guide.html-I8tYpU3u.js | 23 ++ assets/guide.html-MqAl9Hwl.js | 9 + assets/index.html-10UTHdZ8.js | 1 + assets/index.html-3S31PeiG.js | 11 + assets/index.html-4jsKLsHE.js | 11 + assets/index.html-6kqMvCYf.js | 11 + assets/index.html-B1QMRWf6.js | 11 + assets/index.html-B8jQgsFD.js | 1 + assets/index.html-BEvkK6U_.js | 1 + assets/index.html-BFeBzGTv.js | 11 + assets/index.html-BNzZzBwI.js | 1 + assets/index.html-BiVwZvP3.js | 1 + assets/index.html-BszZ_ZuB.js | 11 + assets/index.html-C8BL235-.js | 1 + assets/index.html-C94lRzr8.js | 11 + assets/index.html-CN0Aolsb.js | 1 + assets/index.html-CYCrKtK1.js | 9 + assets/index.html-Cq71pHF1.js | 9 + assets/index.html-Crkx8NN1.js | 11 + assets/index.html-D5ijgUnW.js | 1 + assets/index.html-NGgl-cMu.js | 1 + assets/index.html-Y1wYcIZr.js | 1 + assets/index.html-YB60ZmkU.js | 11 + assets/index.html-dNgmFSRH.js | 1 + assets/index.html-ia4XVneA.js | 11 + assets/index.html-l9neTtpK.js | 1 + assets/locale.html-CBiyLOGJ.js | 21 ++ assets/locale.html-DZtMkHzN.js | 21 ++ assets/markdown.html-BFz9lbN1.js | 50 ++++ assets/markdown.html-QGS-wLl9.js | 50 ++++ assets/medium-zoom.html-BrYLaxXk.js | 31 ++ assets/medium-zoom.html-DBOIkrrJ.js | 31 ++ assets/nprogress.html-D6c03qSI.js | 11 + assets/nprogress.html-fM7jj7yn.js | 11 + assets/page.html-CWQo-Wbb.js | 77 +++++ assets/page.html-DygfZiqD.js | 77 +++++ assets/palette.html-CT6-ohwO.js | 39 +++ assets/palette.html-CrWHHv51.js | 39 +++ assets/photo-swipe.html-CVrJSAOJ.js | 145 ++++++++++ assets/photo-swipe.html-Dw7C8ObA.js | 145 ++++++++++ assets/plugin.html-DpcJp1-q.js | 10 + assets/plugin.html-Dsd8bOpV.js | 10 + assets/prismjs.html-CmrkmCnz.js | 11 + assets/prismjs.html-D5rV4std.js | 11 + assets/reading-time.html-CPp8fwlZ.js | 95 +++++++ assets/reading-time.html-DUOmNho8.js | 95 +++++++ assets/redirect.html-CQdnRl7D.js | 81 ++++++ assets/redirect.html-wE1TgcR8.js | 81 ++++++ assets/register-components.html-CSH-WB-N.js | 49 ++++ assets/register-components.html-nZSf746Y.js | 49 ++++ assets/remove-pwa.html-DyC9esSj.js | 11 + assets/remove-pwa.html-obHjlz2h.js | 11 + assets/rtl.html-COWcRLLy.js | 17 ++ assets/rtl.html-DbExLeFw.js | 17 ++ assets/search.html-BNGeFq23.js | 75 +++++ assets/search.html-DIFheM8W.js | 75 +++++ assets/shared.html-BZlae7Rh.js | 110 +++++++ assets/shared.html-CRDbFVNx.js | 110 +++++++ assets/shiki.html-Br5bIad7.js | 12 + assets/shiki.html-CIFvEyyj.js | 12 + assets/style-BPe9ZiPR.css | 1 + assets/styles.html-Ab_-qJux.js | 263 +++++++++++++++++ assets/styles.html-DfDnb8qj.js | 263 +++++++++++++++++ assets/theme-data.html-CH0nXsCv.js | 51 ++++ assets/theme-data.html-D41xVb4s.js | 51 ++++ assets/toc.html-B5VRzXZc.js | 74 +++++ assets/toc.html-CHxAKS6q.js | 74 +++++ favicon.ico | Bin 0 -> 15086 bytes images/cookbook/extending-a-theme-01.png | Bin 0 -> 13453 bytes images/hero.png | Bin 0 -> 153793 bytes images/icons/android-chrome-192x192.png | Bin 0 -> 6839 bytes images/icons/android-chrome-384x384.png | Bin 0 -> 15528 bytes images/icons/apple-touch-icon.png | Bin 0 -> 5373 bytes images/icons/favicon-16x16.png | Bin 0 -> 806 bytes images/icons/favicon-32x32.png | Bin 0 -> 1295 bytes images/icons/mstile-150x150.png | Bin 0 -> 4676 bytes images/icons/safari-pinned-tab.svg | 23 ++ images/logo.png | Bin 0 -> 3451 bytes index.html | 37 +++ manifest.webmanifest | 21 ++ plugins/active-header-links.html | 47 +++ plugins/back-to-top.html | 51 ++++ plugins/baidu-analytics.html | 47 +++ plugins/blog/config.html | 179 ++++++++++++ plugins/blog/guide.html | 254 +++++++++++++++++ plugins/blog/index.html | 47 +++ plugins/catalog.html | 129 +++++++++ plugins/container.html | 75 +++++ plugins/copy-code.html | 97 +++++++ plugins/copyright.html | 102 +++++++ plugins/docsearch.html | 241 ++++++++++++++++ plugins/external-link-icon.html | 64 +++++ plugins/feed/channel.html | 51 ++++ plugins/feed/config.html | 54 ++++ plugins/feed/frontmatter.html | 87 ++++++ plugins/feed/getter.html | 127 +++++++++ plugins/feed/guide.html | 37 +++ plugins/feed/index.html | 47 +++ plugins/git.html | 66 +++++ plugins/google-analytics.html | 62 ++++ plugins/index.html | 37 +++ plugins/medium-zoom.html | 67 +++++ plugins/nprogress.html | 47 +++ plugins/palette.html | 75 +++++ plugins/photo-swipe.html | 181 ++++++++++++ plugins/prismjs.html | 47 +++ plugins/pwa/config.html | 176 ++++++++++++ plugins/pwa/guide.html | 59 ++++ plugins/pwa/index.html | 47 +++ plugins/reading-time.html | 131 +++++++++ plugins/redirect.html | 117 ++++++++ plugins/register-components.html | 85 ++++++ plugins/remove-pwa.html | 47 +++ plugins/rtl.html | 53 ++++ plugins/search.html | 111 ++++++++ plugins/seo/config.html | 81 ++++++ plugins/seo/guide.html | 73 +++++ plugins/seo/index.html | 47 +++ plugins/shiki.html | 48 ++++ plugins/sitemap/config.html | 37 +++ plugins/sitemap/frontmatter.html | 37 +++ plugins/sitemap/guide.html | 45 +++ plugins/sitemap/index.html | 47 +++ plugins/theme-data.html | 87 ++++++ plugins/toc.html | 110 +++++++ themes/default/components.html | 68 +++++ themes/default/config.html | 177 ++++++++++++ themes/default/extending.html | 102 +++++++ themes/default/frontmatter.html | 96 +++++++ themes/default/index.html | 45 +++ themes/default/locale.html | 57 ++++ themes/default/markdown.html | 86 ++++++ themes/default/plugin.html | 46 +++ themes/default/styles.html | 299 ++++++++++++++++++++ themes/index.html | 37 +++ tools/helper/client.html | 58 ++++ tools/helper/index.html | 37 +++ tools/helper/node/bundler.html | 212 ++++++++++++++ tools/helper/node/index.html | 37 +++ tools/helper/node/page.html | 113 ++++++++ tools/helper/shared.html | 146 ++++++++++ tools/index.html | 37 +++ zh/index.html | 37 +++ zh/plugins/active-header-links.html | 47 +++ zh/plugins/back-to-top.html | 51 ++++ zh/plugins/baidu-analytics.html | 47 +++ zh/plugins/blog/config.html | 179 ++++++++++++ zh/plugins/blog/guide.html | 251 ++++++++++++++++ zh/plugins/blog/index.html | 47 +++ zh/plugins/catalog.html | 129 +++++++++ zh/plugins/container.html | 75 +++++ zh/plugins/copy-code.html | 97 +++++++ zh/plugins/copyright.html | 102 +++++++ zh/plugins/docsearch.html | 239 ++++++++++++++++ zh/plugins/external-link-icon.html | 64 +++++ zh/plugins/feed/channel.html | 51 ++++ zh/plugins/feed/config.html | 54 ++++ zh/plugins/feed/frontmatter.html | 87 ++++++ zh/plugins/feed/getter.html | 127 +++++++++ zh/plugins/feed/guide.html | 37 +++ zh/plugins/feed/index.html | 47 +++ zh/plugins/git.html | 66 +++++ zh/plugins/google-analytics.html | 62 ++++ zh/plugins/index.html | 37 +++ zh/plugins/medium-zoom.html | 67 +++++ zh/plugins/nprogress.html | 47 +++ zh/plugins/palette.html | 75 +++++ zh/plugins/photo-swipe.html | 181 ++++++++++++ zh/plugins/prismjs.html | 47 +++ zh/plugins/pwa/config.html | 176 ++++++++++++ zh/plugins/pwa/guide.html | 59 ++++ zh/plugins/pwa/index.html | 47 +++ zh/plugins/reading-time.html | 131 +++++++++ zh/plugins/redirect.html | 117 ++++++++ zh/plugins/register-components.html | 85 ++++++ zh/plugins/remove-pwa.html | 47 +++ zh/plugins/rtl.html | 53 ++++ zh/plugins/search.html | 111 ++++++++ zh/plugins/seo/config.html | 81 ++++++ zh/plugins/seo/guide.html | 73 +++++ zh/plugins/seo/index.html | 47 +++ zh/plugins/shiki.html | 48 ++++ zh/plugins/sitemap/config.html | 37 +++ zh/plugins/sitemap/frontmatter.html | 37 +++ zh/plugins/sitemap/guide.html | 45 +++ zh/plugins/sitemap/index.html | 47 +++ zh/plugins/theme-data.html | 87 ++++++ zh/plugins/toc.html | 110 +++++++ zh/themes/default/components.html | 68 +++++ zh/themes/default/config.html | 177 ++++++++++++ zh/themes/default/extending.html | 101 +++++++ zh/themes/default/frontmatter.html | 96 +++++++ zh/themes/default/index.html | 45 +++ zh/themes/default/locale.html | 57 ++++ zh/themes/default/markdown.html | 86 ++++++ zh/themes/default/plugin.html | 46 +++ zh/themes/default/styles.html | 299 ++++++++++++++++++++ zh/themes/index.html | 37 +++ zh/tools/helper/client.html | 58 ++++ zh/tools/helper/index.html | 37 +++ zh/tools/helper/node/bundler.html | 215 ++++++++++++++ zh/tools/helper/node/index.html | 37 +++ zh/tools/helper/node/page.html | 113 ++++++++ zh/tools/helper/shared.html | 146 ++++++++++ zh/tools/index.html | 37 +++ 270 files changed, 17359 insertions(+) create mode 100644 404.html create mode 100644 assets/404.html-Clxd7bFP.js create mode 100644 assets/NpmBadge-Bn7Nog-o.js create mode 100644 assets/active-header-links.html-BidXn9_T.js create mode 100644 assets/active-header-links.html-DkiUbm-s.js create mode 100644 assets/app-DI49vYHA.js create mode 100644 assets/back-to-top.html-DIJmeRQG.js create mode 100644 assets/back-to-top.html-DWZAoYSt.js create mode 100644 assets/baidu-analytics.html-AobDAH-_.js create mode 100644 assets/baidu-analytics.html-B5FBeNCL.js create mode 100644 assets/bundler.html-9zmlqkgM.js create mode 100644 assets/bundler.html-lrm24igQ.js create mode 100644 assets/catalog.html-DGD3v_PG.js create mode 100644 assets/catalog.html-ZgM6JxDU.js create mode 100644 assets/channel.html-CzU5ykfZ.js create mode 100644 assets/channel.html-MBD5TiIP.js create mode 100644 assets/client.html-DnXt2EtS.js create mode 100644 assets/client.html-GE5WwRt5.js create mode 100644 assets/components.html-BLsJeb2o.js create mode 100644 assets/components.html-D_m26OMv.js create mode 100644 assets/config.html-BLNioz4j.js create mode 100644 assets/config.html-BLh0zTBN.js create mode 100644 assets/config.html-BiDaVbw2.js create mode 100644 assets/config.html-BqDWUA_G.js create mode 100644 assets/config.html-D8ayWeGZ.js create mode 100644 assets/config.html-DGoed1yi.js create mode 100644 assets/config.html-DYMVh00U.js create mode 100644 assets/config.html-Dc4epx8_.js create mode 100644 assets/config.html-DcZlIOB_.js create mode 100644 assets/config.html-DfnnBIqz.js create mode 100644 assets/config.html-FMBVZuNJ.js create mode 100644 assets/config.html-ixjaZyxV.js create mode 100644 assets/container.html-CcAV80DR.js create mode 100644 assets/container.html-Fg-TBurh.js create mode 100644 assets/copy-code.html-DVC_b5GJ.js create mode 100644 assets/copy-code.html-Dl2NXwK3.js create mode 100644 assets/copyright.html-Cjv27u5V.js create mode 100644 assets/copyright.html-xIj0WcRd.js create mode 100644 assets/docsearch.html-B3tKWbdo.js create mode 100644 assets/docsearch.html-D42wByNJ.js create mode 100644 assets/extending-a-theme-01-Cqco1E4_.js create mode 100644 assets/extending.html-B-2YhQDL.js create mode 100644 assets/extending.html-C_vYwTyq.js create mode 100644 assets/external-link-icon.html-CkGGjwAB.js create mode 100644 assets/external-link-icon.html-oHMFewKb.js create mode 100644 assets/frontmatter.html-BzPSllnx.js create mode 100644 assets/frontmatter.html-CS29ziHh.js create mode 100644 assets/frontmatter.html-CZ3v91x9.js create mode 100644 assets/frontmatter.html-CjXlpGGX.js create mode 100644 assets/frontmatter.html-Dm0TiI4m.js create mode 100644 assets/frontmatter.html-JmG15wiP.js create mode 100644 assets/getter.html-CRfx9SLW.js create mode 100644 assets/getter.html-DWNp6lVm.js create mode 100644 assets/git.html-CMUkyCND.js create mode 100644 assets/git.html-bRRpxIHY.js create mode 100644 assets/google-analytics.html-OhT-gEp5.js create mode 100644 assets/google-analytics.html-R2_6EU47.js create mode 100644 assets/guide.html-6nwdbxs_.js create mode 100644 assets/guide.html-BNXyZOh6.js create mode 100644 assets/guide.html-BVrSzIwm.js create mode 100644 assets/guide.html-BXx1nNKq.js create mode 100644 assets/guide.html-BguhT2bm.js create mode 100644 assets/guide.html-Bnwyqjr4.js create mode 100644 assets/guide.html-DMncVq9E.js create mode 100644 assets/guide.html-Dt9xe1YY.js create mode 100644 assets/guide.html-I8tYpU3u.js create mode 100644 assets/guide.html-MqAl9Hwl.js create mode 100644 assets/index.html-10UTHdZ8.js create mode 100644 assets/index.html-3S31PeiG.js create mode 100644 assets/index.html-4jsKLsHE.js create mode 100644 assets/index.html-6kqMvCYf.js create mode 100644 assets/index.html-B1QMRWf6.js create mode 100644 assets/index.html-B8jQgsFD.js create mode 100644 assets/index.html-BEvkK6U_.js create mode 100644 assets/index.html-BFeBzGTv.js create mode 100644 assets/index.html-BNzZzBwI.js create mode 100644 assets/index.html-BiVwZvP3.js create mode 100644 assets/index.html-BszZ_ZuB.js create mode 100644 assets/index.html-C8BL235-.js create mode 100644 assets/index.html-C94lRzr8.js create mode 100644 assets/index.html-CN0Aolsb.js create mode 100644 assets/index.html-CYCrKtK1.js create mode 100644 assets/index.html-Cq71pHF1.js create mode 100644 assets/index.html-Crkx8NN1.js create mode 100644 assets/index.html-D5ijgUnW.js create mode 100644 assets/index.html-NGgl-cMu.js create mode 100644 assets/index.html-Y1wYcIZr.js create mode 100644 assets/index.html-YB60ZmkU.js create mode 100644 assets/index.html-dNgmFSRH.js create mode 100644 assets/index.html-ia4XVneA.js create mode 100644 assets/index.html-l9neTtpK.js create mode 100644 assets/locale.html-CBiyLOGJ.js create mode 100644 assets/locale.html-DZtMkHzN.js create mode 100644 assets/markdown.html-BFz9lbN1.js create mode 100644 assets/markdown.html-QGS-wLl9.js create mode 100644 assets/medium-zoom.html-BrYLaxXk.js create mode 100644 assets/medium-zoom.html-DBOIkrrJ.js create mode 100644 assets/nprogress.html-D6c03qSI.js create mode 100644 assets/nprogress.html-fM7jj7yn.js create mode 100644 assets/page.html-CWQo-Wbb.js create mode 100644 assets/page.html-DygfZiqD.js create mode 100644 assets/palette.html-CT6-ohwO.js create mode 100644 assets/palette.html-CrWHHv51.js create mode 100644 assets/photo-swipe.html-CVrJSAOJ.js create mode 100644 assets/photo-swipe.html-Dw7C8ObA.js create mode 100644 assets/plugin.html-DpcJp1-q.js create mode 100644 assets/plugin.html-Dsd8bOpV.js create mode 100644 assets/prismjs.html-CmrkmCnz.js create mode 100644 assets/prismjs.html-D5rV4std.js create mode 100644 assets/reading-time.html-CPp8fwlZ.js create mode 100644 assets/reading-time.html-DUOmNho8.js create mode 100644 assets/redirect.html-CQdnRl7D.js create mode 100644 assets/redirect.html-wE1TgcR8.js create mode 100644 assets/register-components.html-CSH-WB-N.js create mode 100644 assets/register-components.html-nZSf746Y.js create mode 100644 assets/remove-pwa.html-DyC9esSj.js create mode 100644 assets/remove-pwa.html-obHjlz2h.js create mode 100644 assets/rtl.html-COWcRLLy.js create mode 100644 assets/rtl.html-DbExLeFw.js create mode 100644 assets/search.html-BNGeFq23.js create mode 100644 assets/search.html-DIFheM8W.js create mode 100644 assets/shared.html-BZlae7Rh.js create mode 100644 assets/shared.html-CRDbFVNx.js create mode 100644 assets/shiki.html-Br5bIad7.js create mode 100644 assets/shiki.html-CIFvEyyj.js create mode 100644 assets/style-BPe9ZiPR.css create mode 100644 assets/styles.html-Ab_-qJux.js create mode 100644 assets/styles.html-DfDnb8qj.js create mode 100644 assets/theme-data.html-CH0nXsCv.js create mode 100644 assets/theme-data.html-D41xVb4s.js create mode 100644 assets/toc.html-B5VRzXZc.js create mode 100644 assets/toc.html-CHxAKS6q.js create mode 100644 favicon.ico create mode 100644 images/cookbook/extending-a-theme-01.png create mode 100644 images/hero.png create mode 100644 images/icons/android-chrome-192x192.png create mode 100644 images/icons/android-chrome-384x384.png create mode 100644 images/icons/apple-touch-icon.png create mode 100644 images/icons/favicon-16x16.png create mode 100644 images/icons/favicon-32x32.png create mode 100644 images/icons/mstile-150x150.png create mode 100644 images/icons/safari-pinned-tab.svg create mode 100644 images/logo.png create mode 100644 index.html create mode 100644 manifest.webmanifest create mode 100644 plugins/active-header-links.html create mode 100644 plugins/back-to-top.html create mode 100644 plugins/baidu-analytics.html create mode 100644 plugins/blog/config.html create mode 100644 plugins/blog/guide.html create mode 100644 plugins/blog/index.html create mode 100644 plugins/catalog.html create mode 100644 plugins/container.html create mode 100644 plugins/copy-code.html create mode 100644 plugins/copyright.html create mode 100644 plugins/docsearch.html create mode 100644 plugins/external-link-icon.html create mode 100644 plugins/feed/channel.html create mode 100644 plugins/feed/config.html create mode 100644 plugins/feed/frontmatter.html create mode 100644 plugins/feed/getter.html create mode 100644 plugins/feed/guide.html create mode 100644 plugins/feed/index.html create mode 100644 plugins/git.html create mode 100644 plugins/google-analytics.html create mode 100644 plugins/index.html create mode 100644 plugins/medium-zoom.html create mode 100644 plugins/nprogress.html create mode 100644 plugins/palette.html create mode 100644 plugins/photo-swipe.html create mode 100644 plugins/prismjs.html create mode 100644 plugins/pwa/config.html create mode 100644 plugins/pwa/guide.html create mode 100644 plugins/pwa/index.html create mode 100644 plugins/reading-time.html create mode 100644 plugins/redirect.html create mode 100644 plugins/register-components.html create mode 100644 plugins/remove-pwa.html create mode 100644 plugins/rtl.html create mode 100644 plugins/search.html create mode 100644 plugins/seo/config.html create mode 100644 plugins/seo/guide.html create mode 100644 plugins/seo/index.html create mode 100644 plugins/shiki.html create mode 100644 plugins/sitemap/config.html create mode 100644 plugins/sitemap/frontmatter.html create mode 100644 plugins/sitemap/guide.html create mode 100644 plugins/sitemap/index.html create mode 100644 plugins/theme-data.html create mode 100644 plugins/toc.html create mode 100644 themes/default/components.html create mode 100644 themes/default/config.html create mode 100644 themes/default/extending.html create mode 100644 themes/default/frontmatter.html create mode 100644 themes/default/index.html create mode 100644 themes/default/locale.html create mode 100644 themes/default/markdown.html create mode 100644 themes/default/plugin.html create mode 100644 themes/default/styles.html create mode 100644 themes/index.html create mode 100644 tools/helper/client.html create mode 100644 tools/helper/index.html create mode 100644 tools/helper/node/bundler.html create mode 100644 tools/helper/node/index.html create mode 100644 tools/helper/node/page.html create mode 100644 tools/helper/shared.html create mode 100644 tools/index.html create mode 100644 zh/index.html create mode 100644 zh/plugins/active-header-links.html create mode 100644 zh/plugins/back-to-top.html create mode 100644 zh/plugins/baidu-analytics.html create mode 100644 zh/plugins/blog/config.html create mode 100644 zh/plugins/blog/guide.html create mode 100644 zh/plugins/blog/index.html create mode 100644 zh/plugins/catalog.html create mode 100644 zh/plugins/container.html create mode 100644 zh/plugins/copy-code.html create mode 100644 zh/plugins/copyright.html create mode 100644 zh/plugins/docsearch.html create mode 100644 zh/plugins/external-link-icon.html create mode 100644 zh/plugins/feed/channel.html create mode 100644 zh/plugins/feed/config.html create mode 100644 zh/plugins/feed/frontmatter.html create mode 100644 zh/plugins/feed/getter.html create mode 100644 zh/plugins/feed/guide.html create mode 100644 zh/plugins/feed/index.html create mode 100644 zh/plugins/git.html create mode 100644 zh/plugins/google-analytics.html create mode 100644 zh/plugins/index.html create mode 100644 zh/plugins/medium-zoom.html create mode 100644 zh/plugins/nprogress.html create mode 100644 zh/plugins/palette.html create mode 100644 zh/plugins/photo-swipe.html create mode 100644 zh/plugins/prismjs.html create mode 100644 zh/plugins/pwa/config.html create mode 100644 zh/plugins/pwa/guide.html create mode 100644 zh/plugins/pwa/index.html create mode 100644 zh/plugins/reading-time.html create mode 100644 zh/plugins/redirect.html create mode 100644 zh/plugins/register-components.html create mode 100644 zh/plugins/remove-pwa.html create mode 100644 zh/plugins/rtl.html create mode 100644 zh/plugins/search.html create mode 100644 zh/plugins/seo/config.html create mode 100644 zh/plugins/seo/guide.html create mode 100644 zh/plugins/seo/index.html create mode 100644 zh/plugins/shiki.html create mode 100644 zh/plugins/sitemap/config.html create mode 100644 zh/plugins/sitemap/frontmatter.html create mode 100644 zh/plugins/sitemap/guide.html create mode 100644 zh/plugins/sitemap/index.html create mode 100644 zh/plugins/theme-data.html create mode 100644 zh/plugins/toc.html create mode 100644 zh/themes/default/components.html create mode 100644 zh/themes/default/config.html create mode 100644 zh/themes/default/extending.html create mode 100644 zh/themes/default/frontmatter.html create mode 100644 zh/themes/default/index.html create mode 100644 zh/themes/default/locale.html create mode 100644 zh/themes/default/markdown.html create mode 100644 zh/themes/default/plugin.html create mode 100644 zh/themes/default/styles.html create mode 100644 zh/themes/index.html create mode 100644 zh/tools/helper/client.html create mode 100644 zh/tools/helper/index.html create mode 100644 zh/tools/helper/node/bundler.html create mode 100644 zh/tools/helper/node/index.html create mode 100644 zh/tools/helper/node/page.html create mode 100644 zh/tools/helper/shared.html create mode 100644 zh/tools/index.html diff --git a/404.html b/404.html new file mode 100644 index 0000000000..38c15f361d --- /dev/null +++ b/404.html @@ -0,0 +1,37 @@ + + + + + + + + + VuePress Ecosystem + + + + + +

404

How did we get here?
Take me home
+ + + diff --git a/assets/404.html-Clxd7bFP.js b/assets/404.html-Clxd7bFP.js new file mode 100644 index 0000000000..3ebca2fb0d --- /dev/null +++ b/assets/404.html-Clxd7bFP.js @@ -0,0 +1 @@ +import{_ as t,o as e,c as o,b as n}from"./app-DI49vYHA.js";const a={},c=n("p",null,"404 Not Found",-1),l=[c];function s(_,r){return e(),o("div",null,l)}const d=t(a,[["render",s],["__file","404.html.vue"]]),h=JSON.parse('{"path":"/404.html","title":"","lang":"en-US","frontmatter":{"layout":"NotFound"},"headers":[],"git":{},"filePathRelative":null}');export{d as comp,h as data}; diff --git a/assets/NpmBadge-Bn7Nog-o.js b/assets/NpmBadge-Bn7Nog-o.js new file mode 100644 index 0000000000..85524fafa6 --- /dev/null +++ b/assets/NpmBadge-Bn7Nog-o.js @@ -0,0 +1 @@ +import{f as r,g as t,o,c as p,b as g,_ as d}from"./app-DI49vYHA.js";const l=["href","title"],i=["src","alt"],m=r({__name:"NpmBadge",props:{package:{type:String,required:!0},distTag:{type:String,required:!1,default:"next"}},setup(a){const e=a,n=t(()=>`https://www.npmjs.com/package/${e.package}`),c=t(()=>e.distTag?`${e.package}@${e.distTag}`:e.package),s=t(()=>`https://badgen.net/npm/v/${e.package}/${e.distTag}?label=${encodeURIComponent(c.value)}`);return(u,_)=>(o(),p("a",{class:"npm-badge",href:n.value,title:a.package,target:"_blank",rel:"noopener noreferrer"},[g("img",{src:s.value,alt:a.package},null,8,i)],8,l))}}),k=d(m,[["__scopeId","data-v-c758b2a0"],["__file","NpmBadge.vue"]]);export{k as default}; diff --git a/assets/active-header-links.html-BidXn9_T.js b/assets/active-header-links.html-BidXn9_T.js new file mode 100644 index 0000000000..770cf81cdc --- /dev/null +++ b/assets/active-header-links.html-BidXn9_T.js @@ -0,0 +1,11 @@ +import{_ as c,r as s,o as d,c as p,a as n,b as e,d as a,w as o,e as i}from"./app-DI49vYHA.js";const h={},u=e("h1",{id:"active-header-links",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#active-header-links"},[e("span",null,"active-header-links")])],-1),m=i(`

This plugin will listen to page scroll event. When the page scrolls to a certain header anchor, this plugin will change the route hash to that header anchor if there is a corresponding header link.

This plugin is mainly used to develop themes, and has been integrated into the default theme. You won't need to use it directly in most cases.

Usage

npm i -D @vuepress/plugin-active-header-links@next
+
import { activeHeaderLinksPlugin } from '@vuepress/plugin-active-header-links'
+
+export default {
+  plugins: [
+    activeHeaderLinksPlugin({
+      // options
+    }),
+  ],
+}
+

Options

headerLinkSelector

headerAnchorSelector

`,9),v=e("li",null,[e("p",null,[a("Type: "),e("code",null,"string")])],-1),f=e("li",null,[e("p",null,[a("Default: "),e("code",null,"'.header-anchor'")])],-1),g=e("p",null,"Details:",-1),k=e("p",null,[a("Selector of "),e("em",null,"header anchor"),a(".")],-1),D=e("code",null,"permalinkClass",-1),y={href:"https://github.com/valeriangalliat/markdown-it-anchor#readme",target:"_blank",rel:"noopener noreferrer"},_=e("p",null,"Also see:",-1),b=i('

delay

offset

',4);function x(C,E){const t=s("NpmBadge"),r=s("ExternalLinkIcon"),l=s("RouteLink");return d(),p("div",null,[u,n(t,{package:"@vuepress/plugin-active-header-links"}),m,e("ul",null,[v,f,e("li",null,[g,k,e("p",null,[a("You don't need to specify this option unless you have changed the "),D,a(" option of "),e("a",y,[a("markdown-it-anchor"),n(r)]),a(" via "),n(l,{to:"/config.html#markdown-anchor"},{default:o(()=>[a("markdown.anchor")]),_:1}),a(".")])]),e("li",null,[_,e("ul",null,[e("li",null,[n(l,{to:"/guide/markdown.html#header-anchors"},{default:o(()=>[a("Guide > Markdown > Syntax Extensions > Header Anchors")]),_:1})])])])]),b])}const T=c(h,[["render",x],["__file","active-header-links.html.vue"]]),S=JSON.parse('{"path":"/plugins/active-header-links.html","title":"active-header-links","lang":"en-US","frontmatter":{},"headers":[{"level":2,"title":"Usage","slug":"usage","link":"#usage","children":[]},{"level":2,"title":"Options","slug":"options","link":"#options","children":[{"level":3,"title":"headerLinkSelector","slug":"headerlinkselector","link":"#headerlinkselector","children":[]},{"level":3,"title":"headerAnchorSelector","slug":"headeranchorselector","link":"#headeranchorselector","children":[]},{"level":3,"title":"delay","slug":"delay","link":"#delay","children":[]},{"level":3,"title":"offset","slug":"offset","link":"#offset","children":[]}]}],"git":{"updatedTime":1706605723000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":1}]},"filePathRelative":"plugins/active-header-links.md"}');export{T as comp,S as data}; diff --git a/assets/active-header-links.html-DkiUbm-s.js b/assets/active-header-links.html-DkiUbm-s.js new file mode 100644 index 0000000000..72c67506cb --- /dev/null +++ b/assets/active-header-links.html-DkiUbm-s.js @@ -0,0 +1,11 @@ +import{_ as c,r as n,o as d,c as p,a,b as e,d as l,w as i,e as o}from"./app-DI49vYHA.js";const h={},u=e("h1",{id:"active-header-links",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#active-header-links"},[e("span",null,"active-header-links")])],-1),m=o(`

该插件会监听页面滚动事件。当页面滚动至某个 标题锚点 后,如果存在对应的 标题链接 ,那么该插件会将路由 Hash 更改为该 标题锚点

该插件主要用于开发主题,并且已经集成到默认主题中。大部分情况下你不需要直接使用它。

使用方法

npm i -D @vuepress/plugin-active-header-links@next
+
import { activeHeaderLinksPlugin } from '@vuepress/plugin-active-header-links'
+
+export default {
+  plugins: [
+    activeHeaderLinksPlugin({
+      // 配置项
+    }),
+  ],
+}
+

配置项

headerLinkSelector

headerAnchorSelector

`,9),v=e("li",null,[e("p",null,[l("类型: "),e("code",null,"string")])],-1),k=e("li",null,[e("p",null,[l("默认值: "),e("code",null,"'.header-anchor'")])],-1),_=e("p",null,"详情:",-1),f=e("p",null,[e("em",null,"标题锚点"),l(" 的选择器。")],-1),D={href:"https://github.com/valeriangalliat/markdown-it-anchor#readme",target:"_blank",rel:"noopener noreferrer"},b=e("code",null,"permalinkClass",-1),g=e("p",null,"参考:",-1),y=o('

delay

offset

',4);function C(x,E){const r=n("NpmBadge"),s=n("RouteLink"),t=n("ExternalLinkIcon");return d(),p("div",null,[u,a(r,{package:"@vuepress/plugin-active-header-links"}),m,e("ul",null,[v,k,e("li",null,[_,f,e("p",null,[l("你通常不需要设置该选项,除非你通过 "),a(s,{to:"/zh/config.html#markdown-anchor"},{default:i(()=>[l("markdown.anchor")]),_:1}),l(" 修改了 "),e("a",D,[l("markdown-it-anchor"),a(t)]),l(" 的 "),b,l(" 选项。")])]),e("li",null,[g,e("ul",null,[e("li",null,[a(s,{to:"/guide/markdown.html#%E6%A0%87%E9%A2%98%E9%94%9A%E7%82%B9"},{default:i(()=>[l("指南 > Markdown > 语法扩展 > 标题锚点")]),_:1})])])])]),y])}const L=c(h,[["render",C],["__file","active-header-links.html.vue"]]),w=JSON.parse('{"path":"/zh/plugins/active-header-links.html","title":"active-header-links","lang":"zh-CN","frontmatter":{},"headers":[{"level":2,"title":"使用方法","slug":"使用方法","link":"#使用方法","children":[]},{"level":2,"title":"配置项","slug":"配置项","link":"#配置项","children":[{"level":3,"title":"headerLinkSelector","slug":"headerlinkselector","link":"#headerlinkselector","children":[]},{"level":3,"title":"headerAnchorSelector","slug":"headeranchorselector","link":"#headeranchorselector","children":[]},{"level":3,"title":"delay","slug":"delay","link":"#delay","children":[]},{"level":3,"title":"offset","slug":"offset","link":"#offset","children":[]}]}],"git":{"updatedTime":1706605723000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":1}]},"filePathRelative":"zh/plugins/active-header-links.md"}');export{L as comp,w as data}; diff --git a/assets/app-DI49vYHA.js b/assets/app-DI49vYHA.js new file mode 100644 index 0000000000..8dc31d642b --- /dev/null +++ b/assets/app-DI49vYHA.js @@ -0,0 +1,32 @@ +/** +* @vue/shared v3.4.19 +* (c) 2018-present Yuxi (Evan) You and Vue contributors +* @license MIT +**/function $i(e,t){const l=new Set(e.split(","));return t?n=>l.has(n.toLowerCase()):n=>l.has(n)}const xe={},dl=[],lt=()=>{},Aa=()=>!1,ql=e=>e.charCodeAt(0)===111&&e.charCodeAt(1)===110&&(e.charCodeAt(2)>122||e.charCodeAt(2)<97),Mi=e=>e.startsWith("onUpdate:"),Te=Object.assign,Ni=(e,t)=>{const l=e.indexOf(t);l>-1&&e.splice(l,1)},Oa=Object.prototype.hasOwnProperty,ge=(e,t)=>Oa.call(e,t),ne=Array.isArray,hl=e=>Vn(e)==="[object Map]",Ms=e=>Vn(e)==="[object Set]",oe=e=>typeof e=="function",Se=e=>typeof e=="string",xl=e=>typeof e=="symbol",ye=e=>e!==null&&typeof e=="object",Ns=e=>(ye(e)||oe(e))&&oe(e.then)&&oe(e.catch),Hs=Object.prototype.toString,Vn=e=>Hs.call(e),Ra=e=>Vn(e).slice(8,-1),Vs=e=>Vn(e)==="[object Object]",Hi=e=>Se(e)&&e!=="NaN"&&e[0]!=="-"&&""+parseInt(e,10)===e,gl=$i(",key,ref,ref_for,ref_key,onVnodeBeforeMount,onVnodeMounted,onVnodeBeforeUpdate,onVnodeUpdated,onVnodeBeforeUnmount,onVnodeUnmounted"),Bn=e=>{const t=Object.create(null);return l=>t[l]||(t[l]=e(l))},Ia=/-(\w)/g,it=Bn(e=>e.replace(Ia,(t,l)=>l?l.toUpperCase():"")),Fa=/\B([A-Z])/g,nl=Bn(e=>e.replace(Fa,"-$1").toLowerCase()),Xl=Bn(e=>e.charAt(0).toUpperCase()+e.slice(1)),ni=Bn(e=>e?`on${Xl(e)}`:""),Mt=(e,t)=>!Object.is(e,t),ii=(e,t)=>{for(let l=0;l{Object.defineProperty(e,t,{configurable:!0,enumerable:!1,value:l})},Da=e=>{const t=parseFloat(e);return isNaN(t)?e:t},$a=e=>{const t=Se(e)?Number(e):NaN;return isNaN(t)?e:t};let _r;const Bs=()=>_r||(_r=typeof globalThis<"u"?globalThis:typeof self<"u"?self:typeof window<"u"?window:typeof global<"u"?global:{});function Yl(e){if(ne(e)){const t={};for(let l=0;l{if(l){const n=l.split(Na);n.length>1&&(t[n[0].trim()]=n[1].trim())}}),t}function Ke(e){let t="";if(Se(e))t=e;else if(ne(e))for(let l=0;lSe(e)?e:e==null?"":ne(e)||ye(e)&&(e.toString===Hs||!oe(e.toString))?JSON.stringify(e,Us,2):String(e),Us=(e,t)=>t&&t.__v_isRef?Us(e,t.value):hl(t)?{[`Map(${t.size})`]:[...t.entries()].reduce((l,[n,i],r)=>(l[ri(n,r)+" =>"]=i,l),{})}:Ms(t)?{[`Set(${t.size})`]:[...t.values()].map(l=>ri(l))}:xl(t)?ri(t):ye(t)&&!ne(t)&&!Vs(t)?String(t):t,ri=(e,t="")=>{var l;return xl(e)?`Symbol(${(l=e.description)!=null?l:t})`:e};/** +* @vue/reactivity v3.4.19 +* (c) 2018-present Yuxi (Evan) You and Vue contributors +* @license MIT +**/let Je;class Ua{constructor(t=!1){this.detached=t,this._active=!0,this.effects=[],this.cleanups=[],this.parent=Je,!t&&Je&&(this.index=(Je.scopes||(Je.scopes=[])).push(this)-1)}get active(){return this._active}run(t){if(this._active){const l=Je;try{return Je=this,t()}finally{Je=l}}}on(){Je=this}off(){Je=this.parent}stop(t){if(this._active){let l,n;for(l=0,n=this.effects.length;l=4))break}this._dirtyLevel===1&&(this._dirtyLevel=0),rl()}return this._dirtyLevel>=4}set dirty(t){this._dirtyLevel=t?4:0}run(){if(this._dirtyLevel=0,!this.active)return this.fn();let t=Dt,l=Qt;try{return Dt=!0,Qt=this,this._runnings++,br(this),this.fn()}finally{yr(this),this._runnings--,Qt=l,Dt=t}}stop(){var t;this.active&&(br(this),yr(this),(t=this.onStop)==null||t.call(this),this.active=!1)}}function Ka(e){return e.value}function br(e){e._trackId++,e._depsLength=0}function yr(e){if(e.deps.length>e._depsLength){for(let t=e._depsLength;t{const l=new Map;return l.cleanup=e,l.computed=t,l},Sn=new WeakMap,Zt=Symbol(""),Li=Symbol("");function Xe(e,t,l){if(Dt&&Qt){let n=Sn.get(e);n||Sn.set(e,n=new Map);let i=n.get(l);i||n.set(l,i=Ys(()=>n.delete(l))),qs(Qt,i)}}function xt(e,t,l,n,i,r){const s=Sn.get(e);if(!s)return;let o=[];if(t==="clear")o=[...s.values()];else if(l==="length"&&ne(e)){const a=Number(n);s.forEach((c,u)=>{(u==="length"||!xl(u)&&u>=a)&&o.push(c)})}else switch(l!==void 0&&o.push(s.get(l)),t){case"add":ne(e)?Hi(l)&&o.push(s.get("length")):(o.push(s.get(Zt)),hl(e)&&o.push(s.get(Li)));break;case"delete":ne(e)||(o.push(s.get(Zt)),hl(e)&&o.push(s.get(Li)));break;case"set":hl(e)&&o.push(s.get(Zt));break}Bi();for(const a of o)a&&Xs(a,4);ji()}function qa(e,t){var l;return(l=Sn.get(e))==null?void 0:l.get(t)}const Xa=$i("__proto__,__v_isRef,__isVue"),Js=new Set(Object.getOwnPropertyNames(Symbol).filter(e=>e!=="arguments"&&e!=="caller").map(e=>Symbol[e]).filter(xl)),xr=Ya();function Ya(){const e={};return["includes","indexOf","lastIndexOf"].forEach(t=>{e[t]=function(...l){const n=de(this);for(let r=0,s=this.length;r{e[t]=function(...l){il(),Bi();const n=de(this)[t].apply(this,l);return ji(),rl(),n}}),e}function Ja(e){const t=de(this);return Xe(t,"has",e),t.hasOwnProperty(e)}class Qs{constructor(t=!1,l=!1){this._isReadonly=t,this._shallow=l}get(t,l,n){const i=this._isReadonly,r=this._shallow;if(l==="__v_isReactive")return!i;if(l==="__v_isReadonly")return i;if(l==="__v_isShallow")return r;if(l==="__v_raw")return n===(i?r?uc:lo:r?to:eo).get(t)||Object.getPrototypeOf(t)===Object.getPrototypeOf(n)?t:void 0;const s=ne(t);if(!i){if(s&&ge(xr,l))return Reflect.get(xr,l,n);if(l==="hasOwnProperty")return Ja}const o=Reflect.get(t,l,n);return(xl(l)?Js.has(l):Xa(l))||(i||Xe(t,"get",l),r)?o:Ne(o)?s&&Hi(l)?o:o.value:ye(o)?i?Ql(o):Jl(o):o}}class Zs extends Qs{constructor(t=!1){super(!1,t)}set(t,l,n,i){let r=t[l];if(!this._shallow){const a=vl(r);if(!Pn(n)&&!vl(n)&&(r=de(r),n=de(n)),!ne(t)&&Ne(r)&&!Ne(n))return a?!1:(r.value=n,!0)}const s=ne(t)&&Hi(l)?Number(l)e,jn=e=>Reflect.getPrototypeOf(e);function cn(e,t,l=!1,n=!1){e=e.__v_raw;const i=de(e),r=de(t);l||(Mt(t,r)&&Xe(i,"get",t),Xe(i,"get",r));const{has:s}=jn(i),o=n?Ui:l?Ki:Ml;if(s.call(i,t))return o(e.get(t));if(s.call(i,r))return o(e.get(r));e!==i&&e.get(t)}function un(e,t=!1){const l=this.__v_raw,n=de(l),i=de(e);return t||(Mt(e,i)&&Xe(n,"has",e),Xe(n,"has",i)),e===i?l.has(e):l.has(e)||l.has(i)}function dn(e,t=!1){return e=e.__v_raw,!t&&Xe(de(e),"iterate",Zt),Reflect.get(e,"size",e)}function Er(e){e=de(e);const t=de(this);return jn(t).has.call(t,e)||(t.add(e),xt(t,"add",e,e)),this}function wr(e,t){t=de(t);const l=de(this),{has:n,get:i}=jn(l);let r=n.call(l,e);r||(e=de(e),r=n.call(l,e));const s=i.call(l,e);return l.set(e,t),r?Mt(t,s)&&xt(l,"set",e,t):xt(l,"add",e,t),this}function Lr(e){const t=de(this),{has:l,get:n}=jn(t);let i=l.call(t,e);i||(e=de(e),i=l.call(t,e)),n&&n.call(t,e);const r=t.delete(e);return i&&xt(t,"delete",e,void 0),r}function Cr(){const e=de(this),t=e.size!==0,l=e.clear();return t&&xt(e,"clear",void 0,void 0),l}function hn(e,t){return function(n,i){const r=this,s=r.__v_raw,o=de(s),a=t?Ui:e?Ki:Ml;return!e&&Xe(o,"iterate",Zt),s.forEach((c,u)=>n.call(i,a(c),a(u),r))}}function gn(e,t,l){return function(...n){const i=this.__v_raw,r=de(i),s=hl(r),o=e==="entries"||e===Symbol.iterator&&s,a=e==="keys"&&s,c=i[e](...n),u=l?Ui:t?Ki:Ml;return!t&&Xe(r,"iterate",a?Li:Zt),{next(){const{value:d,done:h}=c.next();return h?{value:d,done:h}:{value:o?[u(d[0]),u(d[1])]:u(d),done:h}},[Symbol.iterator](){return this}}}}function Tt(e){return function(...t){return e==="delete"?!1:e==="clear"?void 0:this}}function lc(){const e={get(r){return cn(this,r)},get size(){return dn(this)},has:un,add:Er,set:wr,delete:Lr,clear:Cr,forEach:hn(!1,!1)},t={get(r){return cn(this,r,!1,!0)},get size(){return dn(this)},has:un,add:Er,set:wr,delete:Lr,clear:Cr,forEach:hn(!1,!0)},l={get(r){return cn(this,r,!0)},get size(){return dn(this,!0)},has(r){return un.call(this,r,!0)},add:Tt("add"),set:Tt("set"),delete:Tt("delete"),clear:Tt("clear"),forEach:hn(!0,!1)},n={get(r){return cn(this,r,!0,!0)},get size(){return dn(this,!0)},has(r){return un.call(this,r,!0)},add:Tt("add"),set:Tt("set"),delete:Tt("delete"),clear:Tt("clear"),forEach:hn(!0,!0)};return["keys","values","entries",Symbol.iterator].forEach(r=>{e[r]=gn(r,!1,!1),l[r]=gn(r,!0,!1),t[r]=gn(r,!1,!0),n[r]=gn(r,!0,!0)}),[e,l,t,n]}const[nc,ic,rc,sc]=lc();function Wi(e,t){const l=t?e?sc:rc:e?ic:nc;return(n,i,r)=>i==="__v_isReactive"?!e:i==="__v_isReadonly"?e:i==="__v_raw"?n:Reflect.get(ge(l,i)&&i in n?l:n,i,r)}const oc={get:Wi(!1,!1)},ac={get:Wi(!1,!0)},cc={get:Wi(!0,!1)},eo=new WeakMap,to=new WeakMap,lo=new WeakMap,uc=new WeakMap;function dc(e){switch(e){case"Object":case"Array":return 1;case"Map":case"Set":case"WeakMap":case"WeakSet":return 2;default:return 0}}function hc(e){return e.__v_skip||!Object.isExtensible(e)?0:dc(Ra(e))}function Jl(e){return vl(e)?e:Gi(e,!1,Za,oc,eo)}function no(e){return Gi(e,!1,tc,ac,to)}function Ql(e){return Gi(e,!0,ec,cc,lo)}function Gi(e,t,l,n,i){if(!ye(e)||e.__v_raw&&!(t&&e.__v_isReactive))return e;const r=i.get(e);if(r)return r;const s=hc(e);if(s===0)return e;const o=new Proxy(e,s===2?n:l);return i.set(e,o),o}function pl(e){return vl(e)?pl(e.__v_raw):!!(e&&e.__v_isReactive)}function vl(e){return!!(e&&e.__v_isReadonly)}function Pn(e){return!!(e&&e.__v_isShallow)}function io(e){return pl(e)||vl(e)}function de(e){const t=e&&e.__v_raw;return t?de(t):e}function ro(e){return Object.isExtensible(e)&&Tn(e,"__v_skip",!0),e}const Ml=e=>ye(e)?Jl(e):e,Ki=e=>ye(e)?Ql(e):e;class so{constructor(t,l,n,i){this._setter=l,this.dep=void 0,this.__v_isRef=!0,this.__v_isReadonly=!1,this.effect=new Vi(()=>t(this._value),()=>Al(this,this.effect._dirtyLevel===2?2:3)),this.effect.computed=this,this.effect.active=this._cacheable=!i,this.__v_isReadonly=n}get value(){const t=de(this);return(!t._cacheable||t.effect.dirty)&&Mt(t._value,t._value=t.effect.run())&&Al(t,4),qi(t),t.effect._dirtyLevel>=2&&Al(t,2),t._value}set value(t){this._setter(t)}get _dirty(){return this.effect.dirty}set _dirty(t){this.effect.dirty=t}}function gc(e,t,l=!1){let n,i;const r=oe(e);return r?(n=e,i=lt):(n=e.get,i=e.set),new so(n,i,r||!i,l)}function qi(e){var t;Dt&&Qt&&(e=de(e),qs(Qt,(t=e.dep)!=null?t:e.dep=Ys(()=>e.dep=void 0,e instanceof so?e:void 0)))}function Al(e,t=4,l){e=de(e);const n=e.dep;n&&Xs(n,t)}function Ne(e){return!!(e&&e.__v_isRef===!0)}function ae(e){return oo(e,!1)}function sl(e){return oo(e,!0)}function oo(e,t){return Ne(e)?e:new pc(e,t)}class pc{constructor(t,l){this.__v_isShallow=l,this.dep=void 0,this.__v_isRef=!0,this._rawValue=l?t:de(t),this._value=l?t:Ml(t)}get value(){return qi(this),this._value}set value(t){const l=this.__v_isShallow||Pn(t)||vl(t);t=l?t:de(t),Mt(t,this._rawValue)&&(this._rawValue=t,this._value=l?t:Ml(t),Al(this,4))}}function Z(e){return Ne(e)?e.value:e}const fc={get:(e,t,l)=>Z(Reflect.get(e,t,l)),set:(e,t,l,n)=>{const i=e[t];return Ne(i)&&!Ne(l)?(i.value=l,!0):Reflect.set(e,t,l,n)}};function ao(e){return pl(e)?e:new Proxy(e,fc)}class mc{constructor(t){this.dep=void 0,this.__v_isRef=!0;const{get:l,set:n}=t(()=>qi(this),()=>Al(this));this._get=l,this._set=n}get value(){return this._get()}set value(t){this._set(t)}}function co(e){return new mc(e)}function Un(e){const t=ne(e)?new Array(e.length):{};for(const l in e)t[l]=uo(e,l);return t}class vc{constructor(t,l,n){this._object=t,this._key=l,this._defaultValue=n,this.__v_isRef=!0}get value(){const t=this._object[this._key];return t===void 0?this._defaultValue:t}set value(t){this._object[this._key]=t}get dep(){return qa(de(this._object),this._key)}}class kc{constructor(t){this._getter=t,this.__v_isRef=!0,this.__v_isReadonly=!0}get value(){return this._getter()}}function _c(e,t,l){return Ne(e)?e:oe(e)?new kc(e):ye(e)&&arguments.length>1?uo(e,t,l):ae(e)}function uo(e,t,l){const n=e[t];return Ne(n)?n:new vc(e,t,l)}/** +* @vue/runtime-core v3.4.19 +* (c) 2018-present Yuxi (Evan) You and Vue contributors +* @license MIT +**/function $t(e,t,l,n){try{return n?e(...n):e()}catch(i){Zl(i,t,l)}}function nt(e,t,l,n){if(oe(e)){const r=$t(e,t,l,n);return r&&Ns(r)&&r.catch(s=>{Zl(s,t,l)}),r}const i=[];for(let r=0;r>>1,i=Ve[n],r=Hl(i);rgt&&Ve.splice(t,1)}function Ec(e){ne(e)?fl.push(...e):(!At||!At.includes(e,e.allowRecurse?qt+1:qt))&&fl.push(e),go()}function Tr(e,t,l=Nl?gt+1:0){for(;lHl(l)-Hl(n));if(fl.length=0,At){At.push(...t);return}for(At=t,qt=0;qte.id==null?1/0:e.id,wc=(e,t)=>{const l=Hl(e)-Hl(t);if(l===0){if(e.pre&&!t.pre)return-1;if(t.pre&&!e.pre)return 1}return l};function po(e){Ci=!1,Nl=!0,Ve.sort(wc);try{for(gt=0;gtSe(p)?p.trim():p)),d&&(i=l.map(Da))}let o,a=n[o=ni(t)]||n[o=ni(it(t))];!a&&r&&(a=n[o=ni(nl(t))]),a&&nt(a,e,6,i);const c=n[o+"Once"];if(c){if(!e.emitted)e.emitted={};else if(e.emitted[o])return;e.emitted[o]=!0,nt(c,e,6,i)}}function fo(e,t,l=!1){const n=t.emitsCache,i=n.get(e);if(i!==void 0)return i;const r=e.emits;let s={},o=!1;if(!oe(e)){const a=c=>{const u=fo(c,t,!0);u&&(o=!0,Te(s,u))};!l&&t.mixins.length&&t.mixins.forEach(a),e.extends&&a(e.extends),e.mixins&&e.mixins.forEach(a)}return!r&&!o?(ye(e)&&n.set(e,null),null):(ne(r)?r.forEach(a=>s[a]=null):Te(s,r),ye(e)&&n.set(e,s),s)}function Gn(e,t){return!e||!ql(t)?!1:(t=t.slice(2).replace(/Once$/,""),ge(e,t[0].toLowerCase()+t.slice(1))||ge(e,nl(t))||ge(e,t))}let ze=null,mo=null;function An(e){const t=ze;return ze=e,mo=e&&e.type.__scopeId||null,t}function $e(e,t=ze,l){if(!t||e._n)return e;const n=(...i)=>{n._d&&Nr(-1);const r=An(t);let s;try{s=e(...i)}finally{An(r),n._d&&Nr(1)}return s};return n._n=!0,n._c=!0,n._d=!0,n}function si(e){const{type:t,vnode:l,proxy:n,withProxy:i,props:r,propsOptions:[s],slots:o,attrs:a,emit:c,render:u,renderCache:d,data:h,setupState:p,ctx:m,inheritAttrs:b}=e;let x,y;const S=An(e);try{if(l.shapeFlag&4){const w=i||n,H=w;x=st(u.call(H,w,d,r,p,h,m)),y=a}else{const w=t;x=st(w.length>1?w(r,{attrs:a,slots:o,emit:c}):w(r,null)),y=t.props?a:Cc(a)}}catch(w){Fl.length=0,Zl(w,e,1),x=ie(Qe)}let v=x;if(y&&b!==!1){const w=Object.keys(y),{shapeFlag:H}=v;w.length&&H&7&&(s&&w.some(Mi)&&(y=Tc(y,s)),v=Ht(v,y))}return l.dirs&&(v=Ht(v),v.dirs=v.dirs?v.dirs.concat(l.dirs):l.dirs),l.transition&&(v.transition=l.transition),x=v,An(S),x}const Cc=e=>{let t;for(const l in e)(l==="class"||l==="style"||ql(l))&&((t||(t={}))[l]=e[l]);return t},Tc=(e,t)=>{const l={};for(const n in e)(!Mi(n)||!(n.slice(9)in t))&&(l[n]=e[n]);return l};function Sc(e,t,l){const{props:n,children:i,component:r}=e,{props:s,children:o,patchFlag:a}=t,c=r.emitsOptions;if(t.dirs||t.transition)return!0;if(l&&a>=0){if(a&1024)return!0;if(a&16)return n?Sr(n,s,c):!!s;if(a&8){const u=t.dynamicProps;for(let d=0;de.__isSuspense;function ko(e,t){t&&t.pendingBranch?ne(e)?t.effects.push(...e):t.effects.push(e):Ec(e)}const Rc=Symbol.for("v-scx"),Ic=()=>Be(Rc);function Fc(e,t){return Yi(e,null,t)}const pn={};function Fe(e,t,l){return Yi(e,t,l)}function Yi(e,t,{immediate:l,deep:n,flush:i,once:r,onTrack:s,onTrigger:o}=xe){if(t&&r){const I=t;t=(...M)=>{I(...M),H()}}const a=Ie,c=I=>n===!0?I:Yt(I,n===!1?1:void 0);let u,d=!1,h=!1;if(Ne(e)?(u=()=>e.value,d=Pn(e)):pl(e)?(u=()=>c(e),d=!0):ne(e)?(h=!0,d=e.some(I=>pl(I)||Pn(I)),u=()=>e.map(I=>{if(Ne(I))return I.value;if(pl(I))return c(I);if(oe(I))return $t(I,a,2)})):oe(e)?t?u=()=>$t(e,a,2):u=()=>(p&&p(),nt(e,a,3,[m])):u=lt,t&&n){const I=u;u=()=>Yt(I())}let p,m=I=>{p=v.onStop=()=>{$t(I,a,4),p=v.onStop=void 0}},b;if(rn)if(m=lt,t?l&&nt(t,a,3,[u(),h?[]:void 0,m]):u(),i==="sync"){const I=Ic();b=I.__watcherHandles||(I.__watcherHandles=[])}else return lt;let x=h?new Array(e.length).fill(pn):pn;const y=()=>{if(!(!v.active||!v.dirty))if(t){const I=v.run();(n||d||(h?I.some((M,k)=>Mt(M,x[k])):Mt(I,x)))&&(p&&p(),nt(t,a,3,[I,x===pn?void 0:h&&x[0]===pn?[]:x,m]),x=I)}else v.run()};y.allowRecurse=!!t;let S;i==="sync"?S=y:i==="post"?S=()=>Ge(y,a&&a.suspense):(y.pre=!0,a&&(y.id=a.uid),S=()=>Wn(y));const v=new Vi(u,lt,S),w=Ws(),H=()=>{v.stop(),w&&Ni(w.effects,v)};return t?l?y():x=v.run():i==="post"?Ge(v.run.bind(v),a&&a.suspense):v.run(),b&&b.push(H),H}function Dc(e,t,l){const n=this.proxy,i=Se(e)?e.includes(".")?_o(n,e):()=>n[e]:e.bind(n,n);let r;oe(t)?r=t:(r=t.handler,l=t);const s=nn(this),o=Yi(i,r.bind(n),l);return s(),o}function _o(e,t){const l=t.split(".");return()=>{let n=e;for(let i=0;i0){if(l>=t)return e;l++}if(n=n||new Set,n.has(e))return e;if(n.add(e),Ne(e))Yt(e.value,t,l,n);else if(ne(e))for(let i=0;i{Yt(i,t,l,n)});else if(Vs(e))for(const i in e)Yt(e[i],t,l,n);return e}function On(e,t){if(ze===null)return e;const l=Yn(ze)||ze.proxy,n=e.dirs||(e.dirs=[]);for(let i=0;i{e.isMounted=!0}),qn(()=>{e.isUnmounting=!0}),e}const et=[Function,Array],yo={mode:String,appear:Boolean,persisted:Boolean,onBeforeEnter:et,onEnter:et,onAfterEnter:et,onEnterCancelled:et,onBeforeLeave:et,onLeave:et,onAfterLeave:et,onLeaveCancelled:et,onBeforeAppear:et,onAppear:et,onAfterAppear:et,onAppearCancelled:et},$c={name:"BaseTransition",props:yo,setup(e,{slots:t}){const l=ln(),n=bo();let i;return()=>{const r=t.default&&Ji(t.default(),!0);if(!r||!r.length)return;let s=r[0];if(r.length>1){for(const b of r)if(b.type!==Qe){s=b;break}}const o=de(e),{mode:a}=o;if(n.isLeaving)return oi(s);const c=zr(s);if(!c)return oi(s);const u=Vl(c,o,n,l);Bl(c,u);const d=l.subTree,h=d&&zr(d);let p=!1;const{getTransitionKey:m}=c.type;if(m){const b=m();i===void 0?i=b:b!==i&&(i=b,p=!0)}if(h&&h.type!==Qe&&(!Xt(c,h)||p)){const b=Vl(h,o,n,l);if(Bl(h,b),a==="out-in")return n.isLeaving=!0,b.afterLeave=()=>{n.isLeaving=!1,l.update.active!==!1&&(l.effect.dirty=!0,l.update())},oi(s);a==="in-out"&&c.type!==Qe&&(b.delayLeave=(x,y,S)=>{const v=xo(n,h);v[String(h.key)]=h,x[Ot]=()=>{y(),x[Ot]=void 0,delete u.delayedLeave},u.delayedLeave=S})}return s}}},Mc=$c;function xo(e,t){const{leavingVNodes:l}=e;let n=l.get(t.type);return n||(n=Object.create(null),l.set(t.type,n)),n}function Vl(e,t,l,n){const{appear:i,mode:r,persisted:s=!1,onBeforeEnter:o,onEnter:a,onAfterEnter:c,onEnterCancelled:u,onBeforeLeave:d,onLeave:h,onAfterLeave:p,onLeaveCancelled:m,onBeforeAppear:b,onAppear:x,onAfterAppear:y,onAppearCancelled:S}=t,v=String(e.key),w=xo(l,e),H=(k,N)=>{k&&nt(k,n,9,N)},I=(k,N)=>{const z=N[1];H(k,N),ne(k)?k.every(X=>X.length<=1)&&z():k.length<=1&&z()},M={mode:r,persisted:s,beforeEnter(k){let N=o;if(!l.isMounted)if(i)N=b||o;else return;k[Ot]&&k[Ot](!0);const z=w[v];z&&Xt(e,z)&&z.el[Ot]&&z.el[Ot](),H(N,[k])},enter(k){let N=a,z=c,X=u;if(!l.isMounted)if(i)N=x||a,z=y||c,X=S||u;else return;let L=!1;const $=k[fn]=le=>{L||(L=!0,le?H(X,[k]):H(z,[k]),M.delayedLeave&&M.delayedLeave(),k[fn]=void 0)};N?I(N,[k,$]):$()},leave(k,N){const z=String(e.key);if(k[fn]&&k[fn](!0),l.isUnmounting)return N();H(d,[k]);let X=!1;const L=k[Ot]=$=>{X||(X=!0,N(),$?H(m,[k]):H(p,[k]),k[Ot]=void 0,w[z]===e&&delete w[z])};w[z]=e,h?I(h,[k,L]):L()},clone(k){return Vl(k,t,l,n)}};return M}function oi(e){if(en(e))return e=Ht(e),e.children=null,e}function zr(e){return en(e)?e.children?e.children[0]:void 0:e}function Bl(e,t){e.shapeFlag&6&&e.component?Bl(e.component.subTree,t):e.shapeFlag&128?(e.ssContent.transition=t.clone(e.ssContent),e.ssFallback.transition=t.clone(e.ssFallback)):e.transition=t}function Ji(e,t=!1,l){let n=[],i=0;for(let r=0;r1)for(let r=0;r!!e.type.__asyncLoader;/*! #__NO_SIDE_EFFECTS__ */function Eo(e){oe(e)&&(e={loader:e});const{loader:t,loadingComponent:l,errorComponent:n,delay:i=200,timeout:r,suspensible:s=!0,onError:o}=e;let a=null,c,u=0;const d=()=>(u++,a=null,h()),h=()=>{let p;return a||(p=a=t().catch(m=>{if(m=m instanceof Error?m:new Error(String(m)),o)return new Promise((b,x)=>{o(m,()=>b(d()),()=>x(m),u+1)});throw m}).then(m=>p!==a&&a?a:(m&&(m.__esModule||m[Symbol.toStringTag]==="Module")&&(m=m.default),c=m,m)))};return fe({name:"AsyncComponentWrapper",__asyncLoader:h,get __asyncResolved(){return c},setup(){const p=Ie;if(c)return()=>ai(c,p);const m=S=>{a=null,Zl(S,p,13,!n)};if(s&&p.suspense||rn)return h().then(S=>()=>ai(S,p)).catch(S=>(m(S),()=>n?ie(n,{error:S}):null));const b=ae(!1),x=ae(),y=ae(!!i);return i&&setTimeout(()=>{y.value=!1},i),r!=null&&setTimeout(()=>{if(!b.value&&!x.value){const S=new Error(`Async component timed out after ${r}ms.`);m(S),x.value=S}},r),h().then(()=>{b.value=!0,p.parent&&en(p.parent.vnode)&&(p.parent.effect.dirty=!0,Wn(p.parent.update))}).catch(S=>{m(S),x.value=S}),()=>{if(b.value&&c)return ai(c,p);if(x.value&&n)return ie(n,{error:x.value});if(l&&!y.value)return ie(l)}}})}function ai(e,t){const{ref:l,props:n,children:i,ce:r}=t.vnode,s=ie(e,n,i);return s.ref=l,s.ce=r,delete t.vnode.ce,s}const en=e=>e.type.__isKeepAlive;function Nc(e,t){wo(e,"a",t)}function Hc(e,t){wo(e,"da",t)}function wo(e,t,l=Ie){const n=e.__wdc||(e.__wdc=()=>{let i=l;for(;i;){if(i.isDeactivated)return;i=i.parent}return e()});if(Kn(t,n,l),l){let i=l.parent;for(;i&&i.parent;)en(i.parent.vnode)&&Vc(n,t,l,i),i=i.parent}}function Vc(e,t,l,n){const i=Kn(t,e,n,!0);tn(()=>{Ni(n[t],i)},l)}function Kn(e,t,l=Ie,n=!1){if(l){const i=l[e]||(l[e]=[]),r=t.__weh||(t.__weh=(...s)=>{if(l.isUnmounted)return;il();const o=nn(l),a=nt(t,l,e,s);return o(),rl(),a});return n?i.unshift(r):i.push(r),r}}const wt=e=>(t,l=Ie)=>(!rn||e==="sp")&&Kn(e,(...n)=>t(...n),l),Bc=wt("bm"),je=wt("m"),jc=wt("bu"),Lo=wt("u"),qn=wt("bum"),tn=wt("um"),Uc=wt("sp"),Wc=wt("rtg"),Gc=wt("rtc");function Kc(e,t=Ie){Kn("ec",e,t)}function Nt(e,t,l,n){let i;const r=l&&l[n];if(ne(e)||Se(e)){i=new Array(e.length);for(let s=0,o=e.length;st(s,o,void 0,r&&r[o]));else{const s=Object.keys(e);i=new Array(s.length);for(let o=0,a=s.length;oFn(t)?!(t.type===Qe||t.type===_e&&!Co(t.children)):!0)?e:null}const Ti=e=>e?No(e)?Yn(e)||e.proxy:Ti(e.parent):null,Ol=Te(Object.create(null),{$:e=>e,$el:e=>e.vnode.el,$data:e=>e.data,$props:e=>e.props,$attrs:e=>e.attrs,$slots:e=>e.slots,$refs:e=>e.refs,$parent:e=>Ti(e.parent),$root:e=>Ti(e.root),$emit:e=>e.emit,$options:e=>Qi(e),$forceUpdate:e=>e.f||(e.f=()=>{e.effect.dirty=!0,Wn(e.update)}),$nextTick:e=>e.n||(e.n=El.bind(e.proxy)),$watch:e=>Dc.bind(e)}),ci=(e,t)=>e!==xe&&!e.__isScriptSetup&&ge(e,t),qc={get({_:e},t){const{ctx:l,setupState:n,data:i,props:r,accessCache:s,type:o,appContext:a}=e;let c;if(t[0]!=="$"){const p=s[t];if(p!==void 0)switch(p){case 1:return n[t];case 2:return i[t];case 4:return l[t];case 3:return r[t]}else{if(ci(n,t))return s[t]=1,n[t];if(i!==xe&&ge(i,t))return s[t]=2,i[t];if((c=e.propsOptions[0])&&ge(c,t))return s[t]=3,r[t];if(l!==xe&&ge(l,t))return s[t]=4,l[t];Si&&(s[t]=0)}}const u=Ol[t];let d,h;if(u)return t==="$attrs"&&Xe(e,"get",t),u(e);if((d=o.__cssModules)&&(d=d[t]))return d;if(l!==xe&&ge(l,t))return s[t]=4,l[t];if(h=a.config.globalProperties,ge(h,t))return h[t]},set({_:e},t,l){const{data:n,setupState:i,ctx:r}=e;return ci(i,t)?(i[t]=l,!0):n!==xe&&ge(n,t)?(n[t]=l,!0):ge(e.props,t)||t[0]==="$"&&t.slice(1)in e?!1:(r[t]=l,!0)},has({_:{data:e,setupState:t,accessCache:l,ctx:n,appContext:i,propsOptions:r}},s){let o;return!!l[s]||e!==xe&&ge(e,s)||ci(t,s)||(o=r[0])&&ge(o,s)||ge(n,s)||ge(Ol,s)||ge(i.config.globalProperties,s)},defineProperty(e,t,l){return l.get!=null?e._.accessCache[t]=0:ge(l,"value")&&this.set(e,t,l.value,null),Reflect.defineProperty(e,t,l)}};function Ar(e){return ne(e)?e.reduce((t,l)=>(t[l]=null,t),{}):e}let Si=!0;function Xc(e){const t=Qi(e),l=e.proxy,n=e.ctx;Si=!1,t.beforeCreate&&Or(t.beforeCreate,e,"bc");const{data:i,computed:r,methods:s,watch:o,provide:a,inject:c,created:u,beforeMount:d,mounted:h,beforeUpdate:p,updated:m,activated:b,deactivated:x,beforeDestroy:y,beforeUnmount:S,destroyed:v,unmounted:w,render:H,renderTracked:I,renderTriggered:M,errorCaptured:k,serverPrefetch:N,expose:z,inheritAttrs:X,components:L,directives:$,filters:le}=t;if(c&&Yc(c,n,null),s)for(const J in s){const G=s[J];oe(G)&&(n[J]=G.bind(l))}if(i){const J=i.call(l,l);ye(J)&&(e.data=Jl(J))}if(Si=!0,r)for(const J in r){const G=r[J],Ae=oe(G)?G.bind(l,l):oe(G.get)?G.get.bind(l,l):lt,De=!oe(G)&&oe(G.set)?G.set.bind(l):lt,We=O({get:Ae,set:De});Object.defineProperty(n,J,{enumerable:!0,configurable:!0,get:()=>We.value,set:He=>We.value=He})}if(o)for(const J in o)To(o[J],n,l,J);if(a){const J=oe(a)?a.call(l):a;Reflect.ownKeys(J).forEach(G=>{el(G,J[G])})}u&&Or(u,e,"c");function R(J,G){ne(G)?G.forEach(Ae=>J(Ae.bind(l))):G&&J(G.bind(l))}if(R(Bc,d),R(je,h),R(jc,p),R(Lo,m),R(Nc,b),R(Hc,x),R(Kc,k),R(Gc,I),R(Wc,M),R(qn,S),R(tn,w),R(Uc,N),ne(z))if(z.length){const J=e.exposed||(e.exposed={});z.forEach(G=>{Object.defineProperty(J,G,{get:()=>l[G],set:Ae=>l[G]=Ae})})}else e.exposed||(e.exposed={});H&&e.render===lt&&(e.render=H),X!=null&&(e.inheritAttrs=X),L&&(e.components=L),$&&(e.directives=$)}function Yc(e,t,l=lt){ne(e)&&(e=Pi(e));for(const n in e){const i=e[n];let r;ye(i)?"default"in i?r=Be(i.from||n,i.default,!0):r=Be(i.from||n):r=Be(i),Ne(r)?Object.defineProperty(t,n,{enumerable:!0,configurable:!0,get:()=>r.value,set:s=>r.value=s}):t[n]=r}}function Or(e,t,l){nt(ne(e)?e.map(n=>n.bind(t.proxy)):e.bind(t.proxy),t,l)}function To(e,t,l,n){const i=n.includes(".")?_o(l,n):()=>l[n];if(Se(e)){const r=t[e];oe(r)&&Fe(i,r)}else if(oe(e))Fe(i,e.bind(l));else if(ye(e))if(ne(e))e.forEach(r=>To(r,t,l,n));else{const r=oe(e.handler)?e.handler.bind(l):t[e.handler];oe(r)&&Fe(i,r,e)}}function Qi(e){const t=e.type,{mixins:l,extends:n}=t,{mixins:i,optionsCache:r,config:{optionMergeStrategies:s}}=e.appContext,o=r.get(t);let a;return o?a=o:!i.length&&!l&&!n?a=t:(a={},i.length&&i.forEach(c=>Rn(a,c,s,!0)),Rn(a,t,s)),ye(t)&&r.set(t,a),a}function Rn(e,t,l,n=!1){const{mixins:i,extends:r}=t;r&&Rn(e,r,l,!0),i&&i.forEach(s=>Rn(e,s,l,!0));for(const s in t)if(!(n&&s==="expose")){const o=Jc[s]||l&&l[s];e[s]=o?o(e[s],t[s]):t[s]}return e}const Jc={data:Rr,props:Ir,emits:Ir,methods:Pl,computed:Pl,beforeCreate:Ue,created:Ue,beforeMount:Ue,mounted:Ue,beforeUpdate:Ue,updated:Ue,beforeDestroy:Ue,beforeUnmount:Ue,destroyed:Ue,unmounted:Ue,activated:Ue,deactivated:Ue,errorCaptured:Ue,serverPrefetch:Ue,components:Pl,directives:Pl,watch:Zc,provide:Rr,inject:Qc};function Rr(e,t){return t?e?function(){return Te(oe(e)?e.call(this,this):e,oe(t)?t.call(this,this):t)}:t:e}function Qc(e,t){return Pl(Pi(e),Pi(t))}function Pi(e){if(ne(e)){const t={};for(let l=0;l1)return l&&oe(t)?t.call(n&&n.proxy):t}}function lu(e,t,l,n=!1){const i={},r={};Tn(r,Xn,1),e.propsDefaults=Object.create(null),Po(e,t,i,r);for(const s in e.propsOptions[0])s in i||(i[s]=void 0);l?e.props=n?i:no(i):e.type.props?e.props=i:e.props=r,e.attrs=r}function nu(e,t,l,n){const{props:i,attrs:r,vnode:{patchFlag:s}}=e,o=de(i),[a]=e.propsOptions;let c=!1;if((n||s>0)&&!(s&16)){if(s&8){const u=e.vnode.dynamicProps;for(let d=0;d{a=!0;const[h,p]=zo(d,t,!0);Te(s,h),p&&o.push(...p)};!l&&t.mixins.length&&t.mixins.forEach(u),e.extends&&u(e.extends),e.mixins&&e.mixins.forEach(u)}if(!r&&!a)return ye(e)&&n.set(e,dl),dl;if(ne(r))for(let u=0;u-1,p[1]=b<0||m-1||ge(p,"default"))&&o.push(d)}}}const c=[s,o];return ye(e)&&n.set(e,c),c}function Fr(e){return e[0]!=="$"&&!gl(e)}function Dr(e){return e===null?"null":typeof e=="function"?e.name||"":typeof e=="object"&&e.constructor&&e.constructor.name||""}function $r(e,t){return Dr(e)===Dr(t)}function Mr(e,t){return ne(t)?t.findIndex(l=>$r(l,e)):oe(t)&&$r(t,e)?0:-1}const Ao=e=>e[0]==="_"||e==="$stable",Zi=e=>ne(e)?e.map(st):[st(e)],iu=(e,t,l)=>{if(t._n)return t;const n=$e((...i)=>Zi(t(...i)),l);return n._c=!1,n},Oo=(e,t,l)=>{const n=e._ctx;for(const i in e){if(Ao(i))continue;const r=e[i];if(oe(r))t[i]=iu(i,r,n);else if(r!=null){const s=Zi(r);t[i]=()=>s}}},Ro=(e,t)=>{const l=Zi(t);e.slots.default=()=>l},ru=(e,t)=>{if(e.vnode.shapeFlag&32){const l=t._;l?(e.slots=de(t),Tn(t,"_",l)):Oo(t,e.slots={})}else e.slots={},t&&Ro(e,t);Tn(e.slots,Xn,1)},su=(e,t,l)=>{const{vnode:n,slots:i}=e;let r=!0,s=xe;if(n.shapeFlag&32){const o=t._;o?l&&o===1?r=!1:(Te(i,t),!l&&o===1&&delete i._):(r=!t.$stable,Oo(t,i)),s=t}else t&&(Ro(e,t),s={default:1});if(r)for(const o in i)!Ao(o)&&s[o]==null&&delete i[o]};function In(e,t,l,n,i=!1){if(ne(e)){e.forEach((h,p)=>In(h,t&&(ne(t)?t[p]:t),l,n,i));return}if(ml(n)&&!i)return;const r=n.shapeFlag&4?Yn(n.component)||n.component.proxy:n.el,s=i?null:r,{i:o,r:a}=e,c=t&&t.r,u=o.refs===xe?o.refs={}:o.refs,d=o.setupState;if(c!=null&&c!==a&&(Se(c)?(u[c]=null,ge(d,c)&&(d[c]=null)):Ne(c)&&(c.value=null)),oe(a))$t(a,o,12,[s,u]);else{const h=Se(a),p=Ne(a);if(h||p){const m=()=>{if(e.f){const b=h?ge(d,a)?d[a]:u[a]:a.value;i?ne(b)&&Ni(b,r):ne(b)?b.includes(r)||b.push(r):h?(u[a]=[r],ge(d,a)&&(d[a]=u[a])):(a.value=[r],e.k&&(u[e.k]=a.value))}else h?(u[a]=s,ge(d,a)&&(d[a]=s)):p&&(a.value=s,e.k&&(u[e.k]=s))};s?(m.id=-1,Ge(m,l)):m()}}}let St=!1;const ou=e=>e.namespaceURI.includes("svg")&&e.tagName!=="foreignObject",au=e=>e.namespaceURI.includes("MathML"),mn=e=>{if(ou(e))return"svg";if(au(e))return"mathml"},vn=e=>e.nodeType===8;function cu(e){const{mt:t,p:l,o:{patchProp:n,createText:i,nextSibling:r,parentNode:s,remove:o,insert:a,createComment:c}}=e,u=(v,w)=>{if(!w.hasChildNodes()){l(null,v,w),zn(),w._vnode=v;return}St=!1,d(w.firstChild,v,null,null,null),zn(),w._vnode=v,St&&console.error("Hydration completed but contains mismatches.")},d=(v,w,H,I,M,k=!1)=>{const N=vn(v)&&v.data==="[",z=()=>b(v,w,H,I,M,N),{type:X,ref:L,shapeFlag:$,patchFlag:le}=w;let re=v.nodeType;w.el=v,le===-2&&(k=!1,w.dynamicChildren=null);let R=null;switch(X){case kl:re!==3?w.children===""?(a(w.el=i(""),s(v),v),R=v):R=z():(v.data!==w.children&&(St=!0,v.data=w.children),R=r(v));break;case Qe:S(v)?(R=r(v),y(w.el=v.content.firstChild,v,H)):re!==8||N?R=z():R=r(v);break;case Il:if(N&&(v=r(v),re=v.nodeType),re===1||re===3){R=v;const J=!w.children.length;for(let G=0;G{k=k||!!w.dynamicChildren;const{type:N,props:z,patchFlag:X,shapeFlag:L,dirs:$,transition:le}=w,re=N==="input"||N==="option";if(re||X!==-1){$&&ht(w,null,H,"created");let R=!1;if(S(v)){R=Io(I,le)&&H&&H.vnode.props&&H.vnode.props.appear;const G=v.content.firstChild;R&&le.beforeEnter(G),y(G,v,H),w.el=v=G}if(L&16&&!(z&&(z.innerHTML||z.textContent))){let G=p(v.firstChild,w,v,H,I,M,k);for(;G;){St=!0;const Ae=G;G=G.nextSibling,o(Ae)}}else L&8&&v.textContent!==w.children&&(St=!0,v.textContent=w.children);if(z)if(re||!k||X&48)for(const G in z)(re&&(G.endsWith("value")||G==="indeterminate")||ql(G)&&!gl(G)||G[0]===".")&&n(v,G,null,z[G],void 0,void 0,H);else z.onClick&&n(v,"onClick",null,z.onClick,void 0,void 0,H);let J;(J=z&&z.onVnodeBeforeMount)&&tt(J,H,w),$&&ht(w,null,H,"beforeMount"),((J=z&&z.onVnodeMounted)||$||R)&&ko(()=>{J&&tt(J,H,w),R&&le.enter(v),$&&ht(w,null,H,"mounted")},I)}return v.nextSibling},p=(v,w,H,I,M,k,N)=>{N=N||!!w.dynamicChildren;const z=w.children,X=z.length;for(let L=0;L{const{slotScopeIds:N}=w;N&&(M=M?M.concat(N):N);const z=s(v),X=p(r(v),w,z,H,I,M,k);return X&&vn(X)&&X.data==="]"?r(w.anchor=X):(St=!0,a(w.anchor=c("]"),z,X),X)},b=(v,w,H,I,M,k)=>{if(St=!0,w.el=null,k){const X=x(v);for(;;){const L=r(v);if(L&&L!==X)o(L);else break}}const N=r(v),z=s(v);return o(v),l(null,w,z,N,H,I,mn(z),M),N},x=(v,w="[",H="]")=>{let I=0;for(;v;)if(v=r(v),v&&vn(v)&&(v.data===w&&I++,v.data===H)){if(I===0)return r(v);I--}return v},y=(v,w,H)=>{const I=w.parentNode;I&&I.replaceChild(v,w);let M=H;for(;M;)M.vnode.el===w&&(M.vnode.el=M.subTree.el=v),M=M.parent},S=v=>v.nodeType===1&&v.tagName.toLowerCase()==="template";return[u,d]}const Ge=ko;function uu(e){return du(e,cu)}function du(e,t){const l=Bs();l.__VUE__=!0;const{insert:n,remove:i,patchProp:r,createElement:s,createText:o,createComment:a,setText:c,setElementText:u,parentNode:d,nextSibling:h,setScopeId:p=lt,insertStaticContent:m}=e,b=(g,f,_,P=null,C=null,F=null,j=void 0,D=null,V=!!f.dynamicChildren)=>{if(g===f)return;g&&!Xt(g,f)&&(P=T(g),He(g,C,F,!0),g=null),f.patchFlag===-2&&(V=!1,f.dynamicChildren=null);const{type:A,ref:K,shapeFlag:te}=f;switch(A){case kl:x(g,f,_,P);break;case Qe:y(g,f,_,P);break;case Il:g==null&&S(f,_,P,j);break;case _e:L(g,f,_,P,C,F,j,D,V);break;default:te&1?H(g,f,_,P,C,F,j,D,V):te&6?$(g,f,_,P,C,F,j,D,V):(te&64||te&128)&&A.process(g,f,_,P,C,F,j,D,V,Y)}K!=null&&C&&In(K,g&&g.ref,F,f||g,!f)},x=(g,f,_,P)=>{if(g==null)n(f.el=o(f.children),_,P);else{const C=f.el=g.el;f.children!==g.children&&c(C,f.children)}},y=(g,f,_,P)=>{g==null?n(f.el=a(f.children||""),_,P):f.el=g.el},S=(g,f,_,P)=>{[g.el,g.anchor]=m(g.children,f,_,P,g.el,g.anchor)},v=({el:g,anchor:f},_,P)=>{let C;for(;g&&g!==f;)C=h(g),n(g,_,P),g=C;n(f,_,P)},w=({el:g,anchor:f})=>{let _;for(;g&&g!==f;)_=h(g),i(g),g=_;i(f)},H=(g,f,_,P,C,F,j,D,V)=>{f.type==="svg"?j="svg":f.type==="math"&&(j="mathml"),g==null?I(f,_,P,C,F,j,D,V):N(g,f,C,F,j,D,V)},I=(g,f,_,P,C,F,j,D)=>{let V,A;const{props:K,shapeFlag:te,transition:Q,dirs:se}=g;if(V=g.el=s(g.type,F,K&&K.is,K),te&8?u(V,g.children):te&16&&k(g.children,V,null,P,C,ui(g,F),j,D),se&&ht(g,null,P,"created"),M(V,g,g.scopeId,j,P),K){for(const ke in K)ke!=="value"&&!gl(ke)&&r(V,ke,null,K[ke],F,g.children,P,C,Oe);"value"in K&&r(V,"value",null,K.value,F),(A=K.onVnodeBeforeMount)&&tt(A,P,g)}se&&ht(g,null,P,"beforeMount");const ce=Io(C,Q);ce&&Q.beforeEnter(V),n(V,f,_),((A=K&&K.onVnodeMounted)||ce||se)&&Ge(()=>{A&&tt(A,P,g),ce&&Q.enter(V),se&&ht(g,null,P,"mounted")},C)},M=(g,f,_,P,C)=>{if(_&&p(g,_),P)for(let F=0;F{for(let A=V;A{const D=f.el=g.el;let{patchFlag:V,dynamicChildren:A,dirs:K}=f;V|=g.patchFlag&16;const te=g.props||xe,Q=f.props||xe;let se;if(_&&Ut(_,!1),(se=Q.onVnodeBeforeUpdate)&&tt(se,_,f,g),K&&ht(f,g,_,"beforeUpdate"),_&&Ut(_,!0),A?z(g.dynamicChildren,A,D,_,P,ui(f,C),F):j||G(g,f,D,null,_,P,ui(f,C),F,!1),V>0){if(V&16)X(D,f,te,Q,_,P,C);else if(V&2&&te.class!==Q.class&&r(D,"class",null,Q.class,C),V&4&&r(D,"style",te.style,Q.style,C),V&8){const ce=f.dynamicProps;for(let ke=0;ke{se&&tt(se,_,f,g),K&&ht(f,g,_,"updated")},P)},z=(g,f,_,P,C,F,j)=>{for(let D=0;D{if(_!==P){if(_!==xe)for(const D in _)!gl(D)&&!(D in P)&&r(g,D,_[D],null,j,f.children,C,F,Oe);for(const D in P){if(gl(D))continue;const V=P[D],A=_[D];V!==A&&D!=="value"&&r(g,D,A,V,j,f.children,C,F,Oe)}"value"in P&&r(g,"value",_.value,P.value,j)}},L=(g,f,_,P,C,F,j,D,V)=>{const A=f.el=g?g.el:o(""),K=f.anchor=g?g.anchor:o("");let{patchFlag:te,dynamicChildren:Q,slotScopeIds:se}=f;se&&(D=D?D.concat(se):se),g==null?(n(A,_,P),n(K,_,P),k(f.children||[],_,K,C,F,j,D,V)):te>0&&te&64&&Q&&g.dynamicChildren?(z(g.dynamicChildren,Q,_,C,F,j,D),(f.key!=null||C&&f===C.subTree)&&Fo(g,f,!0)):G(g,f,_,K,C,F,j,D,V)},$=(g,f,_,P,C,F,j,D,V)=>{f.slotScopeIds=D,g==null?f.shapeFlag&512?C.ctx.activate(f,_,P,j,V):le(f,_,P,C,F,j,V):re(g,f,V)},le=(g,f,_,P,C,F,j)=>{const D=g.component=bu(g,P,C);if(en(g)&&(D.ctx.renderer=Y),yu(D),D.asyncDep){if(C&&C.registerDep(D,R),!g.el){const V=D.subTree=ie(Qe);y(null,V,f,_)}}else R(D,g,f,_,C,F,j)},re=(g,f,_)=>{const P=f.component=g.component;if(Sc(g,f,_))if(P.asyncDep&&!P.asyncResolved){J(P,f,_);return}else P.next=f,xc(P.update),P.effect.dirty=!0,P.update();else f.el=g.el,P.vnode=f},R=(g,f,_,P,C,F,j)=>{const D=()=>{if(g.isMounted){let{next:K,bu:te,u:Q,parent:se,vnode:ce}=g;{const al=Do(g);if(al){K&&(K.el=ce.el,J(g,K,j)),al.asyncDep.then(()=>{g.isUnmounted||D()});return}}let ke=K,Ee;Ut(g,!1),K?(K.el=ce.el,J(g,K,j)):K=ce,te&&ii(te),(Ee=K.props&&K.props.onVnodeBeforeUpdate)&&tt(Ee,se,K,ce),Ut(g,!0);const Re=si(g),rt=g.subTree;g.subTree=Re,b(rt,Re,d(rt.el),T(rt),g,C,F),K.el=Re.el,ke===null&&Pc(g,Re.el),Q&&Ge(Q,C),(Ee=K.props&&K.props.onVnodeUpdated)&&Ge(()=>tt(Ee,se,K,ce),C)}else{let K;const{el:te,props:Q}=f,{bm:se,m:ce,parent:ke}=g,Ee=ml(f);if(Ut(g,!1),se&&ii(se),!Ee&&(K=Q&&Q.onVnodeBeforeMount)&&tt(K,ke,f),Ut(g,!0),te&&ve){const Re=()=>{g.subTree=si(g),ve(te,g.subTree,g,C,null)};Ee?f.type.__asyncLoader().then(()=>!g.isUnmounted&&Re()):Re()}else{const Re=g.subTree=si(g);b(null,Re,_,P,g,C,F),f.el=Re.el}if(ce&&Ge(ce,C),!Ee&&(K=Q&&Q.onVnodeMounted)){const Re=f;Ge(()=>tt(K,ke,Re),C)}(f.shapeFlag&256||ke&&ml(ke.vnode)&&ke.vnode.shapeFlag&256)&&g.a&&Ge(g.a,C),g.isMounted=!0,f=_=P=null}},V=g.effect=new Vi(D,lt,()=>Wn(A),g.scope),A=g.update=()=>{V.dirty&&V.run()};A.id=g.uid,Ut(g,!0),A()},J=(g,f,_)=>{f.component=g;const P=g.vnode.props;g.vnode=f,g.next=null,nu(g,f.props,P,_),su(g,f.children,_),il(),Tr(g),rl()},G=(g,f,_,P,C,F,j,D,V=!1)=>{const A=g&&g.children,K=g?g.shapeFlag:0,te=f.children,{patchFlag:Q,shapeFlag:se}=f;if(Q>0){if(Q&128){De(A,te,_,P,C,F,j,D,V);return}else if(Q&256){Ae(A,te,_,P,C,F,j,D,V);return}}se&8?(K&16&&Oe(A,C,F),te!==A&&u(_,te)):K&16?se&16?De(A,te,_,P,C,F,j,D,V):Oe(A,C,F,!0):(K&8&&u(_,""),se&16&&k(te,_,P,C,F,j,D,V))},Ae=(g,f,_,P,C,F,j,D,V)=>{g=g||dl,f=f||dl;const A=g.length,K=f.length,te=Math.min(A,K);let Q;for(Q=0;QK?Oe(g,C,F,!0,!1,te):k(f,_,P,C,F,j,D,V,te)},De=(g,f,_,P,C,F,j,D,V)=>{let A=0;const K=f.length;let te=g.length-1,Q=K-1;for(;A<=te&&A<=Q;){const se=g[A],ce=f[A]=V?Rt(f[A]):st(f[A]);if(Xt(se,ce))b(se,ce,_,null,C,F,j,D,V);else break;A++}for(;A<=te&&A<=Q;){const se=g[te],ce=f[Q]=V?Rt(f[Q]):st(f[Q]);if(Xt(se,ce))b(se,ce,_,null,C,F,j,D,V);else break;te--,Q--}if(A>te){if(A<=Q){const se=Q+1,ce=seQ)for(;A<=te;)He(g[A],C,F,!0),A++;else{const se=A,ce=A,ke=new Map;for(A=ce;A<=Q;A++){const Ye=f[A]=V?Rt(f[A]):st(f[A]);Ye.key!=null&&ke.set(Ye.key,A)}let Ee,Re=0;const rt=Q-ce+1;let al=!1,mr=0;const wl=new Array(rt);for(A=0;A=rt){He(Ye,C,F,!0);continue}let dt;if(Ye.key!=null)dt=ke.get(Ye.key);else for(Ee=ce;Ee<=Q;Ee++)if(wl[Ee-ce]===0&&Xt(Ye,f[Ee])){dt=Ee;break}dt===void 0?He(Ye,C,F,!0):(wl[dt-ce]=A+1,dt>=mr?mr=dt:al=!0,b(Ye,f[dt],_,null,C,F,j,D,V),Re++)}const vr=al?hu(wl):dl;for(Ee=vr.length-1,A=rt-1;A>=0;A--){const Ye=ce+A,dt=f[Ye],kr=Ye+1{const{el:F,type:j,transition:D,children:V,shapeFlag:A}=g;if(A&6){We(g.component.subTree,f,_,P);return}if(A&128){g.suspense.move(f,_,P);return}if(A&64){j.move(g,f,_,Y);return}if(j===_e){n(F,f,_);for(let te=0;teD.enter(F),C);else{const{leave:te,delayLeave:Q,afterLeave:se}=D,ce=()=>n(F,f,_),ke=()=>{te(F,()=>{ce(),se&&se()})};Q?Q(F,ce,ke):ke()}else n(F,f,_)},He=(g,f,_,P=!1,C=!1)=>{const{type:F,props:j,ref:D,children:V,dynamicChildren:A,shapeFlag:K,patchFlag:te,dirs:Q}=g;if(D!=null&&In(D,null,_,g,!0),K&256){f.ctx.deactivate(g);return}const se=K&1&&Q,ce=!ml(g);let ke;if(ce&&(ke=j&&j.onVnodeBeforeUnmount)&&tt(ke,f,g),K&6)ut(g.component,_,P);else{if(K&128){g.suspense.unmount(_,P);return}se&&ht(g,null,f,"beforeUnmount"),K&64?g.type.remove(g,f,_,C,Y,P):A&&(F!==_e||te>0&&te&64)?Oe(A,f,_,!1,!0):(F===_e&&te&384||!C&&K&16)&&Oe(V,f,_),P&&Lt(g)}(ce&&(ke=j&&j.onVnodeUnmounted)||se)&&Ge(()=>{ke&&tt(ke,f,g),se&&ht(g,null,f,"unmounted")},_)},Lt=g=>{const{type:f,el:_,anchor:P,transition:C}=g;if(f===_e){Ct(_,P);return}if(f===Il){w(g);return}const F=()=>{i(_),C&&!C.persisted&&C.afterLeave&&C.afterLeave()};if(g.shapeFlag&1&&C&&!C.persisted){const{leave:j,delayLeave:D}=C,V=()=>j(_,F);D?D(g.el,F,V):V()}else F()},Ct=(g,f)=>{let _;for(;g!==f;)_=h(g),i(g),g=_;i(f)},ut=(g,f,_)=>{const{bum:P,scope:C,update:F,subTree:j,um:D}=g;P&&ii(P),C.stop(),F&&(F.active=!1,He(j,g,f,_)),D&&Ge(D,f),Ge(()=>{g.isUnmounted=!0},f),f&&f.pendingBranch&&!f.isUnmounted&&g.asyncDep&&!g.asyncResolved&&g.suspenseId===f.pendingId&&(f.deps--,f.deps===0&&f.resolve())},Oe=(g,f,_,P=!1,C=!1,F=0)=>{for(let j=F;jg.shapeFlag&6?T(g.component.subTree):g.shapeFlag&128?g.suspense.next():h(g.anchor||g.el);let W=!1;const B=(g,f,_)=>{g==null?f._vnode&&He(f._vnode,null,null,!0):b(f._vnode||null,g,f,null,null,null,_),W||(W=!0,Tr(),zn(),W=!1),f._vnode=g},Y={p:b,um:He,m:We,r:Lt,mt:le,mc:k,pc:G,pbc:z,n:T,o:e};let ue,ve;return t&&([ue,ve]=t(Y)),{render:B,hydrate:ue,createApp:tu(B,ue)}}function ui({type:e,props:t},l){return l==="svg"&&e==="foreignObject"||l==="mathml"&&e==="annotation-xml"&&t&&t.encoding&&t.encoding.includes("html")?void 0:l}function Ut({effect:e,update:t},l){e.allowRecurse=t.allowRecurse=l}function Io(e,t){return(!e||e&&!e.pendingBranch)&&t&&!t.persisted}function Fo(e,t,l=!1){const n=e.children,i=t.children;if(ne(n)&&ne(i))for(let r=0;r>1,e[l[o]]0&&(t[n]=l[r-1]),l[r]=n)}}for(r=l.length,s=l[r-1];r-- >0;)l[r]=s,s=t[s];return l}function Do(e){const t=e.subTree.component;if(t)return t.asyncDep&&!t.asyncResolved?t:Do(t)}const gu=e=>e.__isTeleport,_e=Symbol.for("v-fgt"),kl=Symbol.for("v-txt"),Qe=Symbol.for("v-cmt"),Il=Symbol.for("v-stc"),Fl=[];let ot=null;function U(e=!1){Fl.push(ot=e?null:[])}function pu(){Fl.pop(),ot=Fl[Fl.length-1]||null}let jl=1;function Nr(e){jl+=e}function $o(e){return e.dynamicChildren=jl>0?ot||dl:null,pu(),jl>0&&ot&&ot.push(e),e}function ee(e,t,l,n,i,r){return $o(pe(e,t,l,n,i,r,!0))}function Le(e,t,l,n,i){return $o(ie(e,t,l,n,i,!0))}function Fn(e){return e?e.__v_isVNode===!0:!1}function Xt(e,t){return e.type===t.type&&e.key===t.key}const Xn="__vInternal",Mo=({key:e})=>e??null,wn=({ref:e,ref_key:t,ref_for:l})=>(typeof e=="number"&&(e=""+e),e!=null?Se(e)||Ne(e)||oe(e)?{i:ze,r:e,k:t,f:!!l}:e:null);function pe(e,t=null,l=null,n=0,i=null,r=e===_e?0:1,s=!1,o=!1){const a={__v_isVNode:!0,__v_skip:!0,type:e,props:t,key:t&&Mo(t),ref:t&&wn(t),scopeId:mo,slotScopeIds:null,children:l,component:null,suspense:null,ssContent:null,ssFallback:null,dirs:null,transition:null,el:null,anchor:null,target:null,targetAnchor:null,staticCount:0,shapeFlag:r,patchFlag:n,dynamicProps:i,dynamicChildren:null,appContext:null,ctx:ze};return o?(er(a,l),r&128&&e.normalize(a)):l&&(a.shapeFlag|=Se(l)?8:16),jl>0&&!s&&ot&&(a.patchFlag>0||r&6)&&a.patchFlag!==32&&ot.push(a),a}const ie=fu;function fu(e,t=null,l=null,n=0,i=null,r=!1){if((!e||e===zc)&&(e=Qe),Fn(e)){const o=Ht(e,t,!0);return l&&er(o,l),jl>0&&!r&&ot&&(o.shapeFlag&6?ot[ot.indexOf(e)]=o:ot.push(o)),o.patchFlag|=-2,o}if(Cu(e)&&(e=e.__vccOpts),t){t=mu(t);let{class:o,style:a}=t;o&&!Se(o)&&(t.class=Ke(o)),ye(a)&&(io(a)&&!ne(a)&&(a=Te({},a)),t.style=Yl(a))}const s=Se(e)?1:Oc(e)?128:gu(e)?64:ye(e)?4:oe(e)?2:0;return pe(e,t,l,n,i,s,r,!0)}function mu(e){return e?io(e)||Xn in e?Te({},e):e:null}function Ht(e,t,l=!1){const{props:n,ref:i,patchFlag:r,children:s}=e,o=t?Ai(n||{},t):n;return{__v_isVNode:!0,__v_skip:!0,type:e.type,props:o,key:o&&Mo(o),ref:t&&t.ref?l&&i?ne(i)?i.concat(wn(t)):[i,wn(t)]:wn(t):i,scopeId:e.scopeId,slotScopeIds:e.slotScopeIds,children:s,target:e.target,targetAnchor:e.targetAnchor,staticCount:e.staticCount,shapeFlag:e.shapeFlag,patchFlag:t&&e.type!==_e?r===-1?16:r|16:r,dynamicProps:e.dynamicProps,dynamicChildren:e.dynamicChildren,appContext:e.appContext,dirs:e.dirs,transition:e.transition,component:e.component,suspense:e.suspense,ssContent:e.ssContent&&Ht(e.ssContent),ssFallback:e.ssFallback&&Ht(e.ssFallback),el:e.el,anchor:e.anchor,ctx:e.ctx,ce:e.ce}}function Vt(e=" ",t=0){return ie(kl,null,e,t)}function vu(e,t){const l=ie(Il,null,e);return l.staticCount=t,l}function Ce(e="",t=!1){return t?(U(),Le(Qe,null,e)):ie(Qe,null,e)}function st(e){return e==null||typeof e=="boolean"?ie(Qe):ne(e)?ie(_e,null,e.slice()):typeof e=="object"?Rt(e):ie(kl,null,String(e))}function Rt(e){return e.el===null&&e.patchFlag!==-1||e.memo?e:Ht(e)}function er(e,t){let l=0;const{shapeFlag:n}=e;if(t==null)t=null;else if(ne(t))l=16;else if(typeof t=="object")if(n&65){const i=t.default;i&&(i._c&&(i._d=!1),er(e,i()),i._c&&(i._d=!0));return}else{l=32;const i=t._;!i&&!(Xn in t)?t._ctx=ze:i===3&&ze&&(ze.slots._===1?t._=1:(t._=2,e.patchFlag|=1024))}else oe(t)?(t={default:t,_ctx:ze},l=32):(t=String(t),n&64?(l=16,t=[Vt(t)]):l=8);e.children=t,e.shapeFlag|=l}function Ai(...e){const t={};for(let l=0;lIe||ze;let Dn,Oi;{const e=Bs(),t=(l,n)=>{let i;return(i=e[l])||(i=e[l]=[]),i.push(n),r=>{i.length>1?i.forEach(s=>s(r)):i[0](r)}};Dn=t("__VUE_INSTANCE_SETTERS__",l=>Ie=l),Oi=t("__VUE_SSR_SETTERS__",l=>rn=l)}const nn=e=>{const t=Ie;return Dn(e),e.scope.on(),()=>{e.scope.off(),Dn(t)}},Hr=()=>{Ie&&Ie.scope.off(),Dn(null)};function No(e){return e.vnode.shapeFlag&4}let rn=!1;function yu(e,t=!1){t&&Oi(t);const{props:l,children:n}=e.vnode,i=No(e);lu(e,l,i,t),ru(e,n);const r=i?xu(e,t):void 0;return t&&Oi(!1),r}function xu(e,t){const l=e.type;e.accessCache=Object.create(null),e.proxy=ro(new Proxy(e.ctx,qc));const{setup:n}=l;if(n){const i=e.setupContext=n.length>1?wu(e):null,r=nn(e);il();const s=$t(n,e,0,[e.props,i]);if(rl(),r(),Ns(s)){if(s.then(Hr,Hr),t)return s.then(o=>{Vr(e,o,t)}).catch(o=>{Zl(o,e,0)});e.asyncDep=s}else Vr(e,s,t)}else Ho(e,t)}function Vr(e,t,l){oe(t)?e.type.__ssrInlineRender?e.ssrRender=t:e.render=t:ye(t)&&(e.setupState=ao(t)),Ho(e,l)}let Br;function Ho(e,t,l){const n=e.type;if(!e.render){if(!t&&Br&&!n.render){const i=n.template||Qi(e).template;if(i){const{isCustomElement:r,compilerOptions:s}=e.appContext.config,{delimiters:o,compilerOptions:a}=n,c=Te(Te({isCustomElement:r,delimiters:o},s),a);n.render=Br(i,c)}}e.render=n.render||lt}{const i=nn(e);il();try{Xc(e)}finally{rl(),i()}}}function Eu(e){return e.attrsProxy||(e.attrsProxy=new Proxy(e.attrs,{get(t,l){return Xe(e,"get","$attrs"),t[l]}}))}function wu(e){const t=l=>{e.exposed=l||{}};return{get attrs(){return Eu(e)},slots:e.slots,emit:e.emit,expose:t}}function Yn(e){if(e.exposed)return e.exposeProxy||(e.exposeProxy=new Proxy(ao(ro(e.exposed)),{get(t,l){if(l in t)return t[l];if(l in Ol)return Ol[l](e)},has(t,l){return l in t||l in Ol}}))}function Lu(e,t=!0){return oe(e)?e.displayName||e.name:e.name||t&&e.__name}function Cu(e){return oe(e)&&"__vccOpts"in e}const O=(e,t)=>gc(e,t,rn);function q(e,t,l){const n=arguments.length;return n===2?ye(t)&&!ne(t)?Fn(t)?ie(e,null,[t]):ie(e,t):ie(e,null,t):(n>3?l=Array.prototype.slice.call(arguments,2):n===3&&Fn(l)&&(l=[l]),ie(e,t,l))}const Tu="3.4.19";/** +* @vue/runtime-dom v3.4.19 +* (c) 2018-present Yuxi (Evan) You and Vue contributors +* @license MIT +**/const Su="http://www.w3.org/2000/svg",Pu="http://www.w3.org/1998/Math/MathML",It=typeof document<"u"?document:null,jr=It&&It.createElement("template"),zu={insert:(e,t,l)=>{t.insertBefore(e,l||null)},remove:e=>{const t=e.parentNode;t&&t.removeChild(e)},createElement:(e,t,l,n)=>{const i=t==="svg"?It.createElementNS(Su,e):t==="mathml"?It.createElementNS(Pu,e):It.createElement(e,l?{is:l}:void 0);return e==="select"&&n&&n.multiple!=null&&i.setAttribute("multiple",n.multiple),i},createText:e=>It.createTextNode(e),createComment:e=>It.createComment(e),setText:(e,t)=>{e.nodeValue=t},setElementText:(e,t)=>{e.textContent=t},parentNode:e=>e.parentNode,nextSibling:e=>e.nextSibling,querySelector:e=>It.querySelector(e),setScopeId(e,t){e.setAttribute(t,"")},insertStaticContent(e,t,l,n,i,r){const s=l?l.previousSibling:t.lastChild;if(i&&(i===r||i.nextSibling))for(;t.insertBefore(i.cloneNode(!0),l),!(i===r||!(i=i.nextSibling)););else{jr.innerHTML=n==="svg"?`${e}`:n==="mathml"?`${e}`:e;const o=jr.content;if(n==="svg"||n==="mathml"){const a=o.firstChild;for(;a.firstChild;)o.appendChild(a.firstChild);o.removeChild(a)}t.insertBefore(o,l)}return[s?s.nextSibling:t.firstChild,l?l.previousSibling:t.lastChild]}},Pt="transition",Ll="animation",_l=Symbol("_vtc"),sn=(e,{slots:t})=>q(Mc,Bo(e),t);sn.displayName="Transition";const Vo={name:String,type:String,css:{type:Boolean,default:!0},duration:[String,Number,Object],enterFromClass:String,enterActiveClass:String,enterToClass:String,appearFromClass:String,appearActiveClass:String,appearToClass:String,leaveFromClass:String,leaveActiveClass:String,leaveToClass:String},Au=sn.props=Te({},yo,Vo),Wt=(e,t=[])=>{ne(e)?e.forEach(l=>l(...t)):e&&e(...t)},Ur=e=>e?ne(e)?e.some(t=>t.length>1):e.length>1:!1;function Bo(e){const t={};for(const L in e)L in Vo||(t[L]=e[L]);if(e.css===!1)return t;const{name:l="v",type:n,duration:i,enterFromClass:r=`${l}-enter-from`,enterActiveClass:s=`${l}-enter-active`,enterToClass:o=`${l}-enter-to`,appearFromClass:a=r,appearActiveClass:c=s,appearToClass:u=o,leaveFromClass:d=`${l}-leave-from`,leaveActiveClass:h=`${l}-leave-active`,leaveToClass:p=`${l}-leave-to`}=e,m=Ou(i),b=m&&m[0],x=m&&m[1],{onBeforeEnter:y,onEnter:S,onEnterCancelled:v,onLeave:w,onLeaveCancelled:H,onBeforeAppear:I=y,onAppear:M=S,onAppearCancelled:k=v}=t,N=(L,$,le)=>{zt(L,$?u:o),zt(L,$?c:s),le&&le()},z=(L,$)=>{L._isLeaving=!1,zt(L,d),zt(L,p),zt(L,h),$&&$()},X=L=>($,le)=>{const re=L?M:S,R=()=>N($,L,le);Wt(re,[$,R]),Wr(()=>{zt($,L?a:r),_t($,L?u:o),Ur(re)||Gr($,n,b,R)})};return Te(t,{onBeforeEnter(L){Wt(y,[L]),_t(L,r),_t(L,s)},onBeforeAppear(L){Wt(I,[L]),_t(L,a),_t(L,c)},onEnter:X(!1),onAppear:X(!0),onLeave(L,$){L._isLeaving=!0;const le=()=>z(L,$);_t(L,d),Uo(),_t(L,h),Wr(()=>{L._isLeaving&&(zt(L,d),_t(L,p),Ur(w)||Gr(L,n,x,le))}),Wt(w,[L,le])},onEnterCancelled(L){N(L,!1),Wt(v,[L])},onAppearCancelled(L){N(L,!0),Wt(k,[L])},onLeaveCancelled(L){z(L),Wt(H,[L])}})}function Ou(e){if(e==null)return null;if(ye(e))return[di(e.enter),di(e.leave)];{const t=di(e);return[t,t]}}function di(e){return $a(e)}function _t(e,t){t.split(/\s+/).forEach(l=>l&&e.classList.add(l)),(e[_l]||(e[_l]=new Set)).add(t)}function zt(e,t){t.split(/\s+/).forEach(n=>n&&e.classList.remove(n));const l=e[_l];l&&(l.delete(t),l.size||(e[_l]=void 0))}function Wr(e){requestAnimationFrame(()=>{requestAnimationFrame(e)})}let Ru=0;function Gr(e,t,l,n){const i=e._endId=++Ru,r=()=>{i===e._endId&&n()};if(l)return setTimeout(r,l);const{type:s,timeout:o,propCount:a}=jo(e,t);if(!s)return n();const c=s+"end";let u=0;const d=()=>{e.removeEventListener(c,h),r()},h=p=>{p.target===e&&++u>=a&&d()};setTimeout(()=>{u(l[m]||"").split(", "),i=n(`${Pt}Delay`),r=n(`${Pt}Duration`),s=Kr(i,r),o=n(`${Ll}Delay`),a=n(`${Ll}Duration`),c=Kr(o,a);let u=null,d=0,h=0;t===Pt?s>0&&(u=Pt,d=s,h=r.length):t===Ll?c>0&&(u=Ll,d=c,h=a.length):(d=Math.max(s,c),u=d>0?s>c?Pt:Ll:null,h=u?u===Pt?r.length:a.length:0);const p=u===Pt&&/\b(transform|all)(,|$)/.test(n(`${Pt}Property`).toString());return{type:u,timeout:d,propCount:h,hasTransform:p}}function Kr(e,t){for(;e.lengthqr(l)+qr(e[n])))}function qr(e){return e==="auto"?0:Number(e.slice(0,-1).replace(",","."))*1e3}function Uo(){return document.body.offsetHeight}function Iu(e,t,l){const n=e[_l];n&&(t=(t?[t,...n]:[...n]).join(" ")),t==null?e.removeAttribute("class"):l?e.setAttribute("class",t):e.className=t}const Ul=Symbol("_vod"),$n={beforeMount(e,{value:t},{transition:l}){e[Ul]=e.style.display==="none"?"":e.style.display,l&&t?l.beforeEnter(e):Cl(e,t)},mounted(e,{value:t},{transition:l}){l&&t&&l.enter(e)},updated(e,{value:t,oldValue:l},{transition:n}){!t==!l&&(e.style.display===e[Ul]||!t)||(n?t?(n.beforeEnter(e),Cl(e,!0),n.enter(e)):n.leave(e,()=>{Cl(e,!1)}):Cl(e,t))},beforeUnmount(e,{value:t}){Cl(e,t)}};function Cl(e,t){e.style.display=t?e[Ul]:"none"}const Fu=Symbol(""),Du=/(^|;)\s*display\s*:/;function $u(e,t,l){const n=e.style,i=Se(l),r=n.display;let s=!1;if(l&&!i){if(t&&!Se(t))for(const o in t)l[o]==null&&Ri(n,o,"");for(const o in l)o==="display"&&(s=!0),Ri(n,o,l[o])}else if(i){if(t!==l){const o=n[Fu];o&&(l+=";"+o),n.cssText=l,s=Du.test(l)}}else t&&e.removeAttribute("style");Ul in e&&(e[Ul]=s?n.display:"",n.display=r)}const Xr=/\s*!important$/;function Ri(e,t,l){if(ne(l))l.forEach(n=>Ri(e,t,n));else if(l==null&&(l=""),t.startsWith("--"))e.setProperty(t,l);else{const n=Mu(e,t);Xr.test(l)?e.setProperty(nl(n),l.replace(Xr,""),"important"):e[n]=l}}const Yr=["Webkit","Moz","ms"],hi={};function Mu(e,t){const l=hi[t];if(l)return l;let n=it(t);if(n!=="filter"&&n in e)return hi[t]=n;n=Xl(n);for(let i=0;igi||(Wu.then(()=>gi=0),gi=Date.now());function Ku(e,t){const l=n=>{if(!n._vts)n._vts=Date.now();else if(n._vts<=l.attached)return;nt(qu(n,l.value),t,5,[n])};return l.value=e,l.attached=Gu(),l}function qu(e,t){if(ne(t)){const l=e.stopImmediatePropagation;return e.stopImmediatePropagation=()=>{l.call(e),e._stopped=!0},t.map(n=>i=>!i._stopped&&n&&n(i))}else return t}const es=e=>e.charCodeAt(0)===111&&e.charCodeAt(1)===110&&e.charCodeAt(2)>96&&e.charCodeAt(2)<123,Xu=(e,t,l,n,i,r,s,o,a)=>{const c=i==="svg";t==="class"?Iu(e,n,c):t==="style"?$u(e,l,n):ql(t)?Mi(t)||ju(e,t,l,n,s):(t[0]==="."?(t=t.slice(1),!0):t[0]==="^"?(t=t.slice(1),!1):Yu(e,t,n,c))?Hu(e,t,n,r,s,o,a):(t==="true-value"?e._trueValue=n:t==="false-value"&&(e._falseValue=n),Nu(e,t,n,c))};function Yu(e,t,l,n){if(n)return!!(t==="innerHTML"||t==="textContent"||t in e&&es(t)&&oe(l));if(t==="spellcheck"||t==="draggable"||t==="translate"||t==="form"||t==="list"&&e.tagName==="INPUT"||t==="type"&&e.tagName==="TEXTAREA")return!1;if(t==="width"||t==="height"){const i=e.tagName;if(i==="IMG"||i==="VIDEO"||i==="CANVAS"||i==="SOURCE")return!1}return es(t)&&Se(l)?!1:t in e}const Wo=new WeakMap,Go=new WeakMap,Mn=Symbol("_moveCb"),ts=Symbol("_enterCb"),Ko={name:"TransitionGroup",props:Te({},Au,{tag:String,moveClass:String}),setup(e,{slots:t}){const l=ln(),n=bo();let i,r;return Lo(()=>{if(!i.length)return;const s=e.moveClass||`${e.name||"v"}-move`;if(!ld(i[0].el,l.vnode.el,s))return;i.forEach(Zu),i.forEach(ed);const o=i.filter(td);Uo(),o.forEach(a=>{const c=a.el,u=c.style;_t(c,s),u.transform=u.webkitTransform=u.transitionDuration="";const d=c[Mn]=h=>{h&&h.target!==c||(!h||/transform$/.test(h.propertyName))&&(c.removeEventListener("transitionend",d),c[Mn]=null,zt(c,s))};c.addEventListener("transitionend",d)})}),()=>{const s=de(e),o=Bo(s);let a=s.tag||_e;i=r,r=t.default?Ji(t.default()):[];for(let c=0;cdelete e.mode;Ko.props;const Qu=Ko;function Zu(e){const t=e.el;t[Mn]&&t[Mn](),t[ts]&&t[ts]()}function ed(e){Go.set(e,e.el.getBoundingClientRect())}function td(e){const t=Wo.get(e),l=Go.get(e),n=t.left-l.left,i=t.top-l.top;if(n||i){const r=e.el.style;return r.transform=r.webkitTransform=`translate(${n}px,${i}px)`,r.transitionDuration="0s",e}}function ld(e,t,l){const n=e.cloneNode(),i=e[_l];i&&i.forEach(o=>{o.split(/\s+/).forEach(a=>a&&n.classList.remove(a))}),l.split(/\s+/).forEach(o=>o&&n.classList.add(o)),n.style.display="none";const r=t.nodeType===1?t:t.parentNode;r.appendChild(n);const{hasTransform:s}=jo(n);return r.removeChild(n),s}const nd={esc:"escape",space:" ",up:"arrow-up",left:"arrow-left",right:"arrow-right",down:"arrow-down",delete:"backspace"},id=(e,t)=>{const l=e._withKeys||(e._withKeys={}),n=t.join(".");return l[n]||(l[n]=i=>{if(!("key"in i))return;const r=nl(i.key);if(t.some(s=>s===r||nd[s]===r))return e(i)})},rd=Te({patchProp:Xu},zu);let pi,ls=!1;function sd(){return pi=ls?pi:uu(rd),ls=!0,pi}const od=(...e)=>{const t=sd().createApp(...e),{mount:l}=t;return t.mount=n=>{const i=cd(n);if(i)return l(i,!0,ad(i))},t};function ad(e){if(e instanceof SVGElement)return"svg";if(typeof MathMLElement=="function"&&e instanceof MathMLElement)return"mathml"}function cd(e){return Se(e)?document.querySelector(e):e}var ud=["link","meta","script","style","noscript","template"],dd=["title","base"],hd=([e,t,l])=>dd.includes(e)?e:ud.includes(e)?e==="meta"&&t.name?`${e}.${t.name}`:e==="template"&&t.id?`${e}.${t.id}`:JSON.stringify([e,Object.entries(t).map(([n,i])=>typeof i=="boolean"?i?[n,""]:null:[n,i]).filter(n=>n!=null).sort(([n],[i])=>n.localeCompare(i)),l]):null,gd=e=>{const t=new Set,l=[];return e.forEach(n=>{const i=hd(n);i&&!t.has(i)&&(t.add(i),l.push(n))}),l},pd=e=>e[0]==="/"?e:`/${e}`,fd=e=>e[e.length-1]==="/"||e.endsWith(".html")?e:`${e}/`,on=e=>/^(https?:)?\/\//.test(e),md=e=>/^[a-z][a-z0-9+.-]*:/.test(e),Jn=e=>Object.prototype.toString.call(e)==="[object Object]",vd=e=>{const[t,...l]=e.split(/(\?|#)/);if(!t||t.endsWith("/"))return e;let n=t.replace(/(^|\/)README.md$/i,"$1index.html");return n.endsWith(".md")?n=n.substring(0,n.length-3)+".html":n.endsWith(".html")||(n=n+".html"),n.endsWith("/index.html")&&(n=n.substring(0,n.length-10)),n+l.join("")},qo=e=>e[e.length-1]==="/"?e.slice(0,-1):e,Xo=e=>e[0]==="/"?e.slice(1):e,Yo=(e,t)=>{const l=Object.keys(e).sort((n,i)=>{const r=i.split("/").length-n.split("/").length;return r!==0?r:i.length-n.length});for(const n of l)if(t.startsWith(n))return n;return"/"},qe=e=>typeof e=="string";const kd="modulepreload",_d=function(e){return"/ecosystem/"+e},ns={},E=function(t,l,n){let i=Promise.resolve();if(l&&l.length>0){const r=document.getElementsByTagName("link");i=Promise.all(l.map(s=>{if(s=_d(s),s in ns)return;ns[s]=!0;const o=s.endsWith(".css"),a=o?'[rel="stylesheet"]':"";if(!!n)for(let d=r.length-1;d>=0;d--){const h=r[d];if(h.href===s&&(!o||h.rel==="stylesheet"))return}else if(document.querySelector(`link[href="${s}"]${a}`))return;const u=document.createElement("link");if(u.rel=o?"stylesheet":kd,o||(u.as="script",u.crossOrigin=""),u.href=s,document.head.appendChild(u),o)return new Promise((d,h)=>{u.addEventListener("load",d),u.addEventListener("error",()=>h(new Error(`Unable to preload CSS for ${s}`)))})}))}return i.then(()=>t()).catch(r=>{const s=new Event("vite:preloadError",{cancelable:!0});if(s.payload=r,window.dispatchEvent(s),!s.defaultPrevented)throw r})},bd=JSON.parse("{}"),yd=Object.fromEntries([["/",{loader:()=>E(()=>import("./index.html-NGgl-cMu.js"),__vite__mapDeps([])),meta:{title:"Home"}}],["/themes/",{loader:()=>E(()=>import("./index.html-Y1wYcIZr.js"),__vite__mapDeps([])),meta:{title:"Themes"}}],["/plugins/",{loader:()=>E(()=>import("./index.html-BiVwZvP3.js"),__vite__mapDeps([])),meta:{title:"Plugins"}}],["/plugins/active-header-links.html",{loader:()=>E(()=>import("./active-header-links.html-BidXn9_T.js"),__vite__mapDeps([])),meta:{title:"active-header-links"}}],["/plugins/back-to-top.html",{loader:()=>E(()=>import("./back-to-top.html-DIJmeRQG.js"),__vite__mapDeps([])),meta:{title:"back-to-top"}}],["/plugins/baidu-analytics.html",{loader:()=>E(()=>import("./baidu-analytics.html-B5FBeNCL.js"),__vite__mapDeps([])),meta:{title:"baidu-analytics"}}],["/plugins/catalog.html",{loader:()=>E(()=>import("./catalog.html-DGD3v_PG.js"),__vite__mapDeps([])),meta:{title:"catalog"}}],["/plugins/container.html",{loader:()=>E(()=>import("./container.html-Fg-TBurh.js"),__vite__mapDeps([])),meta:{title:"container"}}],["/plugins/copy-code.html",{loader:()=>E(()=>import("./copy-code.html-DVC_b5GJ.js"),__vite__mapDeps([])),meta:{title:"copy-code"}}],["/plugins/copyright.html",{loader:()=>E(()=>import("./copyright.html-xIj0WcRd.js"),__vite__mapDeps([])),meta:{title:"copyright"}}],["/plugins/docsearch.html",{loader:()=>E(()=>import("./docsearch.html-B3tKWbdo.js"),__vite__mapDeps([])),meta:{title:"docsearch"}}],["/plugins/external-link-icon.html",{loader:()=>E(()=>import("./external-link-icon.html-oHMFewKb.js"),__vite__mapDeps([])),meta:{title:"external-link-icon"}}],["/plugins/git.html",{loader:()=>E(()=>import("./git.html-CMUkyCND.js"),__vite__mapDeps([])),meta:{title:"git"}}],["/plugins/google-analytics.html",{loader:()=>E(()=>import("./google-analytics.html-OhT-gEp5.js"),__vite__mapDeps([])),meta:{title:"google-analytics"}}],["/plugins/medium-zoom.html",{loader:()=>E(()=>import("./medium-zoom.html-DBOIkrrJ.js"),__vite__mapDeps([])),meta:{title:"medium-zoom"}}],["/plugins/nprogress.html",{loader:()=>E(()=>import("./nprogress.html-fM7jj7yn.js"),__vite__mapDeps([])),meta:{title:"nprogress"}}],["/plugins/palette.html",{loader:()=>E(()=>import("./palette.html-CT6-ohwO.js"),__vite__mapDeps([])),meta:{title:"palette"}}],["/plugins/photo-swipe.html",{loader:()=>E(()=>import("./photo-swipe.html-CVrJSAOJ.js"),__vite__mapDeps([])),meta:{title:"photo-swipe"}}],["/plugins/prismjs.html",{loader:()=>E(()=>import("./prismjs.html-D5rV4std.js"),__vite__mapDeps([])),meta:{title:"prismjs"}}],["/plugins/reading-time.html",{loader:()=>E(()=>import("./reading-time.html-DUOmNho8.js"),__vite__mapDeps([])),meta:{title:"reading-time"}}],["/plugins/redirect.html",{loader:()=>E(()=>import("./redirect.html-CQdnRl7D.js"),__vite__mapDeps([])),meta:{title:"redirect"}}],["/plugins/register-components.html",{loader:()=>E(()=>import("./register-components.html-nZSf746Y.js"),__vite__mapDeps([])),meta:{title:"register-components"}}],["/plugins/remove-pwa.html",{loader:()=>E(()=>import("./remove-pwa.html-DyC9esSj.js"),__vite__mapDeps([])),meta:{title:"remove-pwa"}}],["/plugins/rtl.html",{loader:()=>E(()=>import("./rtl.html-DbExLeFw.js"),__vite__mapDeps([])),meta:{title:"rtl"}}],["/plugins/search.html",{loader:()=>E(()=>import("./search.html-BNGeFq23.js"),__vite__mapDeps([])),meta:{title:"search"}}],["/plugins/shiki.html",{loader:()=>E(()=>import("./shiki.html-Br5bIad7.js"),__vite__mapDeps([])),meta:{title:"shiki"}}],["/plugins/theme-data.html",{loader:()=>E(()=>import("./theme-data.html-CH0nXsCv.js"),__vite__mapDeps([])),meta:{title:"theme-data"}}],["/plugins/toc.html",{loader:()=>E(()=>import("./toc.html-B5VRzXZc.js"),__vite__mapDeps([])),meta:{title:"toc"}}],["/zh/",{loader:()=>E(()=>import("./index.html-10UTHdZ8.js"),__vite__mapDeps([])),meta:{title:"首页"}}],["/themes/default/",{loader:()=>E(()=>import("./index.html-CYCrKtK1.js"),__vite__mapDeps([])),meta:{title:"theme-default"}}],["/themes/default/components.html",{loader:()=>E(()=>import("./components.html-BLsJeb2o.js"),__vite__mapDeps([])),meta:{title:"Built-in Components"}}],["/themes/default/config.html",{loader:()=>E(()=>import("./config.html-BqDWUA_G.js"),__vite__mapDeps([])),meta:{title:"Config"}}],["/themes/default/extending.html",{loader:()=>E(()=>import("./extending.html-B-2YhQDL.js"),__vite__mapDeps([0,1])),meta:{title:"Extending"}}],["/themes/default/frontmatter.html",{loader:()=>E(()=>import("./frontmatter.html-CS29ziHh.js"),__vite__mapDeps([])),meta:{title:"Frontmatter"}}],["/themes/default/locale.html",{loader:()=>E(()=>import("./locale.html-CBiyLOGJ.js"),__vite__mapDeps([])),meta:{title:"Locale Config"}}],["/themes/default/markdown.html",{loader:()=>E(()=>import("./markdown.html-QGS-wLl9.js"),__vite__mapDeps([])),meta:{title:"Markdown"}}],["/themes/default/plugin.html",{loader:()=>E(()=>import("./plugin.html-DpcJp1-q.js"),__vite__mapDeps([])),meta:{title:"Plugins Config"}}],["/themes/default/styles.html",{loader:()=>E(()=>import("./styles.html-DfDnb8qj.js"),__vite__mapDeps([])),meta:{title:"Styles"}}],["/plugins/blog/",{loader:()=>E(()=>import("./index.html-BFeBzGTv.js"),__vite__mapDeps([])),meta:{title:"blog"}}],["/plugins/blog/config.html",{loader:()=>E(()=>import("./config.html-FMBVZuNJ.js"),__vite__mapDeps([])),meta:{title:"Config"}}],["/plugins/blog/guide.html",{loader:()=>E(()=>import("./guide.html-6nwdbxs_.js"),__vite__mapDeps([])),meta:{title:"Guide"}}],["/plugins/feed/",{loader:()=>E(()=>import("./index.html-BszZ_ZuB.js"),__vite__mapDeps([])),meta:{title:"feed"}}],["/plugins/feed/channel.html",{loader:()=>E(()=>import("./channel.html-MBD5TiIP.js"),__vite__mapDeps([])),meta:{title:"Channel Config"}}],["/plugins/feed/config.html",{loader:()=>E(()=>import("./config.html-BiDaVbw2.js"),__vite__mapDeps([])),meta:{title:"Plugin Config"}}],["/plugins/feed/frontmatter.html",{loader:()=>E(()=>import("./frontmatter.html-BzPSllnx.js"),__vite__mapDeps([])),meta:{title:"Frontmatter Config"}}],["/plugins/feed/getter.html",{loader:()=>E(()=>import("./getter.html-DWNp6lVm.js"),__vite__mapDeps([])),meta:{title:"Feed Getter"}}],["/plugins/feed/guide.html",{loader:()=>E(()=>import("./guide.html-BguhT2bm.js"),__vite__mapDeps([])),meta:{title:"Guide"}}],["/plugins/pwa/",{loader:()=>E(()=>import("./index.html-ia4XVneA.js"),__vite__mapDeps([])),meta:{title:"pwa"}}],["/plugins/pwa/config.html",{loader:()=>E(()=>import("./config.html-DGoed1yi.js"),__vite__mapDeps([])),meta:{title:"Config"}}],["/plugins/pwa/guide.html",{loader:()=>E(()=>import("./guide.html-BNXyZOh6.js"),__vite__mapDeps([])),meta:{title:"Guide"}}],["/plugins/seo/",{loader:()=>E(()=>import("./index.html-C94lRzr8.js"),__vite__mapDeps([])),meta:{title:"seo"}}],["/plugins/seo/config.html",{loader:()=>E(()=>import("./config.html-D8ayWeGZ.js"),__vite__mapDeps([])),meta:{title:"Config"}}],["/plugins/seo/guide.html",{loader:()=>E(()=>import("./guide.html-BVrSzIwm.js"),__vite__mapDeps([])),meta:{title:"Guide"}}],["/plugins/sitemap/",{loader:()=>E(()=>import("./index.html-6kqMvCYf.js"),__vite__mapDeps([])),meta:{title:"sitemap"}}],["/plugins/sitemap/config.html",{loader:()=>E(()=>import("./config.html-DfnnBIqz.js"),__vite__mapDeps([])),meta:{title:"Config"}}],["/plugins/sitemap/frontmatter.html",{loader:()=>E(()=>import("./frontmatter.html-Dm0TiI4m.js"),__vite__mapDeps([])),meta:{title:"Frontmatter"}}],["/plugins/sitemap/guide.html",{loader:()=>E(()=>import("./guide.html-Bnwyqjr4.js"),__vite__mapDeps([])),meta:{title:"Guide"}}],["/tools/helper/",{loader:()=>E(()=>import("./index.html-CN0Aolsb.js"),__vite__mapDeps([])),meta:{title:"@vuepress/helper"}}],["/tools/helper/client.html",{loader:()=>E(()=>import("./client.html-GE5WwRt5.js"),__vite__mapDeps([])),meta:{title:"Client Related"}}],["/tools/helper/shared.html",{loader:()=>E(()=>import("./shared.html-BZlae7Rh.js"),__vite__mapDeps([])),meta:{title:"Shared Methods"}}],["/zh/plugins/",{loader:()=>E(()=>import("./index.html-l9neTtpK.js"),__vite__mapDeps([])),meta:{title:"插件"}}],["/zh/plugins/active-header-links.html",{loader:()=>E(()=>import("./active-header-links.html-DkiUbm-s.js"),__vite__mapDeps([])),meta:{title:"active-header-links"}}],["/zh/plugins/back-to-top.html",{loader:()=>E(()=>import("./back-to-top.html-DWZAoYSt.js"),__vite__mapDeps([])),meta:{title:"back-to-top"}}],["/zh/plugins/baidu-analytics.html",{loader:()=>E(()=>import("./baidu-analytics.html-AobDAH-_.js"),__vite__mapDeps([])),meta:{title:"baidu-analytics"}}],["/zh/plugins/catalog.html",{loader:()=>E(()=>import("./catalog.html-ZgM6JxDU.js"),__vite__mapDeps([])),meta:{title:"catalog"}}],["/zh/plugins/container.html",{loader:()=>E(()=>import("./container.html-CcAV80DR.js"),__vite__mapDeps([])),meta:{title:"container"}}],["/zh/plugins/copy-code.html",{loader:()=>E(()=>import("./copy-code.html-Dl2NXwK3.js"),__vite__mapDeps([])),meta:{title:"copy-code"}}],["/zh/plugins/copyright.html",{loader:()=>E(()=>import("./copyright.html-Cjv27u5V.js"),__vite__mapDeps([])),meta:{title:"copyright"}}],["/zh/plugins/docsearch.html",{loader:()=>E(()=>import("./docsearch.html-D42wByNJ.js"),__vite__mapDeps([])),meta:{title:"docsearch"}}],["/zh/plugins/external-link-icon.html",{loader:()=>E(()=>import("./external-link-icon.html-CkGGjwAB.js"),__vite__mapDeps([])),meta:{title:"external-link-icon"}}],["/zh/plugins/git.html",{loader:()=>E(()=>import("./git.html-bRRpxIHY.js"),__vite__mapDeps([])),meta:{title:"git"}}],["/zh/plugins/google-analytics.html",{loader:()=>E(()=>import("./google-analytics.html-R2_6EU47.js"),__vite__mapDeps([])),meta:{title:"google-analytics"}}],["/zh/plugins/medium-zoom.html",{loader:()=>E(()=>import("./medium-zoom.html-BrYLaxXk.js"),__vite__mapDeps([])),meta:{title:"medium-zoom"}}],["/zh/plugins/nprogress.html",{loader:()=>E(()=>import("./nprogress.html-D6c03qSI.js"),__vite__mapDeps([])),meta:{title:"nprogress"}}],["/zh/plugins/palette.html",{loader:()=>E(()=>import("./palette.html-CrWHHv51.js"),__vite__mapDeps([])),meta:{title:"palette"}}],["/zh/plugins/photo-swipe.html",{loader:()=>E(()=>import("./photo-swipe.html-Dw7C8ObA.js"),__vite__mapDeps([])),meta:{title:"photo-swipe"}}],["/zh/plugins/prismjs.html",{loader:()=>E(()=>import("./prismjs.html-CmrkmCnz.js"),__vite__mapDeps([])),meta:{title:"prismjs"}}],["/zh/plugins/reading-time.html",{loader:()=>E(()=>import("./reading-time.html-CPp8fwlZ.js"),__vite__mapDeps([])),meta:{title:"reading-time"}}],["/zh/plugins/redirect.html",{loader:()=>E(()=>import("./redirect.html-wE1TgcR8.js"),__vite__mapDeps([])),meta:{title:"redirect"}}],["/zh/plugins/register-components.html",{loader:()=>E(()=>import("./register-components.html-CSH-WB-N.js"),__vite__mapDeps([])),meta:{title:"register-components"}}],["/zh/plugins/remove-pwa.html",{loader:()=>E(()=>import("./remove-pwa.html-obHjlz2h.js"),__vite__mapDeps([])),meta:{title:"remove-pwa"}}],["/zh/plugins/rtl.html",{loader:()=>E(()=>import("./rtl.html-COWcRLLy.js"),__vite__mapDeps([])),meta:{title:"rtl"}}],["/zh/plugins/search.html",{loader:()=>E(()=>import("./search.html-DIFheM8W.js"),__vite__mapDeps([])),meta:{title:"search"}}],["/zh/plugins/shiki.html",{loader:()=>E(()=>import("./shiki.html-CIFvEyyj.js"),__vite__mapDeps([])),meta:{title:"shiki"}}],["/zh/plugins/theme-data.html",{loader:()=>E(()=>import("./theme-data.html-D41xVb4s.js"),__vite__mapDeps([])),meta:{title:"theme-data"}}],["/zh/plugins/toc.html",{loader:()=>E(()=>import("./toc.html-CHxAKS6q.js"),__vite__mapDeps([])),meta:{title:"toc"}}],["/zh/themes/",{loader:()=>E(()=>import("./index.html-BNzZzBwI.js"),__vite__mapDeps([])),meta:{title:"主题"}}],["/tools/helper/node/bundler.html",{loader:()=>E(()=>import("./bundler.html-9zmlqkgM.js"),__vite__mapDeps([])),meta:{title:"Bundler Related"}}],["/tools/helper/node/page.html",{loader:()=>E(()=>import("./page.html-DygfZiqD.js"),__vite__mapDeps([])),meta:{title:"Page Related"}}],["/zh/plugins/blog/",{loader:()=>E(()=>import("./index.html-3S31PeiG.js"),__vite__mapDeps([])),meta:{title:"blog"}}],["/zh/plugins/blog/config.html",{loader:()=>E(()=>import("./config.html-Dc4epx8_.js"),__vite__mapDeps([])),meta:{title:"配置"}}],["/zh/plugins/blog/guide.html",{loader:()=>E(()=>import("./guide.html-DMncVq9E.js"),__vite__mapDeps([])),meta:{title:"指南"}}],["/zh/plugins/feed/",{loader:()=>E(()=>import("./index.html-YB60ZmkU.js"),__vite__mapDeps([])),meta:{title:"feed"}}],["/zh/plugins/feed/channel.html",{loader:()=>E(()=>import("./channel.html-CzU5ykfZ.js"),__vite__mapDeps([])),meta:{title:"频道设置"}}],["/zh/plugins/feed/config.html",{loader:()=>E(()=>import("./config.html-DcZlIOB_.js"),__vite__mapDeps([])),meta:{title:"插件配置"}}],["/zh/plugins/feed/frontmatter.html",{loader:()=>E(()=>import("./frontmatter.html-JmG15wiP.js"),__vite__mapDeps([])),meta:{title:"Frontmatter 配置"}}],["/zh/plugins/feed/getter.html",{loader:()=>E(()=>import("./getter.html-CRfx9SLW.js"),__vite__mapDeps([])),meta:{title:"Feed 获取器"}}],["/zh/plugins/feed/guide.html",{loader:()=>E(()=>import("./guide.html-BXx1nNKq.js"),__vite__mapDeps([])),meta:{title:"指南"}}],["/zh/plugins/pwa/",{loader:()=>E(()=>import("./index.html-4jsKLsHE.js"),__vite__mapDeps([])),meta:{title:"pwa"}}],["/zh/plugins/pwa/config.html",{loader:()=>E(()=>import("./config.html-BLNioz4j.js"),__vite__mapDeps([])),meta:{title:"配置"}}],["/zh/plugins/pwa/guide.html",{loader:()=>E(()=>import("./guide.html-I8tYpU3u.js"),__vite__mapDeps([])),meta:{title:"指南"}}],["/zh/plugins/seo/",{loader:()=>E(()=>import("./index.html-Crkx8NN1.js"),__vite__mapDeps([])),meta:{title:"seo"}}],["/zh/plugins/seo/config.html",{loader:()=>E(()=>import("./config.html-DYMVh00U.js"),__vite__mapDeps([])),meta:{title:"选项"}}],["/zh/plugins/seo/guide.html",{loader:()=>E(()=>import("./guide.html-Dt9xe1YY.js"),__vite__mapDeps([])),meta:{title:"指南"}}],["/zh/plugins/sitemap/",{loader:()=>E(()=>import("./index.html-B1QMRWf6.js"),__vite__mapDeps([])),meta:{title:"sitemap"}}],["/zh/plugins/sitemap/config.html",{loader:()=>E(()=>import("./config.html-BLh0zTBN.js"),__vite__mapDeps([])),meta:{title:"配置"}}],["/zh/plugins/sitemap/frontmatter.html",{loader:()=>E(()=>import("./frontmatter.html-CjXlpGGX.js"),__vite__mapDeps([])),meta:{title:"Frontmatter"}}],["/zh/plugins/sitemap/guide.html",{loader:()=>E(()=>import("./guide.html-MqAl9Hwl.js"),__vite__mapDeps([])),meta:{title:"指南"}}],["/zh/themes/default/",{loader:()=>E(()=>import("./index.html-Cq71pHF1.js"),__vite__mapDeps([])),meta:{title:"默认主题"}}],["/zh/themes/default/components.html",{loader:()=>E(()=>import("./components.html-D_m26OMv.js"),__vite__mapDeps([])),meta:{title:"内置组件"}}],["/zh/themes/default/config.html",{loader:()=>E(()=>import("./config.html-ixjaZyxV.js"),__vite__mapDeps([])),meta:{title:"配置"}}],["/zh/themes/default/extending.html",{loader:()=>E(()=>import("./extending.html-C_vYwTyq.js"),__vite__mapDeps([2,1])),meta:{title:"继承"}}],["/zh/themes/default/frontmatter.html",{loader:()=>E(()=>import("./frontmatter.html-CZ3v91x9.js"),__vite__mapDeps([])),meta:{title:"Frontmatter"}}],["/zh/themes/default/locale.html",{loader:()=>E(()=>import("./locale.html-DZtMkHzN.js"),__vite__mapDeps([])),meta:{title:"语言配置"}}],["/zh/themes/default/markdown.html",{loader:()=>E(()=>import("./markdown.html-BFz9lbN1.js"),__vite__mapDeps([])),meta:{title:"Markdown"}}],["/zh/themes/default/plugin.html",{loader:()=>E(()=>import("./plugin.html-Dsd8bOpV.js"),__vite__mapDeps([])),meta:{title:"插件配置"}}],["/zh/themes/default/styles.html",{loader:()=>E(()=>import("./styles.html-Ab_-qJux.js"),__vite__mapDeps([])),meta:{title:"样式"}}],["/zh/tools/helper/",{loader:()=>E(()=>import("./index.html-BEvkK6U_.js"),__vite__mapDeps([])),meta:{title:"@vuepress/helper"}}],["/zh/tools/helper/client.html",{loader:()=>E(()=>import("./client.html-DnXt2EtS.js"),__vite__mapDeps([])),meta:{title:"客户端相关"}}],["/zh/tools/helper/shared.html",{loader:()=>E(()=>import("./shared.html-CRDbFVNx.js"),__vite__mapDeps([])),meta:{title:"共享方法"}}],["/zh/tools/helper/node/bundler.html",{loader:()=>E(()=>import("./bundler.html-lrm24igQ.js"),__vite__mapDeps([])),meta:{title:"打包器相关"}}],["/zh/tools/helper/node/page.html",{loader:()=>E(()=>import("./page.html-CWQo-Wbb.js"),__vite__mapDeps([])),meta:{title:"页面相关"}}],["/404.html",{loader:()=>E(()=>import("./404.html-Clxd7bFP.js"),__vite__mapDeps([])),meta:{title:""}}],["/tools/",{loader:()=>E(()=>import("./index.html-D5ijgUnW.js"),__vite__mapDeps([])),meta:{title:"Tools"}}],["/tools/helper/node/",{loader:()=>E(()=>import("./index.html-C8BL235-.js"),__vite__mapDeps([])),meta:{title:"Node"}}],["/zh/tools/",{loader:()=>E(()=>import("./index.html-dNgmFSRH.js"),__vite__mapDeps([])),meta:{title:"Tools"}}],["/zh/tools/helper/node/",{loader:()=>E(()=>import("./index.html-B8jQgsFD.js"),__vite__mapDeps([])),meta:{title:"Node"}}]]);/*! + * vue-router v4.2.5 + * (c) 2023 Eduardo San Martin Morote + * @license MIT + */const ul=typeof window<"u";function xd(e){return e.__esModule||e[Symbol.toStringTag]==="Module"}const me=Object.assign;function fi(e,t){const l={};for(const n in t){const i=t[n];l[n]=at(i)?i.map(e):e(i)}return l}const Dl=()=>{},at=Array.isArray,Ed=/\/$/,wd=e=>e.replace(Ed,"");function mi(e,t,l="/"){let n,i={},r="",s="";const o=t.indexOf("#");let a=t.indexOf("?");return o=0&&(a=-1),a>-1&&(n=t.slice(0,a),r=t.slice(a+1,o>-1?o:t.length),i=e(r)),o>-1&&(n=n||t.slice(0,o),s=t.slice(o,t.length)),n=Sd(n??t,l),{fullPath:n+(r&&"?")+r+s,path:n,query:i,hash:s}}function Ld(e,t){const l=t.query?e(t.query):"";return t.path+(l&&"?")+l+(t.hash||"")}function is(e,t){return!t||!e.toLowerCase().startsWith(t.toLowerCase())?e:e.slice(t.length)||"/"}function Cd(e,t,l){const n=t.matched.length-1,i=l.matched.length-1;return n>-1&&n===i&&bl(t.matched[n],l.matched[i])&&Jo(t.params,l.params)&&e(t.query)===e(l.query)&&t.hash===l.hash}function bl(e,t){return(e.aliasOf||e)===(t.aliasOf||t)}function Jo(e,t){if(Object.keys(e).length!==Object.keys(t).length)return!1;for(const l in e)if(!Td(e[l],t[l]))return!1;return!0}function Td(e,t){return at(e)?rs(e,t):at(t)?rs(t,e):e===t}function rs(e,t){return at(t)?e.length===t.length&&e.every((l,n)=>l===t[n]):e.length===1&&e[0]===t}function Sd(e,t){if(e.startsWith("/"))return e;if(!e)return t;const l=t.split("/"),n=e.split("/"),i=n[n.length-1];(i===".."||i===".")&&n.push("");let r=l.length-1,s,o;for(s=0;s1&&r--;else break;return l.slice(0,r).join("/")+"/"+n.slice(s-(s===n.length?1:0)).join("/")}var Wl;(function(e){e.pop="pop",e.push="push"})(Wl||(Wl={}));var $l;(function(e){e.back="back",e.forward="forward",e.unknown=""})($l||($l={}));function Pd(e){if(!e)if(ul){const t=document.querySelector("base");e=t&&t.getAttribute("href")||"/",e=e.replace(/^\w+:\/\/[^\/]+/,"")}else e="/";return e[0]!=="/"&&e[0]!=="#"&&(e="/"+e),wd(e)}const zd=/^[^#]+#/;function Ad(e,t){return e.replace(zd,"#")+t}function Od(e,t){const l=document.documentElement.getBoundingClientRect(),n=e.getBoundingClientRect();return{behavior:t.behavior,left:n.left-l.left-(t.left||0),top:n.top-l.top-(t.top||0)}}const Qn=()=>({left:window.pageXOffset,top:window.pageYOffset});function Rd(e){let t;if("el"in e){const l=e.el,n=typeof l=="string"&&l.startsWith("#"),i=typeof l=="string"?n?document.getElementById(l.slice(1)):document.querySelector(l):l;if(!i)return;t=Od(i,e)}else t=e;"scrollBehavior"in document.documentElement.style?window.scrollTo(t):window.scrollTo(t.left!=null?t.left:window.pageXOffset,t.top!=null?t.top:window.pageYOffset)}function ss(e,t){return(history.state?history.state.position-t:-1)+e}const Ii=new Map;function Id(e,t){Ii.set(e,t)}function Fd(e){const t=Ii.get(e);return Ii.delete(e),t}let Dd=()=>location.protocol+"//"+location.host;function Qo(e,t){const{pathname:l,search:n,hash:i}=t,r=e.indexOf("#");if(r>-1){let o=i.includes(e.slice(r))?e.slice(r).length:1,a=i.slice(o);return a[0]!=="/"&&(a="/"+a),is(a,"")}return is(l,e)+n+i}function $d(e,t,l,n){let i=[],r=[],s=null;const o=({state:h})=>{const p=Qo(e,location),m=l.value,b=t.value;let x=0;if(h){if(l.value=p,t.value=h,s&&s===m){s=null;return}x=b?h.position-b.position:0}else n(p);i.forEach(y=>{y(l.value,m,{delta:x,type:Wl.pop,direction:x?x>0?$l.forward:$l.back:$l.unknown})})};function a(){s=l.value}function c(h){i.push(h);const p=()=>{const m=i.indexOf(h);m>-1&&i.splice(m,1)};return r.push(p),p}function u(){const{history:h}=window;h.state&&h.replaceState(me({},h.state,{scroll:Qn()}),"")}function d(){for(const h of r)h();r=[],window.removeEventListener("popstate",o),window.removeEventListener("beforeunload",u)}return window.addEventListener("popstate",o),window.addEventListener("beforeunload",u,{passive:!0}),{pauseListeners:a,listen:c,destroy:d}}function os(e,t,l,n=!1,i=!1){return{back:e,current:t,forward:l,replaced:n,position:window.history.length,scroll:i?Qn():null}}function Md(e){const{history:t,location:l}=window,n={value:Qo(e,l)},i={value:t.state};i.value||r(n.value,{back:null,current:n.value,forward:null,position:t.length-1,replaced:!0,scroll:null},!0);function r(a,c,u){const d=e.indexOf("#"),h=d>-1?(l.host&&document.querySelector("base")?e:e.slice(d))+a:Dd()+e+a;try{t[u?"replaceState":"pushState"](c,"",h),i.value=c}catch(p){console.error(p),l[u?"replace":"assign"](h)}}function s(a,c){const u=me({},t.state,os(i.value.back,a,i.value.forward,!0),c,{position:i.value.position});r(a,u,!0),n.value=a}function o(a,c){const u=me({},i.value,t.state,{forward:a,scroll:Qn()});r(u.current,u,!0);const d=me({},os(n.value,a,null),{position:u.position+1},c);r(a,d,!1),n.value=a}return{location:n,state:i,push:o,replace:s}}function Nd(e){e=Pd(e);const t=Md(e),l=$d(e,t.state,t.location,t.replace);function n(r,s=!0){s||l.pauseListeners(),history.go(r)}const i=me({location:"",base:e,go:n,createHref:Ad.bind(null,e)},t,l);return Object.defineProperty(i,"location",{enumerable:!0,get:()=>t.location.value}),Object.defineProperty(i,"state",{enumerable:!0,get:()=>t.state.value}),i}function Hd(e){return typeof e=="string"||e&&typeof e=="object"}function Zo(e){return typeof e=="string"||typeof e=="symbol"}const bt={path:"/",name:void 0,params:{},query:{},hash:"",fullPath:"/",matched:[],meta:{},redirectedFrom:void 0},ea=Symbol("");var as;(function(e){e[e.aborted=4]="aborted",e[e.cancelled=8]="cancelled",e[e.duplicated=16]="duplicated"})(as||(as={}));function yl(e,t){return me(new Error,{type:e,[ea]:!0},t)}function kt(e,t){return e instanceof Error&&ea in e&&(t==null||!!(e.type&t))}const cs="[^/]+?",Vd={sensitive:!1,strict:!1,start:!0,end:!0},Bd=/[.+*?^${}()[\]/\\]/g;function jd(e,t){const l=me({},Vd,t),n=[];let i=l.start?"^":"";const r=[];for(const c of e){const u=c.length?[]:[90];l.strict&&!c.length&&(i+="/");for(let d=0;dt.length?t.length===1&&t[0]===80?1:-1:0}function Wd(e,t){let l=0;const n=e.score,i=t.score;for(;l0&&t[t.length-1]<0}const Gd={type:0,value:""},Kd=/[a-zA-Z0-9_]/;function qd(e){if(!e)return[[]];if(e==="/")return[[Gd]];if(!e.startsWith("/"))throw new Error(`Invalid path "${e}"`);function t(p){throw new Error(`ERR (${l})/"${c}": ${p}`)}let l=0,n=l;const i=[];let r;function s(){r&&i.push(r),r=[]}let o=0,a,c="",u="";function d(){c&&(l===0?r.push({type:0,value:c}):l===1||l===2||l===3?(r.length>1&&(a==="*"||a==="+")&&t(`A repeatable param (${c}) must be alone in its segment. eg: '/:ids+.`),r.push({type:1,value:c,regexp:u,repeatable:a==="*"||a==="+",optional:a==="*"||a==="?"})):t("Invalid state to consume buffer"),c="")}function h(){c+=a}for(;o{s(S)}:Dl}function s(u){if(Zo(u)){const d=n.get(u);d&&(n.delete(u),l.splice(l.indexOf(d),1),d.children.forEach(s),d.alias.forEach(s))}else{const d=l.indexOf(u);d>-1&&(l.splice(d,1),u.record.name&&n.delete(u.record.name),u.children.forEach(s),u.alias.forEach(s))}}function o(){return l}function a(u){let d=0;for(;d=0&&(u.record.path!==l[d].record.path||!ta(u,l[d]));)d++;l.splice(d,0,u),u.record.name&&!hs(u)&&n.set(u.record.name,u)}function c(u,d){let h,p={},m,b;if("name"in u&&u.name){if(h=n.get(u.name),!h)throw yl(1,{location:u});b=h.record.name,p=me(ds(d.params,h.keys.filter(S=>!S.optional).map(S=>S.name)),u.params&&ds(u.params,h.keys.map(S=>S.name))),m=h.stringify(p)}else if("path"in u)m=u.path,h=l.find(S=>S.re.test(m)),h&&(p=h.parse(m),b=h.record.name);else{if(h=d.name?n.get(d.name):l.find(S=>S.re.test(d.path)),!h)throw yl(1,{location:u,currentLocation:d});b=h.record.name,p=me({},d.params,u.params),m=h.stringify(p)}const x=[];let y=h;for(;y;)x.unshift(y.record),y=y.parent;return{name:b,path:m,params:p,matched:x,meta:Zd(x)}}return e.forEach(u=>r(u)),{addRoute:r,resolve:c,removeRoute:s,getRoutes:o,getRecordMatcher:i}}function ds(e,t){const l={};for(const n of t)n in e&&(l[n]=e[n]);return l}function Jd(e){return{path:e.path,redirect:e.redirect,name:e.name,meta:e.meta||{},aliasOf:void 0,beforeEnter:e.beforeEnter,props:Qd(e),children:e.children||[],instances:{},leaveGuards:new Set,updateGuards:new Set,enterCallbacks:{},components:"components"in e?e.components||null:e.component&&{default:e.component}}}function Qd(e){const t={},l=e.props||!1;if("component"in e)t.default=l;else for(const n in e.components)t[n]=typeof l=="object"?l[n]:l;return t}function hs(e){for(;e;){if(e.record.aliasOf)return!0;e=e.parent}return!1}function Zd(e){return e.reduce((t,l)=>me(t,l.meta),{})}function gs(e,t){const l={};for(const n in e)l[n]=n in t?t[n]:e[n];return l}function ta(e,t){return t.children.some(l=>l===e||ta(e,l))}const la=/#/g,eh=/&/g,th=/\//g,lh=/=/g,nh=/\?/g,na=/\+/g,ih=/%5B/g,rh=/%5D/g,ia=/%5E/g,sh=/%60/g,ra=/%7B/g,oh=/%7C/g,sa=/%7D/g,ah=/%20/g;function tr(e){return encodeURI(""+e).replace(oh,"|").replace(ih,"[").replace(rh,"]")}function ch(e){return tr(e).replace(ra,"{").replace(sa,"}").replace(ia,"^")}function Fi(e){return tr(e).replace(na,"%2B").replace(ah,"+").replace(la,"%23").replace(eh,"%26").replace(sh,"`").replace(ra,"{").replace(sa,"}").replace(ia,"^")}function uh(e){return Fi(e).replace(lh,"%3D")}function dh(e){return tr(e).replace(la,"%23").replace(nh,"%3F")}function hh(e){return e==null?"":dh(e).replace(th,"%2F")}function Nn(e){try{return decodeURIComponent(""+e)}catch{}return""+e}function gh(e){const t={};if(e===""||e==="?")return t;const n=(e[0]==="?"?e.slice(1):e).split("&");for(let i=0;ir&&Fi(r)):[n&&Fi(n)]).forEach(r=>{r!==void 0&&(t+=(t.length?"&":"")+l,r!=null&&(t+="="+r))})}return t}function ph(e){const t={};for(const l in e){const n=e[l];n!==void 0&&(t[l]=at(n)?n.map(i=>i==null?null:""+i):n==null?n:""+n)}return t}const fh=Symbol(""),fs=Symbol(""),Zn=Symbol(""),lr=Symbol(""),Di=Symbol("");function Tl(){let e=[];function t(n){return e.push(n),()=>{const i=e.indexOf(n);i>-1&&e.splice(i,1)}}function l(){e=[]}return{add:t,list:()=>e.slice(),reset:l}}function Ft(e,t,l,n,i){const r=n&&(n.enterCallbacks[i]=n.enterCallbacks[i]||[]);return()=>new Promise((s,o)=>{const a=d=>{d===!1?o(yl(4,{from:l,to:t})):d instanceof Error?o(d):Hd(d)?o(yl(2,{from:t,to:d})):(r&&n.enterCallbacks[i]===r&&typeof d=="function"&&r.push(d),s())},c=e.call(n&&n.instances[i],t,l,a);let u=Promise.resolve(c);e.length<3&&(u=u.then(a)),u.catch(d=>o(d))})}function vi(e,t,l,n){const i=[];for(const r of e)for(const s in r.components){let o=r.components[s];if(!(t!=="beforeRouteEnter"&&!r.instances[s]))if(mh(o)){const c=(o.__vccOpts||o)[t];c&&i.push(Ft(c,l,n,r,s))}else{let a=o();i.push(()=>a.then(c=>{if(!c)return Promise.reject(new Error(`Couldn't resolve component "${s}" at "${r.path}"`));const u=xd(c)?c.default:c;r.components[s]=u;const h=(u.__vccOpts||u)[t];return h&&Ft(h,l,n,r,s)()}))}}return i}function mh(e){return typeof e=="object"||"displayName"in e||"props"in e||"__vccOpts"in e}function ms(e){const t=Be(Zn),l=Be(lr),n=O(()=>t.resolve(Z(e.to))),i=O(()=>{const{matched:a}=n.value,{length:c}=a,u=a[c-1],d=l.matched;if(!u||!d.length)return-1;const h=d.findIndex(bl.bind(null,u));if(h>-1)return h;const p=vs(a[c-2]);return c>1&&vs(u)===p&&d[d.length-1].path!==p?d.findIndex(bl.bind(null,a[c-2])):h}),r=O(()=>i.value>-1&&bh(l.params,n.value.params)),s=O(()=>i.value>-1&&i.value===l.matched.length-1&&Jo(l.params,n.value.params));function o(a={}){return _h(a)?t[Z(e.replace)?"replace":"push"](Z(e.to)).catch(Dl):Promise.resolve()}return{route:n,href:O(()=>n.value.href),isActive:r,isExactActive:s,navigate:o}}const vh=fe({name:"RouterLink",compatConfig:{MODE:3},props:{to:{type:[String,Object],required:!0},replace:Boolean,activeClass:String,exactActiveClass:String,custom:Boolean,ariaCurrentValue:{type:String,default:"page"}},useLink:ms,setup(e,{slots:t}){const l=Jl(ms(e)),{options:n}=Be(Zn),i=O(()=>({[ks(e.activeClass,n.linkActiveClass,"router-link-active")]:l.isActive,[ks(e.exactActiveClass,n.linkExactActiveClass,"router-link-exact-active")]:l.isExactActive}));return()=>{const r=t.default&&t.default(l);return e.custom?r:q("a",{"aria-current":l.isExactActive?e.ariaCurrentValue:null,href:l.href,onClick:l.navigate,class:i.value},r)}}}),kh=vh;function _h(e){if(!(e.metaKey||e.altKey||e.ctrlKey||e.shiftKey)&&!e.defaultPrevented&&!(e.button!==void 0&&e.button!==0)){if(e.currentTarget&&e.currentTarget.getAttribute){const t=e.currentTarget.getAttribute("target");if(/\b_blank\b/i.test(t))return}return e.preventDefault&&e.preventDefault(),!0}}function bh(e,t){for(const l in t){const n=t[l],i=e[l];if(typeof n=="string"){if(n!==i)return!1}else if(!at(i)||i.length!==n.length||n.some((r,s)=>r!==i[s]))return!1}return!0}function vs(e){return e?e.aliasOf?e.aliasOf.path:e.path:""}const ks=(e,t,l)=>e??t??l,yh=fe({name:"RouterView",inheritAttrs:!1,props:{name:{type:String,default:"default"},route:Object},compatConfig:{MODE:3},setup(e,{attrs:t,slots:l}){const n=Be(Di),i=O(()=>e.route||n.value),r=Be(fs,0),s=O(()=>{let c=Z(r);const{matched:u}=i.value;let d;for(;(d=u[c])&&!d.components;)c++;return c}),o=O(()=>i.value.matched[s.value]);el(fs,O(()=>s.value+1)),el(fh,o),el(Di,i);const a=ae();return Fe(()=>[a.value,o.value,e.name],([c,u,d],[h,p,m])=>{u&&(u.instances[d]=c,p&&p!==u&&c&&c===h&&(u.leaveGuards.size||(u.leaveGuards=p.leaveGuards),u.updateGuards.size||(u.updateGuards=p.updateGuards))),c&&u&&(!p||!bl(u,p)||!h)&&(u.enterCallbacks[d]||[]).forEach(b=>b(c))},{flush:"post"}),()=>{const c=i.value,u=e.name,d=o.value,h=d&&d.components[u];if(!h)return _s(l.default,{Component:h,route:c});const p=d.props[u],m=p?p===!0?c.params:typeof p=="function"?p(c):p:null,x=q(h,me({},m,t,{onVnodeUnmounted:y=>{y.component.isUnmounted&&(d.instances[u]=null)},ref:a}));return _s(l.default,{Component:x,route:c})||x}}});function _s(e,t){if(!e)return null;const l=e(t);return l.length===1?l[0]:l}const xh=yh;function Eh(e){const t=Yd(e.routes,e),l=e.parseQuery||gh,n=e.stringifyQuery||ps,i=e.history,r=Tl(),s=Tl(),o=Tl(),a=sl(bt);let c=bt;ul&&e.scrollBehavior&&"scrollRestoration"in history&&(history.scrollRestoration="manual");const u=fi.bind(null,T=>""+T),d=fi.bind(null,hh),h=fi.bind(null,Nn);function p(T,W){let B,Y;return Zo(T)?(B=t.getRecordMatcher(T),Y=W):Y=T,t.addRoute(Y,B)}function m(T){const W=t.getRecordMatcher(T);W&&t.removeRoute(W)}function b(){return t.getRoutes().map(T=>T.record)}function x(T){return!!t.getRecordMatcher(T)}function y(T,W){if(W=me({},W||a.value),typeof T=="string"){const f=mi(l,T,W.path),_=t.resolve({path:f.path},W),P=i.createHref(f.fullPath);return me(f,_,{params:h(_.params),hash:Nn(f.hash),redirectedFrom:void 0,href:P})}let B;if("path"in T)B=me({},T,{path:mi(l,T.path,W.path).path});else{const f=me({},T.params);for(const _ in f)f[_]==null&&delete f[_];B=me({},T,{params:d(f)}),W.params=d(W.params)}const Y=t.resolve(B,W),ue=T.hash||"";Y.params=u(h(Y.params));const ve=Ld(n,me({},T,{hash:ch(ue),path:Y.path})),g=i.createHref(ve);return me({fullPath:ve,hash:ue,query:n===ps?ph(T.query):T.query||{}},Y,{redirectedFrom:void 0,href:g})}function S(T){return typeof T=="string"?mi(l,T,a.value.path):me({},T)}function v(T,W){if(c!==T)return yl(8,{from:W,to:T})}function w(T){return M(T)}function H(T){return w(me(S(T),{replace:!0}))}function I(T){const W=T.matched[T.matched.length-1];if(W&&W.redirect){const{redirect:B}=W;let Y=typeof B=="function"?B(T):B;return typeof Y=="string"&&(Y=Y.includes("?")||Y.includes("#")?Y=S(Y):{path:Y},Y.params={}),me({query:T.query,hash:T.hash,params:"path"in Y?{}:T.params},Y)}}function M(T,W){const B=c=y(T),Y=a.value,ue=T.state,ve=T.force,g=T.replace===!0,f=I(B);if(f)return M(me(S(f),{state:typeof f=="object"?me({},ue,f.state):ue,force:ve,replace:g}),W||B);const _=B;_.redirectedFrom=W;let P;return!ve&&Cd(n,Y,B)&&(P=yl(16,{to:_,from:Y}),We(Y,Y,!0,!1)),(P?Promise.resolve(P):z(_,Y)).catch(C=>kt(C)?kt(C,2)?C:De(C):G(C,_,Y)).then(C=>{if(C){if(kt(C,2))return M(me({replace:g},S(C.to),{state:typeof C.to=="object"?me({},ue,C.to.state):ue,force:ve}),W||_)}else C=L(_,Y,!0,g,ue);return X(_,Y,C),C})}function k(T,W){const B=v(T,W);return B?Promise.reject(B):Promise.resolve()}function N(T){const W=Ct.values().next().value;return W&&typeof W.runWithContext=="function"?W.runWithContext(T):T()}function z(T,W){let B;const[Y,ue,ve]=wh(T,W);B=vi(Y.reverse(),"beforeRouteLeave",T,W);for(const f of Y)f.leaveGuards.forEach(_=>{B.push(Ft(_,T,W))});const g=k.bind(null,T,W);return B.push(g),Oe(B).then(()=>{B=[];for(const f of r.list())B.push(Ft(f,T,W));return B.push(g),Oe(B)}).then(()=>{B=vi(ue,"beforeRouteUpdate",T,W);for(const f of ue)f.updateGuards.forEach(_=>{B.push(Ft(_,T,W))});return B.push(g),Oe(B)}).then(()=>{B=[];for(const f of ve)if(f.beforeEnter)if(at(f.beforeEnter))for(const _ of f.beforeEnter)B.push(Ft(_,T,W));else B.push(Ft(f.beforeEnter,T,W));return B.push(g),Oe(B)}).then(()=>(T.matched.forEach(f=>f.enterCallbacks={}),B=vi(ve,"beforeRouteEnter",T,W),B.push(g),Oe(B))).then(()=>{B=[];for(const f of s.list())B.push(Ft(f,T,W));return B.push(g),Oe(B)}).catch(f=>kt(f,8)?f:Promise.reject(f))}function X(T,W,B){o.list().forEach(Y=>N(()=>Y(T,W,B)))}function L(T,W,B,Y,ue){const ve=v(T,W);if(ve)return ve;const g=W===bt,f=ul?history.state:{};B&&(Y||g?i.replace(T.fullPath,me({scroll:g&&f&&f.scroll},ue)):i.push(T.fullPath,ue)),a.value=T,We(T,W,B,g),De()}let $;function le(){$||($=i.listen((T,W,B)=>{if(!ut.listening)return;const Y=y(T),ue=I(Y);if(ue){M(me(ue,{replace:!0}),Y).catch(Dl);return}c=Y;const ve=a.value;ul&&Id(ss(ve.fullPath,B.delta),Qn()),z(Y,ve).catch(g=>kt(g,12)?g:kt(g,2)?(M(g.to,Y).then(f=>{kt(f,20)&&!B.delta&&B.type===Wl.pop&&i.go(-1,!1)}).catch(Dl),Promise.reject()):(B.delta&&i.go(-B.delta,!1),G(g,Y,ve))).then(g=>{g=g||L(Y,ve,!1),g&&(B.delta&&!kt(g,8)?i.go(-B.delta,!1):B.type===Wl.pop&&kt(g,20)&&i.go(-1,!1)),X(Y,ve,g)}).catch(Dl)}))}let re=Tl(),R=Tl(),J;function G(T,W,B){De(T);const Y=R.list();return Y.length?Y.forEach(ue=>ue(T,W,B)):console.error(T),Promise.reject(T)}function Ae(){return J&&a.value!==bt?Promise.resolve():new Promise((T,W)=>{re.add([T,W])})}function De(T){return J||(J=!T,le(),re.list().forEach(([W,B])=>T?B(T):W()),re.reset()),T}function We(T,W,B,Y){const{scrollBehavior:ue}=e;if(!ul||!ue)return Promise.resolve();const ve=!B&&Fd(ss(T.fullPath,0))||(Y||!B)&&history.state&&history.state.scroll||null;return El().then(()=>ue(T,W,ve)).then(g=>g&&Rd(g)).catch(g=>G(g,T,W))}const He=T=>i.go(T);let Lt;const Ct=new Set,ut={currentRoute:a,listening:!0,addRoute:p,removeRoute:m,hasRoute:x,getRoutes:b,resolve:y,options:e,push:w,replace:H,go:He,back:()=>He(-1),forward:()=>He(1),beforeEach:r.add,beforeResolve:s.add,afterEach:o.add,onError:R.add,isReady:Ae,install(T){const W=this;T.component("RouterLink",kh),T.component("RouterView",xh),T.config.globalProperties.$router=W,Object.defineProperty(T.config.globalProperties,"$route",{enumerable:!0,get:()=>Z(a)}),ul&&!Lt&&a.value===bt&&(Lt=!0,w(i.location).catch(ue=>{}));const B={};for(const ue in bt)Object.defineProperty(B,ue,{get:()=>a.value[ue],enumerable:!0});T.provide(Zn,W),T.provide(lr,no(B)),T.provide(Di,a);const Y=T.unmount;Ct.add(T),T.unmount=function(){Ct.delete(T),Ct.size<1&&(c=bt,$&&$(),$=null,a.value=bt,Lt=!1,J=!1),Y()}}};function Oe(T){return T.reduce((W,B)=>W.then(()=>N(B)),Promise.resolve())}return ut}function wh(e,t){const l=[],n=[],i=[],r=Math.max(t.matched.length,e.matched.length);for(let s=0;sbl(c,o))?n.push(o):l.push(o));const a=e.matched[s];a&&(t.matched.find(c=>bl(c,a))||i.push(a))}return[l,n,i]}function Bt(){return Be(Zn)}function jt(){return Be(lr)}var nr=Symbol(""),vt=()=>{const e=Be(nr);if(!e)throw new Error("useClientData() is called without provider.");return e},Lh=()=>vt().pageComponent,tl=()=>vt().pageData,pt=()=>vt().pageFrontmatter,Ch=()=>vt().pageHead,Th=()=>vt().pageLang,Sh=()=>vt().pageLayout,ol=()=>vt().routeLocale,oa=()=>vt().routes,ir=()=>vt().siteData,rr=()=>vt().siteLocaleData,Ph=Symbol(""),aa=sl(bd),Gl=sl(yd),ca=e=>{const t=vd(e);if(Gl.value[t])return t;const l=encodeURI(t);return Gl.value[l]?l:aa.value[t]||t},sr=e=>{const t=ca(e),l=Gl.value[t]??{...Gl.value["/404.html"],notFound:!0};return{path:t,notFound:!1,...l}},or=fe({name:"ClientOnly",setup(e,t){const l=ae(!1);return je(()=>{l.value=!0}),()=>{var n,i;return l.value?(i=(n=t.slots).default)==null?void 0:i.call(n):null}}}),zh=fe({name:"Content",props:{path:{type:String,required:!1,default:""}},setup(e){const t=Lh(),l=O(()=>{if(!e.path)return t.value;const n=sr(e.path);return Eo(()=>n.loader().then(({comp:i})=>i))});return()=>q(l.value)}}),ei=e=>on(e)?e:`/ecosystem/${Xo(e)}`,Ah=e=>{if(!(e.metaKey||e.altKey||e.ctrlKey||e.shiftKey)&&!e.defaultPrevented&&!(e.button!==void 0&&e.button!==0)){if(e.currentTarget){const t=e.currentTarget.getAttribute("target");if(t!=null&&t.match(/\b_blank\b/i))return}return e.preventDefault(),!0}},yt=({active:e=!1,activeClass:t="route-link-active",to:l,...n},{slots:i})=>{var o;const r=Bt(),s=ei(ca(l));return q("a",{...n,class:["route-link",{[t]:e}],href:s,onClick:(a={})=>{Ah(a)?r.push(l).catch():Promise.resolve()}},(o=i.default)==null?void 0:o.call(i))};yt.displayName="RouteLink";yt.props={active:Boolean,activeClass:String,to:String};var Oh="Layout",Rh="en-US",Gt=Jl({resolveLayouts:e=>e.reduce((t,l)=>({...t,...l.layouts}),{}),resolvePageHead:(e,t,l)=>{const n=qe(t.description)?t.description:l.description,i=[...Array.isArray(t.head)?t.head:[],...l.head,["title",{},e],["meta",{name:"description",content:n}]];return gd(i)},resolvePageHeadTitle:(e,t)=>[e.title,t.title].filter(l=>!!l).join(" | "),resolvePageLang:(e,t)=>e.lang||t.lang||Rh,resolvePageLayout:(e,t)=>{const l=qe(e.frontmatter.layout)?e.frontmatter.layout:Oh;if(!t[l])throw new Error(`[vuepress] Cannot resolve layout: ${l}`);return t[l]},resolveRouteLocale:(e,t)=>Yo(e,t),resolveSiteLocaleData:(e,t)=>{var l;return{...e,...e.locales[t],head:[...((l=e.locales[t])==null?void 0:l.head)??[],...e.head??[]]}}}),ct=(e={})=>e;function an(e){return Ws()?(Ga(e),!0):!1}function Ze(e){return typeof e=="function"?e():Z(e)}const ti=typeof window<"u"&&typeof document<"u";typeof WorkerGlobalScope<"u"&&globalThis instanceof WorkerGlobalScope;const Ih=Object.prototype.toString,Fh=e=>Ih.call(e)==="[object Object]",Hn=()=>{},bs=Dh();function Dh(){var e,t;return ti&&((e=window==null?void 0:window.navigator)==null?void 0:e.userAgent)&&(/iP(ad|hone|od)/.test(window.navigator.userAgent)||((t=window==null?void 0:window.navigator)==null?void 0:t.maxTouchPoints)>2&&/iPad|Macintosh/.test(window==null?void 0:window.navigator.userAgent))}function ua(e,t){function l(...n){return new Promise((i,r)=>{Promise.resolve(e(()=>t.apply(this,n),{fn:t,thisArg:this,args:n})).then(i).catch(r)})}return l}const da=e=>e();function $h(e,t={}){let l,n,i=Hn;const r=o=>{clearTimeout(o),i(),i=Hn};return o=>{const a=Ze(e),c=Ze(t.maxWait);return l&&r(l),a<=0||c!==void 0&&c<=0?(n&&(r(n),n=null),Promise.resolve(o())):new Promise((u,d)=>{i=t.rejectOnCancel?d:u,c&&!n&&(n=setTimeout(()=>{l&&r(l),n=null,u(o())},c)),l=setTimeout(()=>{n&&r(n),n=null,u(o())},a)})}}function Mh(e=da){const t=ae(!0);function l(){t.value=!1}function n(){t.value=!0}const i=(...r)=>{t.value&&e(...r)};return{isActive:Ql(t),pause:l,resume:n,eventFilter:i}}function Nh(e){let t;function l(){return t||(t=e()),t}return l.reset=async()=>{const n=t;t=void 0,n&&await n},l}function Hh(e){return e||ln()}function Vh(...e){if(e.length!==1)return _c(...e);const t=e[0];return typeof t=="function"?Ql(co(()=>({get:t,set:Hn}))):ae(t)}function Bh(e,t=200,l={}){return ua($h(t,l),e)}function jh(e,t,l={}){const{eventFilter:n=da,...i}=l;return Fe(e,ua(n,t),i)}function Uh(e,t,l={}){const{eventFilter:n,...i}=l,{eventFilter:r,pause:s,resume:o,isActive:a}=Mh(n);return{stop:jh(e,t,{...i,eventFilter:r}),pause:s,resume:o,isActive:a}}function ar(e,t=!0,l){Hh()?je(e,l):t?e():El(e)}function Wh(e,t,l={}){const{immediate:n=!0}=l,i=ae(!1);let r=null;function s(){r&&(clearTimeout(r),r=null)}function o(){i.value=!1,s()}function a(...c){s(),i.value=!0,r=setTimeout(()=>{i.value=!1,r=null,e(...c)},Ze(t))}return n&&(i.value=!0,ti&&a()),an(o),{isPending:Ql(i),start:a,stop:o}}function Gh(e=!1,t={}){const{truthyValue:l=!0,falsyValue:n=!1}=t,i=Ne(e),r=ae(e);function s(o){if(arguments.length)return r.value=o,r.value;{const a=Ze(l);return r.value=r.value===a?Ze(n):a,r.value}}return i?s:[r,s]}function Jt(e){var t;const l=Ze(e);return(t=l==null?void 0:l.$el)!=null?t:l}const ft=ti?window:void 0,ha=ti?window.navigator:void 0;function mt(...e){let t,l,n,i;if(typeof e[0]=="string"||Array.isArray(e[0])?([l,n,i]=e,t=ft):[t,l,n,i]=e,!t)return Hn;Array.isArray(l)||(l=[l]),Array.isArray(n)||(n=[n]);const r=[],s=()=>{r.forEach(u=>u()),r.length=0},o=(u,d,h,p)=>(u.addEventListener(d,h,p),()=>u.removeEventListener(d,h,p)),a=Fe(()=>[Jt(t),Ze(i)],([u,d])=>{if(s(),!u)return;const h=Fh(d)?{...d}:d;r.push(...l.flatMap(p=>n.map(m=>o(u,p,m,h))))},{immediate:!0,flush:"post"}),c=()=>{a(),s()};return an(c),c}function Kh(){const e=ae(!1);return ln()&&je(()=>{e.value=!0}),e}function li(e){const t=Kh();return O(()=>(t.value,!!e()))}function ga(e,t={}){const{window:l=ft}=t,n=li(()=>l&&"matchMedia"in l&&typeof l.matchMedia=="function");let i;const r=ae(!1),s=c=>{r.value=c.matches},o=()=>{i&&("removeEventListener"in i?i.removeEventListener("change",s):i.removeListener(s))},a=Fc(()=>{n.value&&(o(),i=l.matchMedia(Ze(e)),"addEventListener"in i?i.addEventListener("change",s):i.addListener(s),r.value=i.matches)});return an(()=>{a(),o(),i=void 0}),r}function ys(e,t={}){const{controls:l=!1,navigator:n=ha}=t,i=li(()=>n&&"permissions"in n);let r;const s=typeof e=="string"?{name:e}:e,o=ae(),a=()=>{r&&(o.value=r.state)},c=Nh(async()=>{if(i.value){if(!r)try{r=await n.permissions.query(s),mt(r,"change",a),a()}catch{o.value="prompt"}return r}});return c(),l?{state:o,isSupported:i,query:c}:o}function qh(e={}){const{navigator:t=ha,read:l=!1,source:n,copiedDuring:i=1500,legacy:r=!1}=e,s=li(()=>t&&"clipboard"in t),o=ys("clipboard-read"),a=ys("clipboard-write"),c=O(()=>s.value||r),u=ae(""),d=ae(!1),h=Wh(()=>d.value=!1,i);function p(){s.value&&o.value!=="denied"?t.clipboard.readText().then(y=>{u.value=y}):u.value=x()}c.value&&l&&mt(["copy","cut"],p);async function m(y=Ze(n)){c.value&&y!=null&&(s.value&&a.value!=="denied"?await t.clipboard.writeText(y):b(y),u.value=y,d.value=!0,h.start())}function b(y){const S=document.createElement("textarea");S.value=y??"",S.style.position="absolute",S.style.opacity="0",document.body.appendChild(S),S.select(),document.execCommand("copy"),S.remove()}function x(){var y,S,v;return(v=(S=(y=document==null?void 0:document.getSelection)==null?void 0:y.call(document))==null?void 0:S.toString())!=null?v:""}return{isSupported:c,text:u,copied:d,copy:m}}const kn=typeof globalThis<"u"?globalThis:typeof window<"u"?window:typeof global<"u"?global:typeof self<"u"?self:{},_n="__vueuse_ssr_handlers__",Xh=Yh();function Yh(){return _n in kn||(kn[_n]=kn[_n]||{}),kn[_n]}function Jh(e,t){return Xh[e]||t}function Qh(e){return e==null?"any":e instanceof Set?"set":e instanceof Map?"map":e instanceof Date?"date":typeof e=="boolean"?"boolean":typeof e=="string"?"string":typeof e=="object"?"object":Number.isNaN(e)?"any":"number"}const Zh={boolean:{read:e=>e==="true",write:e=>String(e)},object:{read:e=>JSON.parse(e),write:e=>JSON.stringify(e)},number:{read:e=>Number.parseFloat(e),write:e=>String(e)},any:{read:e=>e,write:e=>String(e)},string:{read:e=>e,write:e=>String(e)},map:{read:e=>new Map(JSON.parse(e)),write:e=>JSON.stringify(Array.from(e.entries()))},set:{read:e=>new Set(JSON.parse(e)),write:e=>JSON.stringify(Array.from(e))},date:{read:e=>new Date(e),write:e=>e.toISOString()}},xs="vueuse-storage";function cr(e,t,l,n={}){var i;const{flush:r="pre",deep:s=!0,listenToStorageChanges:o=!0,writeDefaults:a=!0,mergeDefaults:c=!1,shallow:u,window:d=ft,eventFilter:h,onError:p=N=>{console.error(N)},initOnMounted:m}=n,b=(u?sl:ae)(typeof t=="function"?t():t);if(!l)try{l=Jh("getDefaultStorage",()=>{var N;return(N=ft)==null?void 0:N.localStorage})()}catch(N){p(N)}if(!l)return b;const x=Ze(t),y=Qh(x),S=(i=n.serializer)!=null?i:Zh[y],{pause:v,resume:w}=Uh(b,()=>H(b.value),{flush:r,deep:s,eventFilter:h});return d&&o&&ar(()=>{mt(d,"storage",k),mt(d,xs,M),m&&k()}),m||k(),b;function H(N){try{if(N==null)l.removeItem(e);else{const z=S.write(N),X=l.getItem(e);X!==z&&(l.setItem(e,z),d&&d.dispatchEvent(new CustomEvent(xs,{detail:{key:e,oldValue:X,newValue:z,storageArea:l}})))}}catch(z){p(z)}}function I(N){const z=N?N.newValue:l.getItem(e);if(z==null)return a&&x!=null&&l.setItem(e,S.write(x)),x;if(!N&&c){const X=S.read(z);return typeof c=="function"?c(X,x):y==="object"&&!Array.isArray(X)?{...x,...X}:X}else return typeof z!="string"?z:S.read(z)}function M(N){k(N.detail)}function k(N){if(!(N&&N.storageArea!==l)){if(N&&N.key==null){b.value=x;return}if(!(N&&N.key!==e)){v();try{(N==null?void 0:N.newValue)!==S.write(b.value)&&(b.value=I(N))}catch(z){p(z)}finally{N?El(w):w()}}}}}function eg(e){return ga("(prefers-color-scheme: dark)",e)}function tg(e,t,l={}){const{window:n=ft,...i}=l;let r;const s=li(()=>n&&"ResizeObserver"in n),o=()=>{r&&(r.disconnect(),r=void 0)},a=O(()=>Array.isArray(e)?e.map(d=>Jt(d)):[Jt(e)]),c=Fe(a,d=>{if(o(),s.value&&n){r=new ResizeObserver(t);for(const h of d)h&&r.observe(h,i)}},{immediate:!0,flush:"post",deep:!0}),u=()=>{o(),c()};return an(u),{isSupported:s,stop:u}}function lg(e,t={width:0,height:0},l={}){const{window:n=ft,box:i="content-box"}=l,r=O(()=>{var d,h;return(h=(d=Jt(e))==null?void 0:d.namespaceURI)==null?void 0:h.includes("svg")}),s=ae(t.width),o=ae(t.height),{stop:a}=tg(e,([d])=>{const h=i==="border-box"?d.borderBoxSize:i==="content-box"?d.contentBoxSize:d.devicePixelContentBoxSize;if(n&&r.value){const p=Jt(e);if(p){const m=n.getComputedStyle(p);s.value=Number.parseFloat(m.width),o.value=Number.parseFloat(m.height)}}else if(h){const p=Array.isArray(h)?h:[h];s.value=p.reduce((m,{inlineSize:b})=>m+b,0),o.value=p.reduce((m,{blockSize:b})=>m+b,0)}else s.value=d.contentRect.width,o.value=d.contentRect.height},l);ar(()=>{const d=Jt(e);d&&(s.value="offsetWidth"in d?d.offsetWidth:t.width,o.value="offsetHeight"in d?d.offsetHeight:t.height)});const c=Fe(()=>Jt(e),d=>{s.value=d?t.width:0,o.value=d?t.height:0});function u(){a(),c()}return{width:s,height:o,stop:u}}function ki(e){return typeof Window<"u"&&e instanceof Window?e.document.documentElement:typeof Document<"u"&&e instanceof Document?e.documentElement:e}function ng(e={}){const{window:t=ft}=e;if(!t)return ae(["en"]);const l=t.navigator,n=ae(l.languages);return mt(t,"languagechange",()=>{n.value=l.languages}),n}function pa(e){const t=window.getComputedStyle(e);if(t.overflowX==="scroll"||t.overflowY==="scroll"||t.overflowX==="auto"&&e.clientWidth1?!0:(t.preventDefault&&t.preventDefault(),!1)}const bn=new WeakMap;function rg(e,t=!1){const l=ae(t);let n=null,i;Fe(Vh(e),o=>{const a=ki(Ze(o));if(a){const c=a;bn.get(c)||bn.set(c,i),l.value&&(c.style.overflow="hidden")}},{immediate:!0});const r=()=>{const o=ki(Ze(e));!o||l.value||(bs&&(n=mt(o,"touchmove",a=>{ig(a)},{passive:!1})),o.style.overflow="hidden",l.value=!0)},s=()=>{var o;const a=ki(Ze(e));!a||!l.value||(bs&&(n==null||n()),a.style.overflow=(o=bn.get(a))!=null?o:"",bn.delete(a),l.value=!1)};return an(s),O({get(){return l.value},set(o){o?r():s()}})}function sg(e,t,l={}){const{window:n=ft}=l;return cr(e,t,n==null?void 0:n.sessionStorage,l)}function og(e={}){const{window:t=ft,behavior:l="auto"}=e;if(!t)return{x:ae(0),y:ae(0)};const n=ae(t.scrollX),i=ae(t.scrollY),r=O({get(){return n.value},set(o){scrollTo({left:o,behavior:l})}}),s=O({get(){return i.value},set(o){scrollTo({top:o,behavior:l})}});return mt(t,"scroll",()=>{n.value=t.scrollX,i.value=t.scrollY},{capture:!1,passive:!0}),{x:r,y:s}}function ag(e={}){const{window:t=ft,initialWidth:l=Number.POSITIVE_INFINITY,initialHeight:n=Number.POSITIVE_INFINITY,listenOrientation:i=!0,includeScrollbar:r=!0}=e,s=ae(l),o=ae(n),a=()=>{t&&(r?(s.value=t.innerWidth,o.value=t.innerHeight):(s.value=t.document.documentElement.clientWidth,o.value=t.document.documentElement.clientHeight))};if(a(),ar(a),mt("resize",a,{passive:!0}),i){const c=ga("(orientation: portrait)");Fe(c,()=>a())}return{width:s,height:o}}const Es=async(e,t)=>{const{path:l,query:n}=e.currentRoute.value,{scrollBehavior:i}=e.options;e.options.scrollBehavior=void 0,await e.replace({path:l,query:n,hash:t}),e.options.scrollBehavior=i},cg=({headerLinkSelector:e,headerAnchorSelector:t,delay:l,offset:n=5})=>{const i=Bt();mt("scroll",Bh(()=>{var m,b;const s=Math.max(window.scrollY,document.documentElement.scrollTop,document.body.scrollTop);if(Math.abs(s-0)d.some(y=>y.hash===x.hash));for(let x=0;x=(((m=y.parentElement)==null?void 0:m.offsetTop)??0)-n,w=!S||s<(((b=S.parentElement)==null?void 0:b.offsetTop)??0)-n;if(!(v&&w))continue;const I=decodeURIComponent(i.currentRoute.value.hash),M=decodeURIComponent(y.hash);if(I===M)return;if(u){for(let k=x+1;k{const t=ol();return O(()=>e[t.value]??{})},fg=()=>{const e=oa();return O(()=>Object.keys(e.value))},Ln=(e,t)=>{var n;const l=(n=(t==null?void 0:t._instance)||ln())==null?void 0:n.appContext.components;return l?e in l||it(e)in l||Xl(it(e))in l:!1},_i=e=>typeof e=="number",ws=(e,t)=>qe(e)&&e.startsWith(t),mg=(e,t)=>qe(e)&&e.endsWith(t),fa=Object.entries,vg=Object.keys;var kg={"/zh/":{backToTop:"返回顶部"},"/":{backToTop:"Back to top"}};const _g=fe({name:"BackToTop",setup(e){const t=pt(),l=ur(kg),n=sl(),{height:i}=lg(n),{height:r}=ag(),{y:s}=og(),o=O(()=>t.value.backToTop!==!1&&s.value>100),a=O(()=>s.value/(i.value-r.value)*100);return je(()=>{n.value=document.body}),()=>q(sn,{name:"back-to-top"},()=>o.value?q("button",{type:"button",class:"vp-back-to-top-button","aria-label":l.value.backToTop,onClick:()=>{window.scrollTo({top:0,behavior:"smooth"})}},[q("span",{class:"vp-scroll-progress",role:"progressbar","aria-labelledby":"loadinglabel","aria-valuenow":a.value},q("svg",q("circle",{cx:"50%",cy:"50%",style:{"stroke-dasharray":`calc(${Math.PI*a.value}% - ${4*Math.PI}px) calc(${Math.PI*100}% - ${4*Math.PI}px)`}}))),q("div",{class:"back-to-top-icon"})]):null)}}),bg=ct({rootComponents:[_g]}),yg=/\b(?:Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini)/i,xg=()=>typeof window<"u"&&window.navigator&&"userAgent"in window.navigator&&yg.test(navigator.userAgent),bi=new Map,Eg=({delay:e=500,duration:t=2e3,locales:l,selector:n,showInMobile:i})=>{const{copy:r}=qh({legacy:!0}),s=ur(l),o=tl(),a=d=>{if(!d.hasAttribute("copy-code-registered")){const h=document.createElement("button");h.type="button",h.classList.add("vp-copy-code-button"),h.innerHTML='
',h.setAttribute("aria-label",s.value.copy),h.setAttribute("data-copied",s.value.copied),d.parentElement&&d.parentElement.insertBefore(h,d),d.setAttribute("copy-code-registered","")}},c=()=>{El().then(()=>setTimeout(()=>{n.forEach(d=>{document.querySelectorAll(d).forEach(a)})},e))},u=(d,h,p)=>{let{innerText:m=""}=h;/language-(shellscript|shell|bash|sh|zsh)/.test(d.classList.toString())&&(m=m.replace(/^ *(\$|>) /gm,"")),r(m).then(()=>{p.classList.add("copied"),clearTimeout(bi.get(p));const b=setTimeout(()=>{p.classList.remove("copied"),p.blur(),bi.delete(p)},t);bi.set(p,b)})};je(()=>{const d=!xg()||i;d&&c(),mt("click",h=>{const p=h.target;if(p.matches('div[class*="language-"] > button.copy')){const m=p.parentElement,b=p.nextElementSibling;b&&u(m,b,p)}else if(p.matches('div[class*="language-"] div.vp-copy-icon')){const m=p.parentElement,b=m.parentElement,x=m.nextElementSibling;x&&u(b,x,m)}}),Fe(()=>o.value.path,()=>{d&&c()})})};var wg={"/zh/":{copy:"复制代码",copied:"已复制"},"/":{copy:"Copy code",copied:"Copied"}},Lg=['.theme-default-content div[class*="language-"] pre'];const Cg=500,Tg=2e3,Sg=wg,Pg=Lg,zg=!1,Ag=ct({setup:()=>{Eg({selector:Pg,locales:Sg,duration:Tg,delay:Cg,showInMobile:zg})}}),Og=q("svg",{class:"external-link-icon",xmlns:"http://www.w3.org/2000/svg","aria-hidden":"true",focusable:"false",x:"0px",y:"0px",viewBox:"0 0 100 100",width:"15",height:"15"},[q("path",{fill:"currentColor",d:"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"}),q("polygon",{fill:"currentColor",points:"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"})]),Rg=fe({name:"ExternalLinkIcon",props:{locales:{type:Object,required:!1,default:()=>({})}},setup(e){const t=ol(),l=O(()=>e.locales[t.value]??{openInNewWindow:"open in new window"});return()=>q("span",[Og,q("span",{class:"external-link-icon-sr-only"},l.value.openInNewWindow)])}});var Ig={"/":{openInNewWindow:"open in new window"},"/zh/":{openInNewWindow:"在新窗口打开"}};const Fg=Ig,Dg=ct({enhance({app:e}){e.component("ExternalLinkIcon",q(Rg,{locales:Fg}))}});/*! medium-zoom 1.1.0 | MIT License | https://github.com/francoischalifour/medium-zoom */var Kt=Object.assign||function(e){for(var t=1;t1&&arguments[1]!==void 0?arguments[1]:{},n=window.Promise||function(L){function $(){}L($,$)},i=function(L){var $=L.target;if($===N){m();return}v.indexOf($)!==-1&&b({target:$})},r=function(){if(!(H||!k.original)){var L=window.pageYOffset||document.documentElement.scrollTop||document.body.scrollTop||0;Math.abs(I-L)>M.scrollOffset&&setTimeout(m,150)}},s=function(L){var $=L.key||L.keyCode;($==="Escape"||$==="Esc"||$===27)&&m()},o=function(){var L=arguments.length>0&&arguments[0]!==void 0?arguments[0]:{},$=L;if(L.background&&(N.style.background=L.background),L.container&&L.container instanceof Object&&($.container=Kt({},M.container,L.container)),L.template){var le=Cn(L.template)?L.template:document.querySelector(L.template);$.template=le}return M=Kt({},M,$),v.forEach(function(re){re.dispatchEvent(cl("medium-zoom:update",{detail:{zoom:z}}))}),z},a=function(){var L=arguments.length>0&&arguments[0]!==void 0?arguments[0]:{};return e(Kt({},M,L))},c=function(){for(var L=arguments.length,$=Array(L),le=0;le0?$.reduce(function(R,J){return[].concat(R,Cs(J))},[]):v;return re.forEach(function(R){R.classList.remove("medium-zoom-image"),R.dispatchEvent(cl("medium-zoom:detach",{detail:{zoom:z}}))}),v=v.filter(function(R){return re.indexOf(R)===-1}),z},d=function(L,$){var le=arguments.length>2&&arguments[2]!==void 0?arguments[2]:{};return v.forEach(function(re){re.addEventListener("medium-zoom:"+L,$,le)}),w.push({type:"medium-zoom:"+L,listener:$,options:le}),z},h=function(L,$){var le=arguments.length>2&&arguments[2]!==void 0?arguments[2]:{};return v.forEach(function(re){re.removeEventListener("medium-zoom:"+L,$,le)}),w=w.filter(function(re){return!(re.type==="medium-zoom:"+L&&re.listener.toString()===$.toString())}),z},p=function(){var L=arguments.length>0&&arguments[0]!==void 0?arguments[0]:{},$=L.target,le=function(){var R={width:document.documentElement.clientWidth,height:document.documentElement.clientHeight,left:0,top:0,right:0,bottom:0},J=void 0,G=void 0;if(M.container)if(M.container instanceof Object)R=Kt({},R,M.container),J=R.width-R.left-R.right-M.margin*2,G=R.height-R.top-R.bottom-M.margin*2;else{var Ae=Cn(M.container)?M.container:document.querySelector(M.container),De=Ae.getBoundingClientRect(),We=De.width,He=De.height,Lt=De.left,Ct=De.top;R=Kt({},R,{width:We,height:He,left:Lt,top:Ct})}J=J||R.width-M.margin*2,G=G||R.height-M.margin*2;var ut=k.zoomedHd||k.original,Oe=Ls(ut)?J:ut.naturalWidth||J,T=Ls(ut)?G:ut.naturalHeight||G,W=ut.getBoundingClientRect(),B=W.top,Y=W.left,ue=W.width,ve=W.height,g=Math.min(Math.max(ue,Oe),J)/ue,f=Math.min(Math.max(ve,T),G)/ve,_=Math.min(g,f),P=(-Y+(J-ue)/2+M.margin+R.left)/_,C=(-B+(G-ve)/2+M.margin+R.top)/_,F="scale("+_+") translate3d("+P+"px, "+C+"px, 0)";k.zoomed.style.transform=F,k.zoomedHd&&(k.zoomedHd.style.transform=F)};return new n(function(re){if($&&v.indexOf($)===-1){re(z);return}var R=function We(){H=!1,k.zoomed.removeEventListener("transitionend",We),k.original.dispatchEvent(cl("medium-zoom:opened",{detail:{zoom:z}})),re(z)};if(k.zoomed){re(z);return}if($)k.original=$;else if(v.length>0){var J=v;k.original=J[0]}else{re(z);return}if(k.original.dispatchEvent(cl("medium-zoom:open",{detail:{zoom:z}})),I=window.pageYOffset||document.documentElement.scrollTop||document.body.scrollTop||0,H=!0,k.zoomed=Ng(k.original),document.body.appendChild(N),M.template){var G=Cn(M.template)?M.template:document.querySelector(M.template);k.template=document.createElement("div"),k.template.appendChild(G.content.cloneNode(!0)),document.body.appendChild(k.template)}if(k.original.parentElement&&k.original.parentElement.tagName==="PICTURE"&&k.original.currentSrc&&(k.zoomed.src=k.original.currentSrc),document.body.appendChild(k.zoomed),window.requestAnimationFrame(function(){document.body.classList.add("medium-zoom--opened")}),k.original.classList.add("medium-zoom-image--hidden"),k.zoomed.classList.add("medium-zoom-image--opened"),k.zoomed.addEventListener("click",m),k.zoomed.addEventListener("transitionend",R),k.original.getAttribute("data-zoom-src")){k.zoomedHd=k.zoomed.cloneNode(),k.zoomedHd.removeAttribute("srcset"),k.zoomedHd.removeAttribute("sizes"),k.zoomedHd.removeAttribute("loading"),k.zoomedHd.src=k.zoomed.getAttribute("data-zoom-src"),k.zoomedHd.onerror=function(){clearInterval(Ae),console.warn("Unable to reach the zoom image target "+k.zoomedHd.src),k.zoomedHd=null,le()};var Ae=setInterval(function(){k.zoomedHd.complete&&(clearInterval(Ae),k.zoomedHd.classList.add("medium-zoom-image--opened"),k.zoomedHd.addEventListener("click",m),document.body.appendChild(k.zoomedHd),le())},10)}else if(k.original.hasAttribute("srcset")){k.zoomedHd=k.zoomed.cloneNode(),k.zoomedHd.removeAttribute("sizes"),k.zoomedHd.removeAttribute("loading");var De=k.zoomedHd.addEventListener("load",function(){k.zoomedHd.removeEventListener("load",De),k.zoomedHd.classList.add("medium-zoom-image--opened"),k.zoomedHd.addEventListener("click",m),document.body.appendChild(k.zoomedHd),le()})}else le()})},m=function(){return new n(function(L){if(H||!k.original){L(z);return}var $=function le(){k.original.classList.remove("medium-zoom-image--hidden"),document.body.removeChild(k.zoomed),k.zoomedHd&&document.body.removeChild(k.zoomedHd),document.body.removeChild(N),k.zoomed.classList.remove("medium-zoom-image--opened"),k.template&&document.body.removeChild(k.template),H=!1,k.zoomed.removeEventListener("transitionend",le),k.original.dispatchEvent(cl("medium-zoom:closed",{detail:{zoom:z}})),k.original=null,k.zoomed=null,k.zoomedHd=null,k.template=null,L(z)};H=!0,document.body.classList.remove("medium-zoom--opened"),k.zoomed.style.transform="",k.zoomedHd&&(k.zoomedHd.style.transform=""),k.template&&(k.template.style.transition="opacity 150ms",k.template.style.opacity=0),k.original.dispatchEvent(cl("medium-zoom:close",{detail:{zoom:z}})),k.zoomed.addEventListener("transitionend",$)})},b=function(){var L=arguments.length>0&&arguments[0]!==void 0?arguments[0]:{},$=L.target;return k.original?m():p({target:$})},x=function(){return M},y=function(){return v},S=function(){return k.original},v=[],w=[],H=!1,I=0,M=l,k={original:null,zoomed:null,zoomedHd:null,template:null};Object.prototype.toString.call(t)==="[object Object]"?M=t:(t||typeof t=="string")&&c(t),M=Kt({margin:0,background:"#fff",scrollOffset:40,container:null,template:null},M);var N=Mg(M.background);document.addEventListener("click",i),document.addEventListener("keyup",s),document.addEventListener("scroll",r),window.addEventListener("resize",m);var z={open:p,close:m,toggle:b,update:o,clone:a,attach:c,detach:u,on:d,off:h,getOptions:x,getImages:y,getZoomedImage:S};return z};function Vg(e,t){t===void 0&&(t={});var l=t.insertAt;if(!(!e||typeof document>"u")){var n=document.head||document.getElementsByTagName("head")[0],i=document.createElement("style");i.type="text/css",l==="top"&&n.firstChild?n.insertBefore(i,n.firstChild):n.appendChild(i),i.styleSheet?i.styleSheet.cssText=e:i.appendChild(document.createTextNode(e))}}var Bg=".medium-zoom-overlay{position:fixed;top:0;right:0;bottom:0;left:0;opacity:0;transition:opacity .3s;will-change:opacity}.medium-zoom--opened .medium-zoom-overlay{cursor:pointer;cursor:zoom-out;opacity:1}.medium-zoom-image{cursor:pointer;cursor:zoom-in;transition:transform .3s cubic-bezier(.2,0,.2,1)!important}.medium-zoom-image--hidden{visibility:hidden}.medium-zoom-image--opened{position:relative;cursor:pointer;cursor:zoom-out;will-change:transform}";Vg(Bg);const jg=Symbol("mediumZoom");var Ug={};const Wg=".theme-default-content > img, .theme-default-content :not(a) > img",Gg=Ug,Kg=300,qg=ct({enhance({app:e,router:t}){const l=Hg(Gg);l.refresh=(n=Wg)=>{l.detach(),l.attach(n)},e.provide(jg,l),t.afterEach(()=>{setTimeout(()=>l.refresh(),Kg)})}});/** + * NProgress, (c) 2013, 2014 Rico Sta. Cruz - http://ricostacruz.com/nprogress + * @license MIT + */const he={settings:{minimum:.08,easing:"ease",speed:200,trickle:!0,trickleRate:.02,trickleSpeed:800,barSelector:'[role="bar"]',parent:"body",template:'
'},status:null,set:e=>{const t=he.isStarted();e=yi(e,he.settings.minimum,1),he.status=e===1?null:e;const l=he.render(!t),n=l.querySelector(he.settings.barSelector),i=he.settings.speed,r=he.settings.easing;return l.offsetWidth,Xg(s=>{xn(n,{transform:"translate3d("+Ts(e)+"%,0,0)",transition:"all "+i+"ms "+r}),e===1?(xn(l,{transition:"none",opacity:"1"}),l.offsetWidth,setTimeout(function(){xn(l,{transition:"all "+i+"ms linear",opacity:"0"}),setTimeout(function(){he.remove(),s()},i)},i)):setTimeout(()=>s(),i)}),he},isStarted:()=>typeof he.status=="number",start:()=>{he.status||he.set(0);const e=()=>{setTimeout(()=>{he.status&&(he.trickle(),e())},he.settings.trickleSpeed)};return he.settings.trickle&&e(),he},done:e=>!e&&!he.status?he:he.inc(.3+.5*Math.random()).set(1),inc:e=>{let t=he.status;return t?(typeof e!="number"&&(e=(1-t)*yi(Math.random()*t,.1,.95)),t=yi(t+e,0,.994),he.set(t)):he.start()},trickle:()=>he.inc(Math.random()*he.settings.trickleRate),render:e=>{if(he.isRendered())return document.getElementById("nprogress");Ss(document.documentElement,"nprogress-busy");const t=document.createElement("div");t.id="nprogress",t.innerHTML=he.settings.template;const l=t.querySelector(he.settings.barSelector),n=e?"-100":Ts(he.status||0),i=document.querySelector(he.settings.parent);return xn(l,{transition:"all 0 linear",transform:"translate3d("+n+"%,0,0)"}),i!==document.body&&Ss(i,"nprogress-custom-parent"),i==null||i.appendChild(t),t},remove:()=>{Ps(document.documentElement,"nprogress-busy"),Ps(document.querySelector(he.settings.parent),"nprogress-custom-parent");const e=document.getElementById("nprogress");e&&Yg(e)},isRendered:()=>!!document.getElementById("nprogress")},yi=(e,t,l)=>el?l:e,Ts=e=>(-1+e)*100,Xg=function(){const e=[];function t(){const l=e.shift();l&&l(t)}return function(l){e.push(l),e.length===1&&t()}}(),xn=function(){const e=["Webkit","O","Moz","ms"],t={};function l(s){return s.replace(/^-ms-/,"ms-").replace(/-([\da-z])/gi,function(o,a){return a.toUpperCase()})}function n(s){const o=document.body.style;if(s in o)return s;let a=e.length;const c=s.charAt(0).toUpperCase()+s.slice(1);let u;for(;a--;)if(u=e[a]+c,u in o)return u;return s}function i(s){return s=l(s),t[s]??(t[s]=n(s))}function r(s,o,a){o=i(o),s.style[o]=a}return function(s,o){for(const a in o){const c=o[a];c!==void 0&&Object.prototype.hasOwnProperty.call(o,a)&&r(s,a,c)}}}(),ma=(e,t)=>(typeof e=="string"?e:dr(e)).indexOf(" "+t+" ")>=0,Ss=(e,t)=>{const l=dr(e),n=l+t;ma(l,t)||(e.className=n.substring(1))},Ps=(e,t)=>{const l=dr(e);if(!ma(e,t))return;const n=l.replace(" "+t+" "," ");e.className=n.substring(1,n.length-1)},dr=e=>(" "+(e.className||"")+" ").replace(/\s+/gi," "),Yg=e=>{e&&e.parentNode&&e.parentNode.removeChild(e)},Jg=()=>{je(()=>{const e=Bt(),t=new Set;t.add(e.currentRoute.value.path),e.beforeEach(l=>{t.has(l.path)||he.start()}),e.afterEach(l=>{t.add(l.path),he.done()})})},Qg=ct({setup(){Jg()}}),Zg=JSON.parse(`{"logo":"/images/hero.png","repo":"vuepress/ecosystem","docsDir":"docs","locales":{"/":{"navbar":[{"text":"Themes","children":[{"text":"Default Theme","link":"/themes/default/"},{"text":"Hope Theme","link":"https://theme-hope.vuejs.press"}]},{"text":"Plugins","children":[{"text":"Common Features","children":["/plugins/back-to-top","/plugins/catalog","/plugins/container","/plugins/copy-code","/plugins/copyright","/plugins/external-link-icon","/plugins/medium-zoom","/plugins/nprogress","/plugins/photo-swipe","/plugins/redirect","/plugins/register-components"]},{"text":"Search","children":["/plugins/docsearch","/plugins/search"]},{"text":"Blogging","children":["/plugins/blog/","/plugins/feed/"]},{"text":"PWA","children":["/plugins/pwa/","/plugins/remove-pwa"]},{"text":"SEO","children":["/plugins/baidu-analytics","/plugins/google-analytics","/plugins/seo/","/plugins/sitemap/"]},{"text":"Syntax Highlighting","children":["/plugins/prismjs","/plugins/shiki"]},{"text":"Theme Development","children":["/plugins/active-header-links","/plugins/git","/plugins/palette","/plugins/reading-time","/plugins/rtl","/plugins/theme-data","/plugins/toc"]}]},{"text":"Tools","children":[{"text":"helper","link":"/tools/helper/"}]}],"sidebar":{"/plugins/":[{"text":"Common Features","children":["/plugins/back-to-top","/plugins/catalog","/plugins/container","/plugins/copy-code","/plugins/copyright","/plugins/external-link-icon","/plugins/medium-zoom","/plugins/nprogress","/plugins/photo-swipe","/plugins/redirect","/plugins/register-components"]},{"text":"Content Search","children":["/plugins/docsearch","/plugins/search"]},{"text":"Blogging","children":[{"text":"Blog","link":"/plugins/blog/","children":["/plugins/blog/guide","/plugins/blog/config"]},{"text":"Feed","link":"/plugins/feed/","children":["/plugins/feed/guide","/plugins/feed/config","/plugins/feed/frontmatter","/plugins/feed/channel","/plugins/feed/getter"]}]},{"text":"PWA","children":[{"text":"PWA","link":"/plugins/pwa/","children":["/plugins/pwa/guide","/plugins/pwa/config"]},"/plugins/remove-pwa"]},{"text":"SEO","children":[{"text":"SEO","link":"/plugins/seo/","children":["/plugins/seo/guide","/plugins/seo/config"]},{"text":"Sitemap","link":"/plugins/sitemap/","children":["/plugins/sitemap/guide","/plugins/sitemap/config","/plugins/sitemap/frontmatter"]},"/plugins/baidu-analytics","/plugins/google-analytics"]},{"text":"Syntax Highlighting","children":["/plugins/prismjs","/plugins/shiki"]},{"text":"Theme Development","children":["/plugins/active-header-links","/plugins/git","/plugins/palette","/plugins/reading-time","/plugins/rtl","/plugins/theme-data","/plugins/toc"]}],"/themes/":[{"text":"Default Theme","link":"/themes/default/","children":["/themes/default/config","/themes/default/plugin","/themes/default/locale","/themes/default/frontmatter","/themes/default/components","/themes/default/markdown","/themes/default/styles","/themes/default/extending"]}],"/tools/":[{"text":"@vuepress/helper","link":"/tools/helper/","children":["/tools/helper/node/bundler","/tools/helper/node/page","/tools/helper/client","/tools/helper/shared"]}]},"editLinkText":"Edit this page on GitHub","selectLanguageName":"English"},"/zh/":{"navbar":[{"text":"主题","children":[{"text":"默认主题","link":"/zh/themes/default/"},{"text":"Hope 主题","link":"https://theme-hope.vuejs.press/zh/"}]},{"text":"插件","children":[{"text":"常用功能","children":["/zh/plugins/back-to-top","/zh/plugins/catalog","/zh/plugins/container","/zh/plugins/copy-code","/zh/plugins/copyright","/zh/plugins/external-link-icon","/zh/plugins/medium-zoom","/zh/plugins/nprogress","/zh/plugins/photo-swipe","/zh/plugins/redirect","/zh/plugins/register-components"]},{"text":"搜索","children":["/zh/plugins/docsearch","/zh/plugins/search"]},{"text":"博客","children":["/zh/plugins/blog/","/zh/plugins/feed/"]},{"text":"PWA","children":["/zh/plugins/pwa/","/zh/plugins/remove-pwa"]},{"text":"搜索引擎增强","children":["/zh/plugins/baidu-analytics","/zh/plugins/google-analytics","/zh/plugins/seo/","/zh/plugins/sitemap/"]},{"text":"语法高亮","children":["/zh/plugins/prismjs","/zh/plugins/shiki"]},{"text":"主题开发","children":["/zh/plugins/active-header-links","/zh/plugins/git","/zh/plugins/palette","/zh/plugins/reading-time","/zh/plugins/rtl","/zh/plugins/theme-data","/zh/plugins/toc"]}]},{"text":"工具","children":[{"text":"helper","link":"/zh/tools/helper/"}]}],"selectLanguageName":"简体中文","selectLanguageText":"选择语言","selectLanguageAriaLabel":"选择语言","sidebar":{"/zh/plugins/":[{"text":"常用功能","children":["/zh/plugins/back-to-top","/zh/plugins/catalog","/zh/plugins/container","/zh/plugins/copy-code","/zh/plugins/copyright","/zh/plugins/external-link-icon","/zh/plugins/medium-zoom","/zh/plugins/nprogress","/zh/plugins/photo-swipe","/zh/plugins/redirect","/zh/plugins/register-components"]},{"text":"搜索","children":["/zh/plugins/docsearch","/zh/plugins/search"]},{"text":"博客","children":[{"text":"博客","link":"/zh/plugins/blog/","children":["/zh/plugins/blog/guide","/zh/plugins/blog/config"]},{"text":"Feed","link":"/zh/plugins/feed/","children":["/zh/plugins/feed/guide","/zh/plugins/feed/config","/zh/themes/default/plugin","/zh/plugins/feed/frontmatter","/zh/plugins/feed/channel","/zh/plugins/feed/getter"]}]},{"text":"PWA","children":[{"text":"PWA","link":"/zh/plugins/pwa/","children":["/zh/plugins/pwa/guide","/zh/plugins/pwa/config"]},"/zh/plugins/remove-pwa"]},{"text":"搜索引擎增强","children":[{"text":"搜索引擎增强","link":"/zh/plugins/seo/","children":["/zh/plugins/seo/guide","/zh/plugins/seo/config"]},{"text":"站点地图","link":"/zh/plugins/sitemap/","children":["/zh/plugins/sitemap/guide","/zh/plugins/sitemap/config","/zh/plugins/sitemap/frontmatter"]},"/zh/plugins/baidu-analytics","/zh/plugins/google-analytics"]},{"text":"语法高亮","children":["/zh/plugins/prismjs","/zh/plugins/shiki"]},{"text":"主题开发","children":["/zh/plugins/active-header-links","/zh/plugins/git","/zh/plugins/palette","/zh/plugins/reading-time","/zh/plugins/rtl","/zh/plugins/theme-data","/zh/plugins/toc"]}],"/zh/themes/":[{"text":"默认主题","link":"/zh/themes/default/","children":["/zh/themes/default/config","/zh/themes/default/plugin","/zh/themes/default/locale","/zh/themes/default/frontmatter","/zh/themes/default/components","/zh/themes/default/markdown","/zh/themes/default/styles","/zh/themes/default/extending"]}],"/zh/tools/":[{"text":"@vuepress/helper","link":"/zh/tools/helper/","children":["/zh/tools/helper/node/bundler","/zh/tools/helper/node/page","/zh/tools/helper/client","/zh/tools/helper/shared"]}]},"editLinkText":"在 GitHub 上编辑此页","lastUpdatedText":"上次更新","contributorsText":"贡献者","tip":"提示","warning":"注意","danger":"警告","notFound":["这里什么都没有","我们怎么到这来了?","这是一个 404 页面","看起来我们进入了错误的链接"],"backToHome":"返回首页","openInNewWindow":"在新窗口打开","toggleColorMode":"切换颜色模式","toggleSidebar":"切换侧边栏"}},"colorMode":"auto","colorModeSwitch":true,"navbar":[],"selectLanguageText":"Languages","selectLanguageAriaLabel":"Select language","sidebar":"auto","sidebarDepth":2,"editLink":true,"editLinkText":"Edit this page","lastUpdated":true,"lastUpdatedText":"Last Updated","contributors":true,"contributorsText":"Contributors","notFound":["There's nothing here.","How did we get here?","That's a Four-Oh-Four.","Looks like we've got some broken links."],"backToHome":"Take me home","openInNewWindow":"open in new window","toggleColorMode":"toggle color mode","toggleSidebar":"toggle sidebar"}`),ep=ae(Zg),va=()=>ep,ka=Symbol(""),tp=()=>{const e=Be(ka);if(!e)throw new Error("useThemeLocaleData() is called without provider.");return e},lp=(e,t)=>{const{locales:l,...n}=e;return{...n,...l==null?void 0:l[t]}},np=ct({enhance({app:e}){const t=va(),l=e._context.provides[nr],n=O(()=>lp(t.value,l.routeLocale.value));e.provide(ka,n),Object.defineProperties(e.config.globalProperties,{$theme:{get(){return t.value}},$themeLocale:{get(){return n.value}}})}}),ip=fe({__name:"Badge",props:{type:{type:String,required:!1,default:"tip"},text:{type:String,required:!1,default:""},vertical:{type:String,required:!1,default:void 0}},setup(e){return(t,l)=>(U(),ee("span",{class:Ke(["badge",e.type]),style:Yl({verticalAlign:e.vertical})},[be(t.$slots,"default",{},()=>[Vt(Pe(e.text),1)])],6))}}),we=(e,t)=>{const l=e.__vccOpts||e;for(const[n,i]of t)l[n]=i;return l},rp=we(ip,[["__file","Badge.vue"]]),sp=fe({name:"CodeGroup",slots:Object,setup(e,{slots:t}){const l=ae([]),n=ae(-1),i=cr("vuepress-code-group",{}),r=O(()=>l.value.map(c=>c.innerText).join(","));je(()=>{Fe(()=>i.value[r.value],(c=-1)=>{n.value!==c&&(n.value=c)},{immediate:!0}),Fe(n,c=>{i.value[r.value]!==c&&(i.value[r.value]=c)})});const s=(c=n.value)=>{c{c>0?n.value=c-1:n.value=l.value.length-1,l.value[n.value].focus()},a=(c,u)=>{c.key===" "||c.key==="Enter"?(c.preventDefault(),n.value=u):c.key==="ArrowRight"?(c.preventDefault(),s(u)):c.key==="ArrowLeft"&&(c.preventDefault(),o(u))};return()=>{var u;const c=(((u=t.default)==null?void 0:u.call(t))||[]).filter(d=>d.type.name==="CodeGroupItem").map(d=>(d.props===null&&(d.props={}),d));return c.length===0?null:(n.value<0||n.value>c.length-1?(n.value=c.findIndex(d=>d.props.active===""||d.props.active===!0),n.value===-1&&(n.value=0)):c.forEach((d,h)=>{d.props.active=h===n.value}),q("div",{class:"code-group"},[q("div",{class:"code-group__nav",role:"tablist"},c.map((d,h)=>{const p=h===n.value;return q("button",{ref:m=>{m&&(l.value[h]=m)},class:{"code-group__nav-tab":!0,"code-group__nav-tab-active":p},role:"tab",ariaSelected:p,onClick:()=>n.value=h,onKeydown:m=>a(m,h)},d.props.title)})),c]))}}}),op=fe({name:"CodeGroupItem",__name:"CodeGroupItem",props:{title:{type:String,required:!0},active:{type:Boolean,required:!1,default:!1}},setup(e){return(t,l)=>(U(),ee("div",{class:Ke(["code-group-item",{"code-group-item__active":e.active}]),role:"tabpanel"},[be(t.$slots,"default")],2))}}),ap=we(op,[["__file","CodeGroupItem.vue"]]),cp=()=>va(),Me=()=>tp(),_a=Symbol(""),hr=()=>{const e=Be(_a);if(!e)throw new Error("useDarkMode() is called without provider.");return e},up=()=>{const e=Me(),t=eg(),l=cr("vuepress-color-scheme",e.value.colorMode),n=O({get(){return e.value.colorModeSwitch?l.value==="auto"?t.value:l.value==="dark":e.value.colorMode==="dark"},set(i){i===t.value?l.value="auto":l.value=i?"dark":"light"}});el(_a,n),dp(n)},dp=e=>{const t=(l=e.value)=>{const n=window==null?void 0:window.document.querySelector("html");n==null||n.classList.toggle("dark",l)};je(()=>{Fe(e,t,{immediate:!0})}),tn(()=>t())};let xi=null,Sl=null;const hp={wait:()=>xi,pending:()=>{xi=new Promise(e=>Sl=e)},resolve:()=>{Sl==null||Sl(),xi=null,Sl=null}},ba=()=>hp,gr=e=>{const{notFound:t,meta:l,path:n}=sr(e);return t?{text:n,link:n}:{text:l.title||n,link:n}},zs=e=>decodeURI(e).replace(/#.*$/,"").replace(/(index)?\.(md|html)$/,""),gp=(e,t)=>{if(t.hash===e)return!0;const l=zs(t.path),n=zs(e);return l===n},ya=(e,t)=>e.link&&gp(e.link,t)?!0:e.children?e.children.some(l=>ya(l,t)):!1,xa=e=>!on(e)||/github\.com/.test(e)?"GitHub":/bitbucket\.org/.test(e)?"Bitbucket":/gitlab\.com/.test(e)?"GitLab":/gitee\.com/.test(e)?"Gitee":null,pp={GitHub:":repo/edit/:branch/:path",GitLab:":repo/-/edit/:branch/:path",Gitee:":repo/edit/:branch/:path",Bitbucket:":repo/src/:branch/:path?mode=edit&spa=0&at=:branch&fileviewer=file-view-default"},fp=({docsRepo:e,editLinkPattern:t})=>{if(t)return t;const l=xa(e);return l!==null?pp[l]:null},mp=({docsRepo:e,docsBranch:t,docsDir:l,filePathRelative:n,editLinkPattern:i})=>{if(!n)return null;const r=fp({docsRepo:e,editLinkPattern:i});return r?r.replace(/:repo/,on(e)?e:`https://github.com/${e}`).replace(/:branch/,t).replace(/:path/,Xo(`${qo(l)}/${n}`)):null},Ea=Symbol("sidebarItems"),pr=()=>{const e=Be(Ea);if(!e)throw new Error("useSidebarItems() is called without provider.");return e},vp=()=>{const e=Me(),t=pt(),l=tl(),n=jt(),i=O(()=>kp(t.value,e.value,l.value,n.path));el(Ea,i)},kp=(e,t,l,n)=>{const i=e.sidebar??t.sidebar??"auto",r=e.sidebarDepth??t.sidebarDepth??2;return e.home||i===!1?[]:i==="auto"?wa(l,r):Array.isArray(i)?La(l,n,i,r):Jn(i)?bp(l,n,i,r):[]},_p=(e,t)=>({text:e.title,link:e.link,children:fr(e.children,t)}),fr=(e,t)=>t>0?e.map(l=>_p(l,t-1)):[],wa=(e,t)=>[{text:e.title,children:fr(e.headers,t)}],La=(e,t,l,n)=>{const i=r=>{var o;let s;if(qe(r)?s=gr(r):s=r,s.children)return{...s,children:s.children.map(a=>i(a))};if(s.link===t){const a=((o=e.headers[0])==null?void 0:o.level)===1?e.headers[0].children:e.headers;return{...s,children:fr(a,n)}}return s};return l.map(r=>i(r))},bp=(e,t,l,n)=>{const i=Yo(l,t),r=l[i]??[];return r==="heading"?wa(e,n):La(e,t,r,n)},yp="719px",xp={mobile:yp};var Kl;(function(e){e.MOBILE="mobile"})(Kl||(Kl={}));var $s;const Ep={[Kl.MOBILE]:Number.parseInt(($s=xp.mobile)==null?void 0:$s.replace("px",""),10)},Ca=(e,t)=>{const l=Ep[e];Number.isInteger(l)&&je(()=>{t(l),window.addEventListener("resize",()=>t(l),!1),window.addEventListener("orientationchange",()=>t(l),!1)})},wp={},Lp={class:"theme-default-content"};function Cp(e,t){const l=ll("Content");return U(),ee("div",Lp,[ie(l)])}const Tp=we(wp,[["render",Cp],["__file","HomeContent.vue"]]),Sp={key:0,class:"features"},Pp=fe({__name:"HomeFeatures",setup(e){const t=pt(),l=O(()=>Array.isArray(t.value.features)?t.value.features:[]);return(n,i)=>l.value.length?(U(),ee("div",Sp,[(U(!0),ee(_e,null,Nt(l.value,r=>(U(),ee("div",{key:r.title,class:"feature"},[pe("h2",null,Pe(r.title),1),pe("p",null,Pe(r.details),1)]))),128))])):Ce("",!0)}}),zp=we(Pp,[["__file","HomeFeatures.vue"]]),Ap=["innerHTML"],Op=["textContent"],Rp=fe({__name:"HomeFooter",setup(e){const t=pt(),l=O(()=>t.value.footer),n=O(()=>t.value.footerHtml);return(i,r)=>l.value?(U(),ee(_e,{key:0},[n.value?(U(),ee("div",{key:0,class:"footer",innerHTML:l.value},null,8,Ap)):(U(),ee("div",{key:1,class:"footer",textContent:Pe(l.value)},null,8,Op))],64)):Ce("",!0)}}),Ip=we(Rp,[["__file","HomeFooter.vue"]]),Fp=["href","rel","target","aria-label"],Dp=fe({inheritAttrs:!1,__name:"AutoLink",props:{item:{type:Object,required:!0}},setup(e){const t=e,l=jt(),n=ir(),{item:i}=Un(t),r=O(()=>on(i.value.link)),s=O(()=>!r.value&&md(i.value.link)),o=O(()=>{if(!s.value){if(i.value.target)return i.value.target;if(r.value)return"_blank"}}),a=O(()=>o.value==="_blank"),c=O(()=>!r.value&&!s.value&&!a.value),u=O(()=>{if(!s.value){if(i.value.rel)return i.value.rel;if(a.value)return"noopener noreferrer"}}),d=O(()=>i.value.ariaLabel||i.value.text),h=O(()=>{const b=Object.keys(n.value.locales);return b.length?!b.some(x=>x===i.value.link):i.value.link!=="/"}),p=O(()=>h.value?l.path.startsWith(i.value.link):!1),m=O(()=>c.value?i.value.activeMatch?new RegExp(i.value.activeMatch).test(l.path):p.value:!1);return(b,x)=>{const y=ll("RouteLink"),S=ll("AutoLinkExternalIcon");return c.value?(U(),Le(y,Ai({key:0,active:m.value,to:Z(i).link,"aria-label":d.value},b.$attrs),{default:$e(()=>[be(b.$slots,"before"),Vt(" "+Pe(Z(i).text)+" ",1),be(b.$slots,"after")]),_:3},16,["active","to","aria-label"])):(U(),ee("a",Ai({key:1,class:"external-link",href:Z(i).link,rel:u.value,target:o.value,"aria-label":d.value},b.$attrs),[be(b.$slots,"before"),Vt(" "+Pe(Z(i).text)+" ",1),a.value?(U(),Le(S,{key:0})):Ce("",!0),be(b.$slots,"after")],16,Fp))}}}),Et=we(Dp,[["__file","AutoLink.vue"]]),$p={class:"hero"},Mp={key:0,id:"main-title"},Np={key:1,class:"description"},Hp={key:2,class:"actions"},Vp=fe({__name:"HomeHero",setup(e){const t=pt(),l=rr(),n=hr(),i=O(()=>n.value&&t.value.heroImageDark!==void 0?t.value.heroImageDark:t.value.heroImage),r=O(()=>t.value.heroAlt||o.value||"hero"),s=O(()=>t.value.heroHeight||280),o=O(()=>t.value.heroText===null?null:t.value.heroText||l.value.title||"Hello"),a=O(()=>t.value.tagline===null?null:t.value.tagline||l.value.description||"Welcome to your VuePress site"),c=O(()=>Array.isArray(t.value.actions)?t.value.actions.map(({text:d,link:h,type:p="primary"})=>({text:d,link:h,type:p})):[]),u=()=>{if(!i.value)return null;const d=q("img",{src:ei(i.value),alt:r.value,height:s.value});return t.value.heroImageDark===void 0?d:q(or,()=>d)};return(d,h)=>(U(),ee("header",$p,[ie(u),o.value?(U(),ee("h1",Mp,Pe(o.value),1)):Ce("",!0),a.value?(U(),ee("p",Np,Pe(a.value),1)):Ce("",!0),c.value.length?(U(),ee("p",Hp,[(U(!0),ee(_e,null,Nt(c.value,p=>(U(),Le(Et,{key:p.text,class:Ke(["action-button",[p.type]]),item:p},null,8,["class","item"]))),128))])):Ce("",!0)]))}}),Bp=we(Vp,[["__file","HomeHero.vue"]]),jp={class:"home"},Up=fe({__name:"Home",setup(e){return(t,l)=>(U(),ee("main",jp,[ie(Bp),ie(zp),ie(Tp),ie(Ip)]))}}),Wp=we(Up,[["__file","Home.vue"]]),Gp=["aria-hidden"],Kp=fe({__name:"NavbarBrand",setup(e){const t=ol(),l=rr(),n=Me(),i=hr(),r=O(()=>n.value.home||t.value),s=O(()=>l.value.title),o=O(()=>i.value&&n.value.logoDark!==void 0?n.value.logoDark:n.value.logo),a=O(()=>n.value.logoAlt??s.value),c=O(()=>s.value.toLocaleUpperCase().trim()===a.value.toLocaleUpperCase().trim()),u=()=>{if(!o.value)return null;const d=q("img",{class:"logo",src:ei(o.value),alt:a.value});return n.value.logoDark===void 0?d:q(or,()=>d)};return(d,h)=>(U(),Le(Z(yt),{to:r.value},{default:$e(()=>[ie(u),s.value?(U(),ee("span",{key:0,class:Ke(["site-name",{"can-hide":o.value}]),"aria-hidden":c.value},Pe(s.value),11,Gp)):Ce("",!0)]),_:1},8,["to"]))}}),qp=we(Kp,[["__file","NavbarBrand.vue"]]),Xp=fe({__name:"DropdownTransition",setup(e){const t=n=>{n.style.height=n.scrollHeight+"px"},l=n=>{n.style.height=""};return(n,i)=>(U(),Le(sn,{name:"dropdown",onEnter:t,onAfterEnter:l,onBeforeLeave:t},{default:$e(()=>[be(n.$slots,"default")]),_:3}))}}),Ta=we(Xp,[["__file","DropdownTransition.vue"]]),Yp=["aria-label"],Jp={class:"title"},Qp=pe("span",{class:"arrow down"},null,-1),Zp=["aria-label"],ef={class:"title"},tf={class:"navbar-dropdown"},lf={class:"navbar-dropdown-subtitle"},nf={key:1},rf={class:"navbar-dropdown-subitem-wrapper"},sf=fe({__name:"NavbarDropdown",props:{item:{type:Object,required:!0}},setup(e){const t=e,{item:l}=Un(t),n=O(()=>l.value.ariaLabel||l.value.text),i=ae(!1),r=jt();Fe(()=>r.path,()=>{i.value=!1});const s=a=>{a.detail===0?i.value=!i.value:i.value=!1},o=(a,c)=>c[c.length-1]===a;return(a,c)=>(U(),ee("div",{class:Ke(["navbar-dropdown-wrapper",{open:i.value}])},[pe("button",{class:"navbar-dropdown-title",type:"button","aria-label":n.value,onClick:s},[pe("span",Jp,Pe(Z(l).text),1),Qp],8,Yp),pe("button",{class:"navbar-dropdown-title-mobile",type:"button","aria-label":n.value,onClick:c[0]||(c[0]=u=>i.value=!i.value)},[pe("span",ef,Pe(Z(l).text),1),pe("span",{class:Ke(["arrow",i.value?"down":"right"])},null,2)],8,Zp),ie(Ta,null,{default:$e(()=>[On(pe("ul",tf,[(U(!0),ee(_e,null,Nt(Z(l).children,u=>(U(),ee("li",{key:u.text,class:"navbar-dropdown-item"},[u.children?(U(),ee(_e,{key:0},[pe("h4",lf,[u.link?(U(),Le(Et,{key:0,item:u,onFocusout:d=>o(u,Z(l).children)&&u.children.length===0&&(i.value=!1)},null,8,["item","onFocusout"])):(U(),ee("span",nf,Pe(u.text),1))]),pe("ul",rf,[(U(!0),ee(_e,null,Nt(u.children,d=>(U(),ee("li",{key:d.link,class:"navbar-dropdown-subitem"},[ie(Et,{item:d,onFocusout:h=>o(d,u.children)&&o(u,Z(l).children)&&(i.value=!1)},null,8,["item","onFocusout"])]))),128))])],64)):(U(),Le(Et,{key:1,item:u,onFocusout:d=>o(u,Z(l).children)&&(i.value=!1)},null,8,["item","onFocusout"]))]))),128))],512),[[$n,i.value]])]),_:1})],2))}}),of=we(sf,[["__file","NavbarDropdown.vue"]]),af=["aria-label"],cf=fe({__name:"NavbarItems",setup(e){const t=()=>{const d=jt(),h=fg(),p=ol(),m=ir(),b=rr(),x=cp(),y=Me();return O(()=>{const S=Object.keys(m.value.locales);if(S.length<2)return[];const v=d.path,w=d.fullPath;return[{text:`${y.value.selectLanguageText}`,ariaLabel:`${y.value.selectLanguageAriaLabel??y.value.selectLanguageText}`,children:S.map(I=>{var L,$;const M=((L=m.value.locales)==null?void 0:L[I])??{},k=(($=x.value.locales)==null?void 0:$[I])??{},N=`${M.lang}`,z=k.selectLanguageName??N;let X;if(N===b.value.lang)X=w;else{const le=v.replace(p.value,I);h.value.some(re=>re===le)?X=w.replace(v,le):X=k.home??I}return{text:z,link:X}})}]})},l=()=>{const d=Me(),h=O(()=>d.value.repo),p=O(()=>h.value?xa(h.value):null),m=O(()=>h.value&&!on(h.value)?`https://github.com/${h.value}`:h.value),b=O(()=>m.value?d.value.repoLabel?d.value.repoLabel:p.value===null?"Source":p.value:null);return O(()=>!m.value||!b.value?[]:[{text:b.value,link:m.value}])},n=d=>qe(d)?gr(d):d.children?{...d,children:d.children.map(h=>n(h))}:d,i=()=>{const d=Me();return O(()=>(d.value.navbar||[]).map(h=>n(h)))},r=ae(!1),s=i(),o=t(),a=l(),c=O(()=>[...s.value,...o.value,...a.value]);Ca(Kl.MOBILE,d=>{window.innerWidthMe().value.navbarLabel??"site navigation");return(d,h)=>c.value.length?(U(),ee("nav",{key:0,class:"navbar-items","aria-label":u.value},[(U(!0),ee(_e,null,Nt(c.value,p=>(U(),ee("div",{key:p.text,class:"navbar-item"},["children"in p?(U(),Le(of,{key:0,item:p,class:Ke(r.value?"mobile":"")},null,8,["item","class"])):(U(),Le(Et,{key:1,item:p},null,8,["item"]))]))),128))],8,af)):Ce("",!0)}}),Sa=we(cf,[["__file","NavbarItems.vue"]]),uf=["title"],df={class:"icon",focusable:"false",viewBox:"0 0 32 32"},hf=vu('',9),gf=[hf],pf={class:"icon",focusable:"false",viewBox:"0 0 32 32"},ff=pe("path",{d:"M13.502 5.414a15.075 15.075 0 0 0 11.594 18.194a11.113 11.113 0 0 1-7.975 3.39c-.138 0-.278.005-.418 0a11.094 11.094 0 0 1-3.2-21.584M14.98 3a1.002 1.002 0 0 0-.175.016a13.096 13.096 0 0 0 1.825 25.981c.164.006.328 0 .49 0a13.072 13.072 0 0 0 10.703-5.555a1.01 1.01 0 0 0-.783-1.565A13.08 13.08 0 0 1 15.89 4.38A1.015 1.015 0 0 0 14.98 3z",fill:"currentColor"},null,-1),mf=[ff],vf=fe({__name:"ToggleColorModeButton",setup(e){const t=Me(),l=hr(),n=()=>{l.value=!l.value};return(i,r)=>(U(),ee("button",{class:"toggle-color-mode-button",title:Z(t).toggleColorMode,onClick:n},[On((U(),ee("svg",df,gf,512)),[[$n,!Z(l)]]),On((U(),ee("svg",pf,mf,512)),[[$n,Z(l)]])],8,uf))}}),kf=we(vf,[["__file","ToggleColorModeButton.vue"]]),_f=["title"],bf=pe("div",{class:"icon","aria-hidden":"true"},[pe("span"),pe("span"),pe("span")],-1),yf=[bf],xf=fe({__name:"ToggleSidebarButton",emits:["toggle"],setup(e){const t=Me();return(l,n)=>(U(),ee("div",{class:"toggle-sidebar-button",title:Z(t).toggleSidebar,"aria-expanded":"false",role:"button",tabindex:"0",onClick:n[0]||(n[0]=i=>l.$emit("toggle"))},yf,8,_f))}}),Ef=we(xf,[["__file","ToggleSidebarButton.vue"]]),wf=fe({__name:"Navbar",emits:["toggle-sidebar"],setup(e){const t=Me(),l=ae(null),n=ae(null),i=ae(0),r=O(()=>i.value?{maxWidth:i.value+"px"}:{});Ca(Kl.MOBILE,o=>{var c;const a=s(l.value,"paddingLeft")+s(l.value,"paddingRight");window.innerWidth{const c=ll("NavbarSearch");return U(),ee("header",{ref_key:"navbar",ref:l,class:"navbar"},[ie(Ef,{onToggle:a[0]||(a[0]=u=>o.$emit("toggle-sidebar"))}),pe("span",{ref_key:"navbarBrand",ref:n},[ie(qp)],512),pe("div",{class:"navbar-items-wrapper",style:Yl(r.value)},[be(o.$slots,"before"),ie(Sa,{class:"can-hide"}),be(o.$slots,"after"),Z(t).colorModeSwitch?(U(),Le(kf,{key:0})):Ce("",!0),ie(c)],4)],512)}}}),Lf=we(wf,[["__file","Navbar.vue"]]),Cf={class:"page-meta"},Tf={key:0,class:"meta-item edit-link"},Sf={key:1,class:"meta-item last-updated"},Pf={class:"meta-item-label"},zf={class:"meta-item-info"},Af={key:2,class:"meta-item contributors"},Of={class:"meta-item-label"},Rf={class:"meta-item-info"},If=["title"],Ff=fe({__name:"PageMeta",setup(e){const t=()=>{const a=Me(),c=tl(),u=pt();return O(()=>{if(!(u.value.editLink??a.value.editLink??!0))return null;const{repo:h,docsRepo:p=h,docsBranch:m="main",docsDir:b="",editLinkText:x}=a.value;if(!p)return null;const y=mp({docsRepo:p,docsBranch:m,docsDir:b,filePathRelative:c.value.filePathRelative,editLinkPattern:u.value.editLinkPattern??a.value.editLinkPattern});return y?{text:x??"Edit this page",link:y}:null})},l=()=>{const a=Me(),c=tl(),u=pt();return O(()=>{var p,m;return!(u.value.lastUpdated??a.value.lastUpdated??!0)||!((p=c.value.git)!=null&&p.updatedTime)?null:new Date((m=c.value.git)==null?void 0:m.updatedTime).toLocaleString()})},n=()=>{const a=Me(),c=tl(),u=pt();return O(()=>{var h;return u.value.contributors??a.value.contributors??!0?((h=c.value.git)==null?void 0:h.contributors)??null:null})},i=Me(),r=t(),s=l(),o=n();return(a,c)=>{const u=ll("ClientOnly");return U(),ee("footer",Cf,[Z(r)?(U(),ee("div",Tf,[ie(Et,{class:"meta-item-label",item:Z(r)},null,8,["item"])])):Ce("",!0),Z(s)?(U(),ee("div",Sf,[pe("span",Pf,Pe(Z(i).lastUpdatedText)+": ",1),ie(u,null,{default:$e(()=>[pe("span",zf,Pe(Z(s)),1)]),_:1})])):Ce("",!0),Z(o)&&Z(o).length?(U(),ee("div",Af,[pe("span",Of,Pe(Z(i).contributorsText)+": ",1),pe("span",Rf,[(U(!0),ee(_e,null,Nt(Z(o),(d,h)=>(U(),ee(_e,{key:h},[pe("span",{class:"contributor",title:`email: ${d.email}`},Pe(d.name),9,If),h!==Z(o).length-1?(U(),ee(_e,{key:0},[Vt(", ")],64)):Ce("",!0)],64))),128))])])):Ce("",!0)])}}}),Df=we(Ff,[["__file","PageMeta.vue"]]),$f=["aria-label"],Mf={class:"inner"},Nf={key:0,class:"prev"},Hf={key:1,class:"next"},Vf=fe({__name:"PageNav",setup(e){const t=(u,d)=>d===!1?null:qe(d)?gr(u):Jn(d)?d:!1,l=(u,d,h)=>{const p=u.findIndex(m=>m.link===d);if(p!==-1){const m=u[p+h];return m!=null&&m.link?m:null}for(const m of u)if(m.children){const b=l(m.children,d,h);if(b)return b}return null},n=pt(),i=pr(),r=jt(),s=Bt(),o=O(()=>{const u=t(s,n.value.prev);return u!==!1?u:l(i.value,r.path,-1)}),a=O(()=>{const u=t(s,n.value.next);return u!==!1?u:l(i.value,r.path,1)}),c=O(()=>Me().value.pageNavbarLabel??"page navigation");return(u,d)=>o.value||a.value?(U(),ee("nav",{key:0,class:"page-nav","aria-label":c.value},[pe("p",Mf,[o.value?(U(),ee("span",Nf,[ie(Et,{item:o.value},null,8,["item"])])):Ce("",!0),a.value?(U(),ee("span",Hf,[ie(Et,{item:a.value},null,8,["item"])])):Ce("",!0)])],8,$f)):Ce("",!0)}}),Bf=we(Vf,[["__file","PageNav.vue"]]),jf={class:"page"},Uf={class:"theme-default-content"},Wf=fe({__name:"Page",setup(e){return(t,l)=>{const n=ll("Content");return U(),ee("main",jf,[be(t.$slots,"top"),pe("div",Uf,[be(t.$slots,"content-top"),ie(n),be(t.$slots,"content-bottom")]),ie(Df),ie(Bf),be(t.$slots,"bottom")])}}}),Gf=we(Wf,[["__file","Page.vue"]]),Kf={class:"sidebar-item-children"},qf=fe({__name:"SidebarItem",props:{item:{type:Object,required:!0},depth:{type:Number,required:!1,default:0}},setup(e){const t=e,{item:l,depth:n}=Un(t),i=jt(),r=Bt(),s=O(()=>ya(l.value,i)),o=O(()=>({"sidebar-item":!0,"sidebar-heading":n.value===0,active:s.value,collapsible:l.value.collapsible})),a=O(()=>l.value.collapsible?s.value:!0),[c,u]=Gh(a.value),d=p=>{l.value.collapsible&&(p.preventDefault(),u())},h=r.afterEach(p=>{El(()=>{c.value=a.value})});return qn(()=>{h()}),(p,m)=>{var x;const b=ll("SidebarItem",!0);return U(),ee("li",null,[Z(l).link?(U(),Le(Et,{key:0,class:Ke(o.value),item:Z(l)},null,8,["class","item"])):(U(),ee("p",{key:1,tabindex:"0",class:Ke(o.value),onClick:d,onKeydown:id(d,["enter"])},[Vt(Pe(Z(l).text)+" ",1),Z(l).collapsible?(U(),ee("span",{key:0,class:Ke(["arrow",Z(c)?"down":"right"])},null,2)):Ce("",!0)],34)),(x=Z(l).children)!=null&&x.length?(U(),Le(Ta,{key:2},{default:$e(()=>[On(pe("ul",Kf,[(U(!0),ee(_e,null,Nt(Z(l).children,y=>(U(),Le(b,{key:`${Z(n)}${y.text}${y.link}`,item:y,depth:Z(n)+1},null,8,["item","depth"]))),128))],512),[[$n,Z(c)]])]),_:1})):Ce("",!0)])}}}),Xf=we(qf,[["__file","SidebarItem.vue"]]),Yf={key:0,class:"sidebar-items"},Jf=fe({__name:"SidebarItems",setup(e){const t=jt(),l=pr();return je(()=>{Fe(()=>t.hash,n=>{const i=document.querySelector(".sidebar");if(!i)return;const r=document.querySelector(`.sidebar a.sidebar-item[href="${t.path}${n}"]`);if(!r)return;const{top:s,height:o}=i.getBoundingClientRect(),{top:a,height:c}=r.getBoundingClientRect();as+o&&r.scrollIntoView(!1)})}),(n,i)=>Z(l).length?(U(),ee("ul",Yf,[(U(!0),ee(_e,null,Nt(Z(l),r=>(U(),Le(Xf,{key:`${r.text}${r.link}`,item:r},null,8,["item"]))),128))])):Ce("",!0)}}),Qf=we(Jf,[["__file","SidebarItems.vue"]]),Zf={class:"sidebar"},em=fe({__name:"Sidebar",setup(e){return(t,l)=>(U(),ee("aside",Zf,[ie(Sa),be(t.$slots,"top"),ie(Qf),be(t.$slots,"bottom")]))}}),tm=we(em,[["__file","Sidebar.vue"]]),lm=fe({__name:"Layout",setup(e){const t=tl(),l=pt(),n=Me(),i=O(()=>l.value.navbar!==!1&&n.value.navbar!==!1),r=pr(),s=ae(!1),o=x=>{s.value=typeof x=="boolean"?x:!s.value},a={x:0,y:0},c=x=>{a.x=x.changedTouches[0].clientX,a.y=x.changedTouches[0].clientY},u=x=>{const y=x.changedTouches[0].clientX-a.x,S=x.changedTouches[0].clientY-a.y;Math.abs(y)>Math.abs(S)&&Math.abs(y)>40&&(y>0&&a.x<=80?o(!0):o(!1))},d=O(()=>[{"no-navbar":!i.value,"no-sidebar":!r.value.length,"sidebar-open":s.value},l.value.pageClass]);let h;je(()=>{h=Bt().afterEach(()=>{o(!1)})}),tn(()=>{h()});const p=ba(),m=p.resolve,b=p.pending;return(x,y)=>(U(),ee("div",{class:Ke(["theme-container",d.value]),onTouchstart:c,onTouchend:u},[be(x.$slots,"navbar",{},()=>[i.value?(U(),Le(Lf,{key:0,onToggleSidebar:o},{before:$e(()=>[be(x.$slots,"navbar-before")]),after:$e(()=>[be(x.$slots,"navbar-after")]),_:3})):Ce("",!0)]),pe("div",{class:"sidebar-mask",onClick:y[0]||(y[0]=S=>o(!1))}),be(x.$slots,"sidebar",{},()=>[ie(tm,null,{top:$e(()=>[be(x.$slots,"sidebar-top")]),bottom:$e(()=>[be(x.$slots,"sidebar-bottom")]),_:3})]),be(x.$slots,"page",{},()=>[Z(l).home?(U(),Le(Wp,{key:0})):(U(),Le(sn,{key:1,name:"fade-slide-y",mode:"out-in",onBeforeEnter:Z(m),onBeforeLeave:Z(b)},{default:$e(()=>[(U(),Le(Gf,{key:Z(t).path},{top:$e(()=>[be(x.$slots,"page-top")]),"content-top":$e(()=>[be(x.$slots,"page-content-top")]),"content-bottom":$e(()=>[be(x.$slots,"page-content-bottom")]),bottom:$e(()=>[be(x.$slots,"page-bottom")]),_:3}))]),_:3},8,["onBeforeEnter","onBeforeLeave"]))])],34))}}),nm=we(lm,[["__file","Layout.vue"]]),im={class:"theme-container"},rm={class:"page"},sm={class:"theme-default-content"},om=pe("h1",null,"404",-1),am=fe({__name:"NotFound",setup(e){const t=ol(),l=Me(),n=l.value.notFound??["Not Found"],i=()=>n[Math.floor(Math.random()*n.length)],r=l.value.home??t.value,s=l.value.backToHome??"Back to home";return(o,a)=>(U(),ee("div",im,[pe("main",rm,[pe("div",sm,[om,pe("blockquote",null,Pe(i()),1),ie(Z(yt),{to:Z(r)},{default:$e(()=>[Vt(Pe(Z(s)),1)]),_:1},8,["to"])])])]))}}),cm=we(am,[["__file","NotFound.vue"]]),um=ct({enhance({app:e,router:t}){Ln("Badge")||e.component("Badge",rp),Ln("CodeGroup")||e.component("CodeGroup",sp),Ln("CodeGroupItem")||e.component("CodeGroupItem",ap),e.component("AutoLinkExternalIcon",()=>{const n=e.component("ExternalLinkIcon");return n?q(n):null}),e.component("NavbarSearch",()=>{const n=e.component("Docsearch")||e.component("SearchBox");return n?q(n):null});const l=t.options.scrollBehavior;t.options.scrollBehavior=async(...n)=>(await ba().wait(),l(...n))},setup(){up(),vp()},layouts:{Layout:nm,NotFound:cm}});let dm=e=>qe(e.title)?{title:e.title}:null;const Pa=Symbol(""),hm=()=>Be(Pa),gm=e=>{e.provide(Pa,dm)};var pm={"/zh/":{title:"目录",empty:"暂无目录"},"/":{title:"Catalog",empty:"No catalog"}};const fm=fe({name:"Catalog",props:{base:{type:String,default:""},level:{type:Number,default:3},index:Boolean,hideHeading:Boolean},setup(e){const t=hm(),l=ur(pm),n=tl(),i=oa(),r=ir(),o=sl(fa(i.value).map(([c,{meta:u}])=>{const d=t(u);if(!d)return null;const h=c.split("/").length;return{level:mg(c,"/")?h-2:h-1,base:c.replace(/\/[^/]+\/?$/,"/"),path:c,...d}}).filter(c=>Jn(c)&&qe(c.title))),a=O(()=>{const c=e.base?pd(fd(e.base)):n.value.path.replace(/\/[^/]+$/,"/"),u=c.split("/").length-2,d=[];return o.value.filter(({level:h,path:p})=>{if(!ws(p,c)||p===c)return!1;if(c==="/"){const m=vg(r.value.locales).filter(b=>b!=="/");if(p==="/404.html"||m.some(b=>ws(p,b)))return!1}return h-u<=e.level}).sort(({title:h,level:p,order:m},{title:b,level:x,order:y})=>{const S=p-x;return S||(_i(m)?_i(y)?m>0?y>0?m-y:-1:y<0?m-y:1:m:_i(y)?y:h.localeCompare(b))}).forEach(h=>{var b;const{base:p,level:m}=h;switch(m-u){case 1:{d.push(h);break}case 2:{const x=d.find(y=>y.path===p);x&&(x.children??(x.children=[])).push(h);break}default:{const x=d.find(y=>y.path===p.replace(/\/[^/]+\/$/,"/"));if(x){const y=(b=x.children)==null?void 0:b.find(S=>S.path===p);y&&(y.children??(y.children=[])).push(h)}}}}),d});return()=>{const c=a.value.some(u=>u.children);return q("div",{class:["vp-catalog-wrapper",{index:e.index}]},[e.hideHeading?null:q("h2",{class:"vp-catalog-main-title"},l.value.title),a.value.length?q(e.index?"ol":"ul",{class:["vp-catalogs",{deep:c}]},a.value.map(({children:u=[],title:d,path:h,content:p})=>{const m=q(yt,{class:"vp-catalog-title",to:h},()=>p?q(p):d);return q("li",{class:"vp-catalog"},c?[q("h3",{id:d,class:["vp-catalog-child-title",{"has-children":u.length}]},[q("a",{href:`#${d}`,class:"vp-catalog-header-anchor","aria-hidden":!0},"#"),m]),u.length?q(e.index?"ol":"ul",{class:"vp-child-catalogs"},u.map(({children:b=[],content:x,path:y,title:S})=>q("li",{class:"vp-child-catalog"},[q("div",{class:["vp-catalog-sub-title",{"has-children":b.length}]},[q("a",{href:`#${S}`,class:"vp-catalog-header-anchor"},"#"),q(yt,{class:"vp-catalog-title",to:y},()=>x?q(x):S)]),b.length?q(e.index?"ol":"div",{class:e.index?"vp-sub-catalogs":"vp-sub-catalogs-wrapper"},b.map(({content:v,path:w,title:H})=>e.index?q("li",{class:"vp-sub-catalog"},q(yt,{to:w},()=>v?q(v):H)):q(yt,{class:"vp-sub-catalog-link",to:w},()=>v?q(v):H))):null]))):null]:q("div",{class:"vp-catalog-child-title"},m))})):q("p",{class:"vp-empty-catalog"},l.value.empty)])}}}),mm=ct({enhance:({app:e})=>{gm(e),Ln("Catalog",e)||e.component("Catalog",fm)}});var vm={autoLocale:!1,switchLocale:"modal",localeConfig:{"/zh/":["zh-CN"]},defaultLocale:"/zh/",localeFallback:!0,defaultBehavior:"defaultLocale"};const za=vm,As=fa(za.localeConfig);var km={"/zh/":{name:"简体中文",hint:"您的首选语言是 $1,是否切换到该语言?",switch:"切换到 $1",cancel:"取消"},"/":{name:"English",hint:"Your primary language is $1, do you want to switch to it?",switch:"Switch to $1",cancel:"Cancel"}};const Os=km,{switchLocale:Rs}=za,Is=sg("VUEPRESS_REDIRECT_LOCALES",{}),_m=fe({name:"LanguageSwitch",setup(){const e=ng(),t=jt(),l=Bt(),n=ol(),i=ae(!1),r=O(()=>{if(As.some(([c])=>n.value===c)){for(const c of e.value)for(const[u,d]of As)if(d.includes(c))return u===n.value?null:{lang:c,localePath:u}}return null}),s=O(()=>{if(r.value){const{lang:c,localePath:u}=r.value,d=[Os[n.value],Os[u]];return{hint:d.map(({hint:h})=>h.replace("$1",c)),switch:d.map(({switch:h})=>h.replace("$1",c)).join(" / "),cancel:d.map(({cancel:h})=>h).join(" / ")}}return null}),o=O(()=>r.value?t.path.replace(n.value,r.value.localePath):null),a=()=>{Is.value[n.value]=!0,i.value=!1};return je(()=>{const c=rg(document.body);Is.value[n.value]||(r.value?Rs==="direct"?l.replace(o.value):Rs==="modal"?i.value=!0:i.value=!1:i.value=!1),Fe(i,u=>{c.value=u},{immediate:!0}),tn(()=>{c.value=!1})}),()=>i.value?q(Qu,{name:"lang-modal-fade"},()=>{var c,u,d;return i.value?q("div",{key:"mask",class:"lang-modal-mask"},q("div",{key:"popup",class:"lang-modal-wrapper"},[q("div",{class:"lang-modal-content"},(c=s.value)==null?void 0:c.hint.map(h=>q("p",h))),q("button",{type:"button",class:"lang-modal-action primary",onClick:()=>{a(),l.replace(o.value)}},(u=s.value)==null?void 0:u.switch),q("button",{type:"button",class:"lang-modal-action",onClick:()=>a()},(d=s.value)==null?void 0:d.cancel)])):null}):null}}),bm=ct({setup(){},rootComponents:[_m]}),ym={enhance:({app:e})=>{e.component("NpmBadge",Eo(()=>E(()=>import("./NpmBadge-Bn7Nog-o.js"),__vite__mapDeps([]))))}},xm=e=>e instanceof Element?document.activeElement===e&&(["TEXTAREA","SELECT","INPUT"].includes(e.tagName)||e.hasAttribute("contenteditable")):!1,Em=(e,t)=>t.some(l=>{if(qe(l))return l===e.key;const{key:n,ctrl:i=!1,shift:r=!1,alt:s=!1}=l;return n===e.key&&i===e.ctrlKey&&r===e.shiftKey&&s===e.altKey}),wm=/[^\x00-\x7F]/,Lm=e=>e.split(/\s+/g).map(t=>t.trim()).filter(t=>!!t),Fs=e=>e.replace(/[-/\\^$*+?.()|[\]{}]/g,"\\$&"),Ds=(e,t)=>{const l=t.join(" "),n=Lm(e);if(wm.test(e))return n.some(s=>l.toLowerCase().indexOf(s)>-1);const i=e.endsWith(" ");return new RegExp(n.map((s,o)=>n.length===o+1&&!i?`(?=.*\\b${Fs(s)})`:`(?=.*\\b${Fs(s)}\\b)`).join("")+".+","gi").test(l)},Cm=({input:e,hotKeys:t})=>{if(t.value.length===0)return;const l=n=>{e.value&&Em(n,t.value)&&!xm(n.target)&&(n.preventDefault(),e.value.focus())};je(()=>{document.addEventListener("keydown",l)}),qn(()=>{document.removeEventListener("keydown",l)})},Tm=[{title:"Home",headers:[],path:"/",pathLocale:"/",extraFields:[]},{title:"Themes",headers:[],path:"/themes/",pathLocale:"/",extraFields:[]},{title:"Plugins",headers:[],path:"/plugins/",pathLocale:"/",extraFields:[]},{title:"active-header-links",headers:[{level:2,title:"Usage",slug:"usage",link:"#usage",children:[]},{level:2,title:"Options",slug:"options",link:"#options",children:[{level:3,title:"headerLinkSelector",slug:"headerlinkselector",link:"#headerlinkselector",children:[]},{level:3,title:"headerAnchorSelector",slug:"headeranchorselector",link:"#headeranchorselector",children:[]},{level:3,title:"delay",slug:"delay",link:"#delay",children:[]},{level:3,title:"offset",slug:"offset",link:"#offset",children:[]}]}],path:"/plugins/active-header-links.html",pathLocale:"/",extraFields:[]},{title:"back-to-top",headers:[{level:2,title:"Usage",slug:"usage",link:"#usage",children:[]},{level:2,title:"Options",slug:"options",link:"#options",children:[{level:3,title:"threshold",slug:"threshold",link:"#threshold",children:[]},{level:3,title:"progress",slug:"progress",link:"#progress",children:[]}]},{level:2,title:"Styles",slug:"styles",link:"#styles",children:[]}],path:"/plugins/back-to-top.html",pathLocale:"/",extraFields:[]},{title:"baidu-analytics",headers:[{level:2,title:"Usage",slug:"usage",link:"#usage",children:[{level:3,title:"Reporting Events",slug:"reporting-events",link:"#reporting-events",children:[]}]},{level:2,title:"Options",slug:"options",link:"#options",children:[{level:3,title:"id",slug:"id",link:"#id",children:[]}]}],path:"/plugins/baidu-analytics.html",pathLocale:"/",extraFields:[]},{title:"catalog",headers:[{level:2,title:"Usage",slug:"usage",link:"#usage",children:[]},{level:2,title:"Options",slug:"options",link:"#options",children:[{level:3,title:"level",slug:"level",link:"#level",children:[]},{level:3,title:"index",slug:"index",link:"#index",children:[]},{level:3,title:"frontmatter",slug:"frontmatter",link:"#frontmatter",children:[]},{level:3,title:"exclude",slug:"exclude",link:"#exclude",children:[]},{level:3,title:"component",slug:"component",link:"#component",children:[]},{level:3,title:"locales",slug:"locales",link:"#locales",children:[]}]},{level:2,title:"Client options",slug:"client-options",link:"#client-options",children:[{level:3,title:"defineCatalogInfoGetter",slug:"definecataloginfogetter",link:"#definecataloginfogetter",children:[]}]},{level:2,title:"Components",slug:"components",link:"#components",children:[{level:3,title:"Catalog",slug:"catalog-1",link:"#catalog-1",children:[]}]},{level:2,title:"Styles",slug:"styles",link:"#styles",children:[]}],path:"/plugins/catalog.html",pathLocale:"/",extraFields:[]},{title:"container",headers:[{level:2,title:"Usage",slug:"usage",link:"#usage",children:[]},{level:2,title:"Container Syntax",slug:"container-syntax",link:"#container-syntax",children:[]},{level:2,title:"Options",slug:"options",link:"#options",children:[{level:3,title:"type",slug:"type",link:"#type",children:[]},{level:3,title:"locales",slug:"locales",link:"#locales",children:[]},{level:3,title:"before",slug:"before",link:"#before",children:[]},{level:3,title:"after",slug:"after",link:"#after",children:[]},{level:3,title:"render",slug:"render",link:"#render",children:[]},{level:3,title:"validate",slug:"validate",link:"#validate",children:[]},{level:3,title:"marker",slug:"marker",link:"#marker",children:[]}]}],path:"/plugins/container.html",pathLocale:"/",extraFields:[]},{title:"copy-code",headers:[{level:2,title:"Usage",slug:"usage",link:"#usage",children:[]},{level:2,title:"Options",slug:"options",link:"#options",children:[{level:3,title:"selector",slug:"selector",link:"#selector",children:[]},{level:3,title:"showInMobile",slug:"showinmobile",link:"#showinmobile",children:[]},{level:3,title:"duration",slug:"duration",link:"#duration",children:[]},{level:3,title:"delay",slug:"delay",link:"#delay",children:[]},{level:3,title:"locales",slug:"locales",link:"#locales",children:[]}]},{level:2,title:"Styles",slug:"styles",link:"#styles",children:[]}],path:"/plugins/copy-code.html",pathLocale:"/",extraFields:[]},{title:"copyright",headers:[{level:2,title:"Usage",slug:"usage",link:"#usage",children:[{level:3,title:"Enabling Copyright",slug:"enabling-copyright",link:"#enabling-copyright",children:[]},{level:3,title:"Disable Copy and Selection",slug:"disable-copy-and-selection",link:"#disable-copy-and-selection",children:[]}]},{level:2,title:"Options",slug:"options",link:"#options",children:[{level:3,title:"author",slug:"author",link:"#author",children:[]},{level:3,title:"license",slug:"license",link:"#license",children:[]},{level:3,title:"authorGetter",slug:"authorgetter",link:"#authorgetter",children:[]},{level:3,title:"licenseGetter",slug:"licensegetter",link:"#licensegetter",children:[]},{level:3,title:"copyrightGetter",slug:"copyrightgetter",link:"#copyrightgetter",children:[]},{level:3,title:"triggerLength",slug:"triggerlength",link:"#triggerlength",children:[]},{level:3,title:"maxLength",slug:"maxlength",link:"#maxlength",children:[]},{level:3,title:"global",slug:"global",link:"#global",children:[]},{level:3,title:"disableCopy",slug:"disablecopy",link:"#disablecopy",children:[]},{level:3,title:"disableSelection",slug:"disableselection",link:"#disableselection",children:[]},{level:3,title:"canonical",slug:"canonical",link:"#canonical",children:[]},{level:3,title:"locales",slug:"locales",link:"#locales",children:[]}]},{level:2,title:"Frontmatter",slug:"frontmatter",link:"#frontmatter",children:[{level:3,title:"copy.triggerLength",slug:"copy-triggerlength",link:"#copy-triggerlength",children:[]},{level:3,title:"copy.maxLength",slug:"copy-maxlength",link:"#copy-maxlength",children:[]},{level:3,title:"copy.disableCopy",slug:"copy-disablecopy",link:"#copy-disablecopy",children:[]},{level:3,title:"copy.disableSelection",slug:"copy-disableselection",link:"#copy-disableselection",children:[]}]}],path:"/plugins/copyright.html",pathLocale:"/",extraFields:[]},{title:"docsearch",headers:[{level:2,title:"Usage",slug:"usage",link:"#usage",children:[]},{level:2,title:"Get Search Index",slug:"get-search-index",link:"#get-search-index",children:[]},{level:2,title:"Options",slug:"options",link:"#options",children:[{level:3,title:"apiKey",slug:"apikey",link:"#apikey",children:[]},{level:3,title:"indexName",slug:"indexname",link:"#indexname",children:[]},{level:3,title:"appId",slug:"appid",link:"#appid",children:[]},{level:3,title:"searchParameters",slug:"searchparameters",link:"#searchparameters",children:[]},{level:3,title:"placeholder",slug:"placeholder",link:"#placeholder",children:[]},{level:3,title:"disableUserPersonalization",slug:"disableuserpersonalization",link:"#disableuserpersonalization",children:[]},{level:3,title:"initialQuery",slug:"initialquery",link:"#initialquery",children:[]},{level:3,title:"translations",slug:"translations",link:"#translations",children:[]},{level:3,title:"locales",slug:"locales",link:"#locales",children:[]},{level:3,title:"indexBase",slug:"indexbase",link:"#indexbase",children:[]},{level:3,title:"injectStyles",slug:"injectstyles",link:"#injectstyles",children:[]}]},{level:2,title:"Styles",slug:"styles",link:"#styles",children:[]},{level:2,title:"Components",slug:"components",link:"#components",children:[{level:3,title:"Docsearch",slug:"docsearch-1",link:"#docsearch-1",children:[]}]}],path:"/plugins/docsearch.html",pathLocale:"/",extraFields:[]},{title:"external-link-icon",headers:[{level:2,title:"Usage",slug:"usage",link:"#usage",children:[]},{level:2,title:"Options",slug:"options",link:"#options",children:[{level:3,title:"locales",slug:"locales",link:"#locales",children:[]}]},{level:2,title:"Frontmatter",slug:"frontmatter",link:"#frontmatter",children:[{level:3,title:"externalLinkIcon",slug:"externallinkicon",link:"#externallinkicon",children:[]}]},{level:2,title:"Styles",slug:"styles",link:"#styles",children:[]},{level:2,title:"Components",slug:"components",link:"#components",children:[{level:3,title:"ExternalLinkIcon",slug:"externallinkicon-1",link:"#externallinkicon-1",children:[]}]}],path:"/plugins/external-link-icon.html",pathLocale:"/",extraFields:[]},{title:"git",headers:[{level:2,title:"Usage",slug:"usage",link:"#usage",children:[]},{level:2,title:"Git Repository",slug:"git-repository",link:"#git-repository",children:[]},{level:2,title:"Options",slug:"options",link:"#options",children:[{level:3,title:"createdTime",slug:"createdtime",link:"#createdtime",children:[]},{level:3,title:"updatedTime",slug:"updatedtime",link:"#updatedtime",children:[]},{level:3,title:"contributors",slug:"contributors",link:"#contributors",children:[]}]},{level:2,title:"Frontmatter",slug:"frontmatter",link:"#frontmatter",children:[{level:3,title:"gitInclude",slug:"gitinclude",link:"#gitinclude",children:[]}]},{level:2,title:"Page Data",slug:"page-data",link:"#page-data",children:[{level:3,title:"git.createdTime",slug:"git-createdtime",link:"#git-createdtime",children:[]},{level:3,title:"git.updatedTime",slug:"git-updatedtime",link:"#git-updatedtime",children:[]},{level:3,title:"git.contributors",slug:"git-contributors",link:"#git-contributors",children:[]}]}],path:"/plugins/git.html",pathLocale:"/",extraFields:[]},{title:"google-analytics",headers:[{level:2,title:"Usage",slug:"usage",link:"#usage",children:[{level:3,title:"Reporting Events",slug:"reporting-events",link:"#reporting-events",children:[]}]},{level:2,title:"Options",slug:"options",link:"#options",children:[{level:3,title:"id",slug:"id",link:"#id",children:[]},{level:3,title:"debug",slug:"debug",link:"#debug",children:[]}]}],path:"/plugins/google-analytics.html",pathLocale:"/",extraFields:[]},{title:"medium-zoom",headers:[{level:2,title:"Usage",slug:"usage",link:"#usage",children:[]},{level:2,title:"Options",slug:"options",link:"#options",children:[{level:3,title:"selector",slug:"selector",link:"#selector",children:[]},{level:3,title:"delay",slug:"delay",link:"#delay",children:[]},{level:3,title:"zoomOptions",slug:"zoomoptions",link:"#zoomoptions",children:[]}]},{level:2,title:"Styles",slug:"styles",link:"#styles",children:[]},{level:2,title:"Composition API",slug:"composition-api",link:"#composition-api",children:[{level:3,title:"useMediumZoom",slug:"usemediumzoom",link:"#usemediumzoom",children:[]}]}],path:"/plugins/medium-zoom.html",pathLocale:"/",extraFields:[]},{title:"nprogress",headers:[{level:2,title:"Usage",slug:"usage",link:"#usage",children:[]},{level:2,title:"Styles",slug:"styles",link:"#styles",children:[]}],path:"/plugins/nprogress.html",pathLocale:"/",extraFields:[]},{title:"palette",headers:[{level:2,title:"Usage",slug:"usage",link:"#usage",children:[]},{level:2,title:"Palette and Style",slug:"palette-and-style",link:"#palette-and-style",children:[]},{level:2,title:"Usage",slug:"usage-1",link:"#usage-1",children:[{level:3,title:"Usage of Palette",slug:"usage-of-palette",link:"#usage-of-palette",children:[]},{level:3,title:"Usage of Style",slug:"usage-of-style",link:"#usage-of-style",children:[]}]},{level:2,title:"Options",slug:"options",link:"#options",children:[{level:3,title:"preset",slug:"preset",link:"#preset",children:[]},{level:3,title:"userPaletteFile",slug:"userpalettefile",link:"#userpalettefile",children:[]},{level:3,title:"tempPaletteFile",slug:"temppalettefile",link:"#temppalettefile",children:[]},{level:3,title:"userStyleFile",slug:"userstylefile",link:"#userstylefile",children:[]},{level:3,title:"tempStyleFile",slug:"tempstylefile",link:"#tempstylefile",children:[]},{level:3,title:"importCode",slug:"importcode",link:"#importcode",children:[]}]}],path:"/plugins/palette.html",pathLocale:"/",extraFields:[]},{title:"photo-swipe",headers:[{level:2,title:"Usage",slug:"usage",link:"#usage",children:[]},{level:2,title:"Options",slug:"options",link:"#options",children:[{level:3,title:"selector",slug:"selector",link:"#selector",children:[]},{level:3,title:"scrollToClose",slug:"scrolltoclose",link:"#scrolltoclose",children:[]},{level:3,title:"delay",slug:"delay",link:"#delay",children:[]},{level:3,title:"locales",slug:"locales",link:"#locales",children:[]}]},{level:2,title:"Frontmatter",slug:"frontmatter",link:"#frontmatter",children:[]},{level:2,title:"Client Config",slug:"client-config",link:"#client-config",children:[{level:3,title:"definePhotoSwipeConfig",slug:"definephotoswipeconfig",link:"#definephotoswipeconfig",children:[]}]},{level:2,title:"API",slug:"api",link:"#api",children:[]},{level:2,title:"Styles",slug:"styles",link:"#styles",children:[]}],path:"/plugins/photo-swipe.html",pathLocale:"/",extraFields:[]},{title:"prismjs",headers:[{level:2,title:"Usage",slug:"usage",link:"#usage",children:[]},{level:2,title:"Options",slug:"options",link:"#options",children:[{level:3,title:"preloadLanguages",slug:"preloadlanguages",link:"#preloadlanguages",children:[]}]}],path:"/plugins/prismjs.html",pathLocale:"/",extraFields:[]},{title:"reading-time",headers:[{level:2,title:"Usage",slug:"usage",link:"#usage",children:[{level:3,title:"Getting data on Node Side",slug:"getting-data-on-node-side",link:"#getting-data-on-node-side",children:[]},{level:3,title:"Getting data on Client Side",slug:"getting-data-on-client-side",link:"#getting-data-on-client-side",children:[]}]},{level:2,title:"Options",slug:"options",link:"#options",children:[{level:3,title:"wordPerMinute",slug:"wordperminute",link:"#wordperminute",children:[]},{level:3,title:"locales",slug:"locales",link:"#locales",children:[]}]},{level:2,title:"Client API",slug:"client-api",link:"#client-api",children:[{level:3,title:"useReadingTimeData",slug:"usereadingtimedata",link:"#usereadingtimedata",children:[]},{level:3,title:"useReadingTimeLocale",slug:"usereadingtimelocale",link:"#usereadingtimelocale",children:[]}]},{level:2,title:"Advanced Usage",slug:"advanced-usage",link:"#advanced-usage",children:[]}],path:"/plugins/reading-time.html",pathLocale:"/",extraFields:[]},{title:"redirect",headers:[{level:2,title:"Usage",slug:"usage",link:"#usage",children:[{level:3,title:"Control Page Redirection",slug:"control-page-redirection",link:"#control-page-redirection",children:[]},{level:3,title:"Auto Locales",slug:"auto-locales",link:"#auto-locales",children:[]},{level:3,title:"Automatically switch languages",slug:"automatically-switch-languages",link:"#automatically-switch-languages",children:[]},{level:3,title:"Customizing Locale Settings",slug:"customizing-locale-settings",link:"#customizing-locale-settings",children:[]},{level:3,title:"Redirecting Sites",slug:"redirecting-sites",link:"#redirecting-sites",children:[]}]},{level:2,title:"Options",slug:"options",link:"#options",children:[{level:3,title:"config",slug:"config",link:"#config",children:[]},{level:3,title:"autoLocale",slug:"autolocale",link:"#autolocale",children:[]},{level:3,title:"switchLocale",slug:"switchlocale",link:"#switchlocale",children:[]},{level:3,title:"localeConfig",slug:"localeconfig",link:"#localeconfig",children:[]},{level:3,title:"localeFallback",slug:"localefallback",link:"#localefallback",children:[]},{level:3,title:"defaultBehavior",slug:"defaultbehavior",link:"#defaultbehavior",children:[]},{level:3,title:"defaultLocale",slug:"defaultlocale",link:"#defaultlocale",children:[]}]},{level:2,title:"Frontmatter",slug:"frontmatter",link:"#frontmatter",children:[{level:3,title:"redirectFrom",slug:"redirectfrom",link:"#redirectfrom",children:[]},{level:3,title:"redirectTo",slug:"redirectto",link:"#redirectto",children:[]}]},{level:2,title:"Styles",slug:"styles",link:"#styles",children:[]}],path:"/plugins/redirect.html",pathLocale:"/",extraFields:[]},{title:"register-components",headers:[{level:2,title:"Usage",slug:"usage",link:"#usage",children:[]},{level:2,title:"Options",slug:"options",link:"#options",children:[{level:3,title:"components",slug:"components",link:"#components",children:[]},{level:3,title:"componentsDir",slug:"componentsdir",link:"#componentsdir",children:[]},{level:3,title:"componentsPatterns",slug:"componentspatterns",link:"#componentspatterns",children:[]},{level:3,title:"getComponentName",slug:"getcomponentname",link:"#getcomponentname",children:[]}]}],path:"/plugins/register-components.html",pathLocale:"/",extraFields:[]},{title:"remove-pwa",headers:[{level:2,title:"Usage",slug:"usage",link:"#usage",children:[]},{level:2,title:"Options",slug:"options",link:"#options",children:[{level:3,title:"cachePrefix",slug:"cacheprefix",link:"#cacheprefix",children:[]},{level:3,title:"swLocation",slug:"swlocation",link:"#swlocation",children:[]}]}],path:"/plugins/remove-pwa.html",pathLocale:"/",extraFields:[]},{title:"rtl",headers:[{level:2,title:"Usage",slug:"usage",link:"#usage",children:[]},{level:2,title:"Options",slug:"options",link:"#options",children:[{level:3,title:"locales",slug:"locales",link:"#locales",children:[]},{level:3,title:"selector",slug:"selector",link:"#selector",children:[]}]}],path:"/plugins/rtl.html",pathLocale:"/",extraFields:[]},{title:"search",headers:[{level:2,title:"Usage",slug:"usage",link:"#usage",children:[]},{level:2,title:"Local Search Index",slug:"local-search-index",link:"#local-search-index",children:[]},{level:2,title:"Options",slug:"options",link:"#options",children:[{level:3,title:"locales",slug:"locales",link:"#locales",children:[]},{level:3,title:"hotKeys",slug:"hotkeys",link:"#hotkeys",children:[]},{level:3,title:"maxSuggestions",slug:"maxsuggestions",link:"#maxsuggestions",children:[]},{level:3,title:"isSearchable",slug:"issearchable",link:"#issearchable",children:[]},{level:3,title:"getExtraFields",slug:"getextrafields",link:"#getextrafields",children:[]}]},{level:2,title:"Styles",slug:"styles",link:"#styles",children:[]},{level:2,title:"Components",slug:"components",link:"#components",children:[{level:3,title:"SearchBox",slug:"searchbox",link:"#searchbox",children:[]}]}],path:"/plugins/search.html",pathLocale:"/",extraFields:[]},{title:"shiki",headers:[{level:2,title:"Usage",slug:"usage",link:"#usage",children:[]},{level:2,title:"Options",slug:"options",link:"#options",children:[{level:3,title:"langs",slug:"langs",link:"#langs",children:[]},{level:3,title:"theme",slug:"theme",link:"#theme",children:[]},{level:3,title:"themes",slug:"themes",link:"#themes",children:[]}]}],path:"/plugins/shiki.html",pathLocale:"/",extraFields:[]},{title:"theme-data",headers:[{level:2,title:"Usage",slug:"usage",link:"#usage",children:[]},{level:2,title:"Options",slug:"options",link:"#options",children:[{level:3,title:"themeData",slug:"themedata",link:"#themedata",children:[]}]},{level:2,title:"Composition API",slug:"composition-api",link:"#composition-api",children:[{level:3,title:"useThemeData",slug:"usethemedata",link:"#usethemedata",children:[]},{level:3,title:"useThemeLocaleData",slug:"usethemelocaledata",link:"#usethemelocaledata",children:[]}]}],path:"/plugins/theme-data.html",pathLocale:"/",extraFields:[]},{title:"toc",headers:[{level:2,title:"Usage",slug:"usage",link:"#usage",children:[]},{level:2,title:"Differences with Markdown TOC Syntax",slug:"differences-with-markdown-toc-syntax",link:"#differences-with-markdown-toc-syntax",children:[]},{level:2,title:"Options",slug:"options",link:"#options",children:[{level:3,title:"componentName",slug:"componentname",link:"#componentname",children:[]},{level:3,title:"defaultPropsOptions",slug:"defaultpropsoptions",link:"#defaultpropsoptions",children:[]}]},{level:2,title:"Component Props",slug:"component-props",link:"#component-props",children:[{level:3,title:"headers",slug:"headers",link:"#headers",children:[]},{level:3,title:"options",slug:"options-1",link:"#options-1",children:[]}]}],path:"/plugins/toc.html",pathLocale:"/",extraFields:[]},{title:"首页",headers:[],path:"/zh/",pathLocale:"/zh/",extraFields:[]},{title:"theme-default",headers:[{level:2,title:"Usage",slug:"usage",link:"#usage",children:[]}],path:"/themes/default/",pathLocale:"/",extraFields:[]},{title:"Built-in Components",headers:[{level:2,title:"Badge",slug:"badge",link:"#badge",children:[]},{level:2,title:"CodeGroup",slug:"codegroup",link:"#codegroup",children:[]},{level:2,title:"CodeGroupItem",slug:"codegroupitem",link:"#codegroupitem",children:[]}],path:"/themes/default/components.html",pathLocale:"/",extraFields:[]},{title:"Config",headers:[{level:2,title:"Basic Config",slug:"basic-config",link:"#basic-config",children:[{level:3,title:"hostname",slug:"hostname",link:"#hostname",children:[]},{level:3,title:"locales",slug:"locales",link:"#locales",children:[]}]},{level:2,title:"Locale Config",slug:"locale-config",link:"#locale-config",children:[{level:3,title:"colorMode",slug:"colormode",link:"#colormode",children:[]},{level:3,title:"colorModeSwitch",slug:"colormodeswitch",link:"#colormodeswitch",children:[]},{level:3,title:"home",slug:"home",link:"#home",children:[]},{level:3,title:"navbar",slug:"navbar",link:"#navbar",children:[]},{level:3,title:"logo",slug:"logo",link:"#logo",children:[]},{level:3,title:"logoDark",slug:"logodark",link:"#logodark",children:[]},{level:3,title:"logoAlt",slug:"logoalt",link:"#logoalt",children:[]},{level:3,title:"repo",slug:"repo",link:"#repo",children:[]},{level:3,title:"sidebar",slug:"sidebar",link:"#sidebar",children:[]},{level:3,title:"sidebarDepth",slug:"sidebardepth",link:"#sidebardepth",children:[]},{level:3,title:"editLink",slug:"editlink",link:"#editlink",children:[]},{level:3,title:"editLinkPattern",slug:"editlinkpattern",link:"#editlinkpattern",children:[]},{level:3,title:"docsRepo",slug:"docsrepo",link:"#docsrepo",children:[]},{level:3,title:"docsBranch",slug:"docsbranch",link:"#docsbranch",children:[]},{level:3,title:"docsDir",slug:"docsdir",link:"#docsdir",children:[]},{level:3,title:"lastUpdated",slug:"lastupdated",link:"#lastupdated",children:[]},{level:3,title:"contributors",slug:"contributors",link:"#contributors",children:[]}]}],path:"/themes/default/config.html",pathLocale:"/",extraFields:[]},{title:"Extending",headers:[{level:2,title:"Layout Slots",slug:"layout-slots",link:"#layout-slots",children:[]},{level:2,title:"Components Replacement",slug:"components-replacement",link:"#components-replacement",children:[]},{level:2,title:"Developing a Child Theme",slug:"developing-a-child-theme",link:"#developing-a-child-theme",children:[]}],path:"/themes/default/extending.html",pathLocale:"/",extraFields:[]},{title:"Frontmatter",headers:[{level:2,title:"All Pages",slug:"all-pages",link:"#all-pages",children:[{level:3,title:"externalLinkIcon",slug:"externallinkicon",link:"#externallinkicon",children:[]},{level:3,title:"navbar",slug:"navbar",link:"#navbar",children:[]},{level:3,title:"pageClass",slug:"pageclass",link:"#pageclass",children:[]}]},{level:2,title:"Home Page",slug:"home-page",link:"#home-page",children:[{level:3,title:"home",slug:"home",link:"#home",children:[]},{level:3,title:"heroImage",slug:"heroimage",link:"#heroimage",children:[]},{level:3,title:"heroImageDark",slug:"heroimagedark",link:"#heroimagedark",children:[]},{level:3,title:"heroAlt",slug:"heroalt",link:"#heroalt",children:[]},{level:3,title:"heroHeight",slug:"heroheight",link:"#heroheight",children:[]},{level:3,title:"heroText",slug:"herotext",link:"#herotext",children:[]},{level:3,title:"tagline",slug:"tagline",link:"#tagline",children:[]},{level:3,title:"actions",slug:"actions",link:"#actions",children:[]},{level:3,title:"features",slug:"features",link:"#features",children:[]},{level:3,title:"footer",slug:"footer",link:"#footer",children:[]},{level:3,title:"footerHtml",slug:"footerhtml",link:"#footerhtml",children:[]}]},{level:2,title:"Normal Page",slug:"normal-page",link:"#normal-page",children:[{level:3,title:"editLink",slug:"editlink",link:"#editlink",children:[]},{level:3,title:"editLinkPattern",slug:"editlinkpattern",link:"#editlinkpattern",children:[]},{level:3,title:"lastUpdated",slug:"lastupdated",link:"#lastupdated",children:[]},{level:3,title:"contributors",slug:"contributors",link:"#contributors",children:[]},{level:3,title:"sidebar",slug:"sidebar",link:"#sidebar",children:[]},{level:3,title:"sidebarDepth",slug:"sidebardepth",link:"#sidebardepth",children:[]},{level:3,title:"prev",slug:"prev",link:"#prev",children:[]},{level:3,title:"next",slug:"next",link:"#next",children:[]}]}],path:"/themes/default/frontmatter.html",pathLocale:"/",extraFields:[]},{title:"Locale Config",headers:[{level:2,title:"repoLabel",slug:"repolabel",link:"#repolabel",children:[]},{level:2,title:"selectLanguageText",slug:"selectlanguagetext",link:"#selectlanguagetext",children:[]},{level:2,title:"selectLanguageAriaLabel",slug:"selectlanguagearialabel",link:"#selectlanguagearialabel",children:[]},{level:2,title:"selectLanguageName",slug:"selectlanguagename",link:"#selectlanguagename",children:[]},{level:2,title:"navbarLabel",slug:"navbarlabel",link:"#navbarlabel",children:[]},{level:2,title:"pageNavbarLabel",slug:"pagenavbarlabel",link:"#pagenavbarlabel",children:[]},{level:2,title:"editLinkText",slug:"editlinktext",link:"#editlinktext",children:[]},{level:2,title:"lastUpdatedText",slug:"lastupdatedtext",link:"#lastupdatedtext",children:[]},{level:2,title:"contributorsText",slug:"contributorstext",link:"#contributorstext",children:[]},{level:2,title:"tip",slug:"tip",link:"#tip",children:[]},{level:2,title:"warning",slug:"warning",link:"#warning",children:[]},{level:2,title:"danger",slug:"danger",link:"#danger",children:[]},{level:2,title:"notFound",slug:"notfound",link:"#notfound",children:[]},{level:2,title:"backToHome",slug:"backtohome",link:"#backtohome",children:[]},{level:2,title:"openInNewWindow",slug:"openinnewwindow",link:"#openinnewwindow",children:[]},{level:2,title:"toggleColorMode",slug:"togglecolormode",link:"#togglecolormode",children:[]},{level:2,title:"toggleSidebar",slug:"togglesidebar",link:"#togglesidebar",children:[]}],path:"/themes/default/locale.html",pathLocale:"/",extraFields:[]},{title:"Markdown",headers:[{level:2,title:"Custom Containers",slug:"custom-containers",link:"#custom-containers",children:[]}],path:"/themes/default/markdown.html",pathLocale:"/",extraFields:[]},{title:"Plugins Config",headers:[{level:2,title:"themePlugins.activeHeaderLinks",slug:"themeplugins-activeheaderlinks",link:"#themeplugins-activeheaderlinks",children:[]},{level:2,title:"themePlugins.backToTop",slug:"themeplugins-backtotop",link:"#themeplugins-backtotop",children:[]},{level:2,title:"themePlugins.container",slug:"themeplugins-container",link:"#themeplugins-container",children:[]},{level:2,title:"themePlugins.externalLinkIcon",slug:"themeplugins-externallinkicon",link:"#themeplugins-externallinkicon",children:[]},{level:2,title:"themePlugins.copyCode",slug:"themeplugins-copycode",link:"#themeplugins-copycode",children:[]},{level:2,title:"themePlugins.git",slug:"themeplugins-git",link:"#themeplugins-git",children:[]},{level:2,title:"themePlugins.mediumZoom",slug:"themeplugins-mediumzoom",link:"#themeplugins-mediumzoom",children:[]},{level:2,title:"themePlugins.nprogress",slug:"themeplugins-nprogress",link:"#themeplugins-nprogress",children:[]},{level:2,title:"themePlugins.seo",slug:"themeplugins-seo",link:"#themeplugins-seo",children:[]},{level:2,title:"themePlugins.sitemap",slug:"themeplugins-sitemap",link:"#themeplugins-sitemap",children:[]}],path:"/themes/default/plugin.html",pathLocale:"/",extraFields:[]},{title:"Styles",headers:[{level:2,title:"Palette File",slug:"palette-file",link:"#palette-file",children:[]},{level:2,title:"Style File",slug:"style-file",link:"#style-file",children:[]}],path:"/themes/default/styles.html",pathLocale:"/",extraFields:[]},{title:"blog",headers:[{level:2,title:"Usage",slug:"usage",link:"#usage",children:[]}],path:"/plugins/blog/",pathLocale:"/",extraFields:[]},{title:"Config",headers:[{level:2,title:"Plugin Options",slug:"plugin-options",link:"#plugin-options",children:[{level:3,title:"getInfo",slug:"getinfo",link:"#getinfo",children:[]},{level:3,title:"filter",slug:"filter",link:"#filter",children:[]},{level:3,title:"category",slug:"category",link:"#category",children:[]},{level:3,title:"type",slug:"type",link:"#type",children:[]},{level:3,title:"slugify",slug:"slugify",link:"#slugify",children:[]},{level:3,title:"excerpt",slug:"excerpt",link:"#excerpt",children:[]},{level:3,title:"excerptSeparator",slug:"excerptseparator",link:"#excerptseparator",children:[]},{level:3,title:"excerptLength",slug:"excerptlength",link:"#excerptlength",children:[]},{level:3,title:"excerptFilter",slug:"excerptfilter",link:"#excerptfilter",children:[]},{level:3,title:"isCustomElement",slug:"iscustomelement",link:"#iscustomelement",children:[]},{level:3,title:"metaScope",slug:"metascope",link:"#metascope",children:[]},{level:3,title:"hotReload",slug:"hotreload",link:"#hotreload",children:[]}]},{level:2,title:"Blog Category Config",slug:"blog-category-config",link:"#blog-category-config",children:[]},{level:2,title:"Blog Type Config",slug:"blog-type-config",link:"#blog-type-config",children:[]},{level:2,title:"Composition API",slug:"composition-api",link:"#composition-api",children:[]}],path:"/plugins/blog/config.html",pathLocale:"/",extraFields:[]},{title:"Guide",headers:[{level:2,title:"Collecting Articles",slug:"collecting-articles",link:"#collecting-articles",children:[]},{level:2,title:"Gathering Info",slug:"gathering-info",link:"#gathering-info",children:[]},{level:2,title:"Customizing Categories and Types",slug:"customizing-categories-and-types",link:"#customizing-categories-and-types",children:[]},{level:2,title:"Using Composition API in Client-side",slug:"using-composition-api-in-client-side",link:"#using-composition-api-in-client-side",children:[]},{level:2,title:"I18n Support",slug:"i18n-support",link:"#i18n-support",children:[]},{level:2,title:"Generating Excerpt",slug:"generating-excerpt",link:"#generating-excerpt",children:[]}],path:"/plugins/blog/guide.html",pathLocale:"/",extraFields:[]},{title:"feed",headers:[{level:2,title:"Usage",slug:"usage",link:"#usage",children:[]}],path:"/plugins/feed/",pathLocale:"/",extraFields:[]},{title:"Channel Config",headers:[{level:2,title:"channel.title",slug:"channel-title",link:"#channel-title",children:[]},{level:2,title:"channel.link",slug:"channel-link",link:"#channel-link",children:[]},{level:2,title:"channel.description",slug:"channel-description",link:"#channel-description",children:[]},{level:2,title:"channel.language",slug:"channel-language",link:"#channel-language",children:[]},{level:2,title:"channel.copyright",slug:"channel-copyright",link:"#channel-copyright",children:[]},{level:2,title:"channel.pubDate",slug:"channel-pubdate",link:"#channel-pubdate",children:[]},{level:2,title:"channel.lastUpdated",slug:"channel-lastupdated",link:"#channel-lastupdated",children:[]},{level:2,title:"channel.ttl",slug:"channel-ttl",link:"#channel-ttl",children:[]},{level:2,title:"channel.image",slug:"channel-image",link:"#channel-image",children:[]},{level:2,title:"channel.icon",slug:"channel-icon",link:"#channel-icon",children:[]},{level:2,title:"channel.author",slug:"channel-author",link:"#channel-author",children:[]}],path:"/plugins/feed/channel.html",pathLocale:"/",extraFields:[]},{title:"Plugin Config",headers:[{level:2,title:"hostname",slug:"hostname",link:"#hostname",children:[]},{level:2,title:"atom",slug:"atom",link:"#atom",children:[]},{level:2,title:"json",slug:"json",link:"#json",children:[]},{level:2,title:"rss",slug:"rss",link:"#rss",children:[]},{level:2,title:"image",slug:"image",link:"#image",children:[]},{level:2,title:"icon",slug:"icon",link:"#icon",children:[]},{level:2,title:"count",slug:"count",link:"#count",children:[]},{level:2,title:"preservedElements",slug:"preservedelements",link:"#preservedelements",children:[]},{level:2,title:"filter",slug:"filter",link:"#filter",children:[]},{level:2,title:"sorter",slug:"sorter",link:"#sorter",children:[]},{level:2,title:"channel",slug:"channel",link:"#channel",children:[]},{level:2,title:"devServer",slug:"devserver",link:"#devserver",children:[]},{level:2,title:"devHostname",slug:"devhostname",link:"#devhostname",children:[]},{level:2,title:"atomOutputFilename",slug:"atomoutputfilename",link:"#atomoutputfilename",children:[]},{level:2,title:"atomXslTemplate",slug:"atomxsltemplate",link:"#atomxsltemplate",children:[]},{level:2,title:"atomXslFilename",slug:"atomxslfilename",link:"#atomxslfilename",children:[]},{level:2,title:"jsonOutputFilename",slug:"jsonoutputfilename",link:"#jsonoutputfilename",children:[]},{level:2,title:"rssOutputFilename",slug:"rssoutputfilename",link:"#rssoutputfilename",children:[]},{level:2,title:"rssXslTemplate",slug:"rssxsltemplate",link:"#rssxsltemplate",children:[]},{level:2,title:"rssXslFilename",slug:"rssxslfilename",link:"#rssxslfilename",children:[]},{level:2,title:"getter",slug:"getter",link:"#getter",children:[]},{level:2,title:"locales",slug:"locales",link:"#locales",children:[]}],path:"/plugins/feed/config.html",pathLocale:"/",extraFields:[]},{title:"Frontmatter Config",headers:[{level:2,title:"Additions and Removals",slug:"additions-and-removals",link:"#additions-and-removals",children:[]},{level:2,title:"Frontmatter Information",slug:"frontmatter-information",link:"#frontmatter-information",children:[{level:3,title:"title",slug:"title",link:"#title",children:[]},{level:3,title:"description",slug:"description",link:"#description",children:[]},{level:3,title:"date",slug:"date",link:"#date",children:[]},{level:3,title:"article",slug:"article",link:"#article",children:[]},{level:3,title:"copyright",slug:"copyright",link:"#copyright",children:[]},{level:3,title:"cover / image / banner",slug:"cover-image-banner",link:"#cover-image-banner",children:[]}]},{level:2,title:"Frontmatter Options",slug:"frontmatter-options",link:"#frontmatter-options",children:[{level:3,title:"feed.title",slug:"feed-title",link:"#feed-title",children:[]},{level:3,title:"feed.description",slug:"feed-description",link:"#feed-description",children:[]},{level:3,title:"feed.content",slug:"feed-content",link:"#feed-content",children:[]},{level:3,title:"feed.author",slug:"feed-author",link:"#feed-author",children:[]},{level:3,title:"feed.contributor",slug:"feed-contributor",link:"#feed-contributor",children:[]},{level:3,title:"feed.guid",slug:"feed-guid",link:"#feed-guid",children:[]}]}],path:"/plugins/feed/frontmatter.html",pathLocale:"/",extraFields:[]},{title:"Feed Getter",headers:[{level:2,title:"getter.title",slug:"getter-title",link:"#getter-title",children:[]},{level:2,title:"getter.link",slug:"getter-link",link:"#getter-link",children:[]},{level:2,title:"getter.description",slug:"getter-description",link:"#getter-description",children:[]},{level:2,title:"getter.content",slug:"getter-content",link:"#getter-content",children:[]},{level:2,title:"getter.author",slug:"getter-author",link:"#getter-author",children:[]},{level:2,title:"getter.category",slug:"getter-category",link:"#getter-category",children:[]},{level:2,title:"getter.enclosure",slug:"getter-enclosure",link:"#getter-enclosure",children:[]},{level:2,title:"getter.publishDate",slug:"getter-publishdate",link:"#getter-publishdate",children:[]},{level:2,title:"getter.lastUpdateDate",slug:"getter-lastupdatedate",link:"#getter-lastupdatedate",children:[]},{level:2,title:"getter.image",slug:"getter-image",link:"#getter-image",children:[]},{level:2,title:"getter.contributor",slug:"getter-contributor",link:"#getter-contributor",children:[]},{level:2,title:"getter.copyright",slug:"getter-copyright",link:"#getter-copyright",children:[]}],path:"/plugins/feed/getter.html",pathLocale:"/",extraFields:[]},{title:"Guide",headers:[{level:2,title:"Usage",slug:"usage",link:"#usage",children:[]},{level:2,title:"Readable Preview",slug:"readable-preview",link:"#readable-preview",children:[]},{level:2,title:"Channel settings",slug:"channel-settings",link:"#channel-settings",children:[]},{level:2,title:"Feed Generation",slug:"feed-generation",link:"#feed-generation",children:[{level:3,title:"I18n Config",slug:"i18n-config",link:"#i18n-config",children:[]}]}],path:"/plugins/feed/guide.html",pathLocale:"/",extraFields:[]},{title:"pwa",headers:[{level:2,title:"Usage",slug:"usage",link:"#usage",children:[]}],path:"/plugins/pwa/",pathLocale:"/",extraFields:[]},{title:"Config",headers:[{level:2,title:"Options",slug:"options",link:"#options",children:[{level:3,title:"showInstall",slug:"showinstall",link:"#showinstall",children:[]},{level:3,title:"manifest",slug:"manifest",link:"#manifest",children:[]},{level:3,title:"favicon",slug:"favicon",link:"#favicon",children:[]},{level:3,title:"themeColor",slug:"themecolor",link:"#themecolor",children:[]},{level:3,title:"cacheHTML",slug:"cachehtml",link:"#cachehtml",children:[]},{level:3,title:"cacheImage",slug:"cacheimage",link:"#cacheimage",children:[]},{level:3,title:"maxSize",slug:"maxsize",link:"#maxsize",children:[]},{level:3,title:"maxImageSize",slug:"maximagesize",link:"#maximagesize",children:[]},{level:3,title:"update",slug:"update",link:"#update",children:[]},{level:3,title:"apple",slug:"apple",link:"#apple",children:[]},{level:3,title:"msTile",slug:"mstile",link:"#mstile",children:[]},{level:3,title:"foundComponent",slug:"foundcomponent",link:"#foundcomponent",children:[]},{level:3,title:"readyComponent",slug:"readycomponent",link:"#readycomponent",children:[]},{level:3,title:"appendBase",slug:"appendbase",link:"#appendbase",children:[]},{level:3,title:"generateSwConfig",slug:"generateswconfig",link:"#generateswconfig",children:[]},{level:3,title:"locales",slug:"locales",link:"#locales",children:[]}]},{level:2,title:"Composition API",slug:"composition-api",link:"#composition-api",children:[{level:3,title:"usePwaEvent",slug:"usepwaevent",link:"#usepwaevent",children:[]}]},{level:2,title:"Utilities",slug:"utilities",link:"#utilities",children:[{level:3,title:"forceUpdate",slug:"forceupdate",link:"#forceupdate",children:[]},{level:3,title:"registerSW",slug:"registersw",link:"#registersw",children:[]},{level:3,title:"skipWaiting",slug:"skipwaiting",link:"#skipwaiting",children:[]},{level:3,title:"unregisterSW",slug:"unregistersw",link:"#unregistersw",children:[]}]},{level:2,title:"Styles",slug:"styles",link:"#styles",children:[]}],path:"/plugins/pwa/config.html",pathLocale:"/",extraFields:[]},{title:"Guide",headers:[{level:2,title:"Intro",slug:"intro",link:"#intro",children:[]},{level:2,title:"Web App Manifests",slug:"web-app-manifests",link:"#web-app-manifests",children:[]},{level:2,title:"Cache Control",slug:"cache-control",link:"#cache-control",children:[{level:3,title:"Default cache",slug:"default-cache",link:"#default-cache",children:[]},{level:3,title:"Image Cache",slug:"image-cache",link:"#image-cache",children:[]},{level:3,title:"HTML Cache",slug:"html-cache",link:"#html-cache",children:[]},{level:3,title:"Size Control",slug:"size-control",link:"#size-control",children:[]}]},{level:2,title:"Update Control",slug:"update-control",link:"#update-control",children:[{level:3,title:"Popups",slug:"popups",link:"#popups",children:[]}]},{level:2,title:"Other Options",slug:"other-options",link:"#other-options",children:[]},{level:2,title:"Further Reading",slug:"further-reading",link:"#further-reading",children:[]}],path:"/plugins/pwa/guide.html",pathLocale:"/",extraFields:[]},{title:"seo",headers:[{level:2,title:"Usage",slug:"usage",link:"#usage",children:[]}],path:"/plugins/seo/",pathLocale:"/",extraFields:[]},{title:"Config",headers:[{level:2,title:"hostname",slug:"hostname",link:"#hostname",children:[]},{level:2,title:"author",slug:"author",link:"#author",children:[]},{level:2,title:"autoDescription",slug:"autodescription",link:"#autodescription",children:[]},{level:2,title:"canonical",slug:"canonical",link:"#canonical",children:[]},{level:2,title:"fallBackImage",slug:"fallbackimage",link:"#fallbackimage",children:[]},{level:2,title:"restrictions",slug:"restrictions",link:"#restrictions",children:[]},{level:2,title:"twitterID",slug:"twitterid",link:"#twitterid",children:[]},{level:2,title:"isArticle",slug:"isarticle",link:"#isarticle",children:[]},{level:2,title:"ogp",slug:"ogp",link:"#ogp",children:[]},{level:2,title:"jsonLd",slug:"jsonld",link:"#jsonld",children:[]},{level:2,title:"customHead",slug:"customhead",link:"#customhead",children:[]}],path:"/plugins/seo/config.html",pathLocale:"/",extraFields:[]},{title:"Guide",headers:[{level:2,title:"Out of Box",slug:"out-of-box",link:"#out-of-box",children:[{level:3,title:"Default OGP Generation",slug:"default-ogp-generation",link:"#default-ogp-generation",children:[]},{level:3,title:"Default JSON-LD Generation",slug:"default-json-ld-generation",link:"#default-json-ld-generation",children:[]}]},{level:2,title:"Setting Tags Directly",slug:"setting-tags-directly",link:"#setting-tags-directly",children:[]},{level:2,title:"Customize Generation",slug:"customize-generation",link:"#customize-generation",children:[{level:3,title:"Page Type",slug:"page-type",link:"#page-type",children:[]},{level:3,title:"OGP",slug:"ogp",link:"#ogp",children:[]},{level:3,title:"JSON-LD",slug:"json-ld",link:"#json-ld",children:[]}]},{level:2,title:"Canonical Link",slug:"canonical-link",link:"#canonical-link",children:[{level:3,title:"Customize head Tags",slug:"customize-head-tags",link:"#customize-head-tags",children:[]}]},{level:2,title:"SEO Introduction",slug:"seo-introduction",link:"#seo-introduction",children:[]},{level:2,title:"Related Documents",slug:"related-documents",link:"#related-documents",children:[]},{level:2,title:"Related Tools",slug:"related-tools",link:"#related-tools",children:[]}],path:"/plugins/seo/guide.html",pathLocale:"/",extraFields:[]},{title:"sitemap",headers:[{level:2,title:"Usage",slug:"usage",link:"#usage",children:[]}],path:"/plugins/sitemap/",pathLocale:"/",extraFields:[]},{title:"Config",headers:[{level:2,title:"hostname",slug:"hostname",link:"#hostname",children:[]},{level:2,title:"extraUrls",slug:"extraurls",link:"#extraurls",children:[]},{level:2,title:"excludePaths",slug:"excludepaths",link:"#excludepaths",children:[]},{level:2,title:"devServer",slug:"devserver",link:"#devserver",children:[]},{level:2,title:"devHostname",slug:"devhostname",link:"#devhostname",children:[]},{level:2,title:"sitemapFilename",slug:"sitemapfilename",link:"#sitemapfilename",children:[]},{level:2,title:"sitemapXSLFilename",slug:"sitemapxslfilename",link:"#sitemapxslfilename",children:[]},{level:2,title:"sitemapXSLTemplate",slug:"sitemapxsltemplate",link:"#sitemapxsltemplate",children:[]},{level:2,title:"changefreq",slug:"changefreq",link:"#changefreq",children:[]},{level:2,title:"priority",slug:"priority",link:"#priority",children:[]},{level:2,title:"modifyTimeGetter",slug:"modifytimegetter",link:"#modifytimegetter",children:[]}],path:"/plugins/sitemap/config.html",pathLocale:"/",extraFields:[]},{title:"Frontmatter",headers:[{level:2,title:"sitemap",slug:"sitemap",link:"#sitemap",children:[{level:3,title:"sitemap.changefreq",slug:"sitemap-changefreq",link:"#sitemap-changefreq",children:[]},{level:3,title:"sitemap.priority",slug:"sitemap-priority",link:"#sitemap-priority",children:[]}]}],path:"/plugins/sitemap/frontmatter.html",pathLocale:"/",extraFields:[]},{title:"Guide",headers:[{level:2,title:"Control Sitemap Link",slug:"control-sitemap-link",link:"#control-sitemap-link",children:[]},{level:2,title:"Output Location",slug:"output-location",link:"#output-location",children:[]},{level:2,title:"Change Frequency",slug:"change-frequency",link:"#change-frequency",children:[]},{level:2,title:"Priority",slug:"priority",link:"#priority",children:[]},{level:2,title:"Modify Time",slug:"modify-time",link:"#modify-time",children:[]},{level:2,title:"Sitemap Intro",slug:"sitemap-intro",link:"#sitemap-intro",children:[]}],path:"/plugins/sitemap/guide.html",pathLocale:"/",extraFields:[]},{title:"@vuepress/helper",headers:[],path:"/tools/helper/",pathLocale:"/",extraFields:[]},{title:"Client Related",headers:[{level:2,title:"Composables APIs",slug:"composables-apis",link:"#composables-apis",children:[{level:3,title:"hasGlobalComponent",slug:"hasglobalcomponent",link:"#hasglobalcomponent",children:[]},{level:3,title:"useLocaleConfig",slug:"uselocaleconfig",link:"#uselocaleconfig",children:[]}]}],path:"/tools/helper/client.html",pathLocale:"/",extraFields:[]},{title:"Shared Methods",headers:[{level:2,title:"Data Related",slug:"data-related",link:"#data-related",children:[]},{level:2,title:"Type Helper",slug:"type-helper",link:"#type-helper",children:[]},{level:2,title:"String Related",slug:"string-related",link:"#string-related",children:[]},{level:2,title:"对象相关",slug:"对象相关",link:"#对象相关",children:[]},{level:2,title:"Date Related",slug:"date-related",link:"#date-related",children:[]},{level:2,title:"Link Related",slug:"link-related",link:"#link-related",children:[]}],path:"/tools/helper/shared.html",pathLocale:"/",extraFields:[]},{title:"插件",headers:[],path:"/zh/plugins/",pathLocale:"/zh/",extraFields:[]},{title:"active-header-links",headers:[{level:2,title:"使用方法",slug:"使用方法",link:"#使用方法",children:[]},{level:2,title:"配置项",slug:"配置项",link:"#配置项",children:[{level:3,title:"headerLinkSelector",slug:"headerlinkselector",link:"#headerlinkselector",children:[]},{level:3,title:"headerAnchorSelector",slug:"headeranchorselector",link:"#headeranchorselector",children:[]},{level:3,title:"delay",slug:"delay",link:"#delay",children:[]},{level:3,title:"offset",slug:"offset",link:"#offset",children:[]}]}],path:"/zh/plugins/active-header-links.html",pathLocale:"/zh/",extraFields:[]},{title:"back-to-top",headers:[{level:2,title:"使用方法",slug:"使用方法",link:"#使用方法",children:[]},{level:2,title:"选项",slug:"选项",link:"#选项",children:[{level:3,title:"threshold",slug:"threshold",link:"#threshold",children:[]},{level:3,title:"progress",slug:"progress",link:"#progress",children:[]}]},{level:2,title:"样式",slug:"样式",link:"#样式",children:[]}],path:"/zh/plugins/back-to-top.html",pathLocale:"/zh/",extraFields:[]},{title:"baidu-analytics",headers:[{level:2,title:"使用方法",slug:"使用方法",link:"#使用方法",children:[{level:3,title:"上报事件",slug:"上报事件",link:"#上报事件",children:[]}]},{level:2,title:"选项",slug:"选项",link:"#选项",children:[{level:3,title:"id",slug:"id",link:"#id",children:[]}]}],path:"/zh/plugins/baidu-analytics.html",pathLocale:"/zh/",extraFields:[]},{title:"catalog",headers:[{level:2,title:"使用方法",slug:"使用方法",link:"#使用方法",children:[]},{level:2,title:"选项",slug:"选项",link:"#选项",children:[{level:3,title:"level",slug:"level",link:"#level",children:[]},{level:3,title:"index",slug:"index",link:"#index",children:[]},{level:3,title:"frontmatter",slug:"frontmatter",link:"#frontmatter",children:[]},{level:3,title:"exclude",slug:"exclude",link:"#exclude",children:[]},{level:3,title:"component",slug:"component",link:"#component",children:[]},{level:3,title:"locales",slug:"locales",link:"#locales",children:[]}]},{level:2,title:"客户端选项",slug:"客户端选项",link:"#客户端选项",children:[{level:3,title:"defineCatalogInfoGetter",slug:"definecataloginfogetter",link:"#definecataloginfogetter",children:[]}]},{level:2,title:"组件",slug:"组件",link:"#组件",children:[{level:3,title:"Catalog",slug:"catalog-1",link:"#catalog-1",children:[]}]},{level:2,title:"样式",slug:"样式",link:"#样式",children:[]}],path:"/zh/plugins/catalog.html",pathLocale:"/zh/",extraFields:[]},{title:"container",headers:[{level:2,title:"使用方法",slug:"使用方法",link:"#使用方法",children:[]},{level:2,title:"容器语法",slug:"容器语法",link:"#容器语法",children:[]},{level:2,title:"配置项",slug:"配置项",link:"#配置项",children:[{level:3,title:"type",slug:"type",link:"#type",children:[]},{level:3,title:"locales",slug:"locales",link:"#locales",children:[]},{level:3,title:"before",slug:"before",link:"#before",children:[]},{level:3,title:"after",slug:"after",link:"#after",children:[]},{level:3,title:"render",slug:"render",link:"#render",children:[]},{level:3,title:"validate",slug:"validate",link:"#validate",children:[]},{level:3,title:"marker",slug:"marker",link:"#marker",children:[]}]}],path:"/zh/plugins/container.html",pathLocale:"/zh/",extraFields:[]},{title:"copy-code",headers:[{level:2,title:"使用",slug:"使用",link:"#使用",children:[]},{level:2,title:"选项",slug:"选项",link:"#选项",children:[{level:3,title:"selector",slug:"selector",link:"#selector",children:[]},{level:3,title:"showInMobile",slug:"showinmobile",link:"#showinmobile",children:[]},{level:3,title:"duration",slug:"duration",link:"#duration",children:[]},{level:3,title:"delay",slug:"delay",link:"#delay",children:[]},{level:3,title:"locales",slug:"locales",link:"#locales",children:[]}]},{level:2,title:"样式",slug:"样式",link:"#样式",children:[]}],path:"/zh/plugins/copy-code.html",pathLocale:"/zh/",extraFields:[]},{title:"copyright",headers:[{level:2,title:"使用",slug:"使用",link:"#使用",children:[{level:3,title:"启用版权信息",slug:"启用版权信息",link:"#启用版权信息",children:[]},{level:3,title:"禁用复制和选择",slug:"禁用复制和选择",link:"#禁用复制和选择",children:[]}]},{level:2,title:"选项",slug:"选项",link:"#选项",children:[{level:3,title:"author",slug:"author",link:"#author",children:[]},{level:3,title:"license",slug:"license",link:"#license",children:[]},{level:3,title:"authorGetter",slug:"authorgetter",link:"#authorgetter",children:[]},{level:3,title:"licenseGetter",slug:"licensegetter",link:"#licensegetter",children:[]},{level:3,title:"copyrightGetter",slug:"copyrightgetter",link:"#copyrightgetter",children:[]},{level:3,title:"triggerLength",slug:"triggerlength",link:"#triggerlength",children:[]},{level:3,title:"maxLength",slug:"maxlength",link:"#maxlength",children:[]},{level:3,title:"global",slug:"global",link:"#global",children:[]},{level:3,title:"disableCopy",slug:"disablecopy",link:"#disablecopy",children:[]},{level:3,title:"disableSelection",slug:"disableselection",link:"#disableselection",children:[]},{level:3,title:"canonical",slug:"canonical",link:"#canonical",children:[]},{level:3,title:"locales",slug:"locales",link:"#locales",children:[]}]},{level:2,title:"Frontmatter",slug:"frontmatter",link:"#frontmatter",children:[{level:3,title:"copy.triggerLength",slug:"copy-triggerlength",link:"#copy-triggerlength",children:[]},{level:3,title:"copy.maxLength",slug:"copy-maxlength",link:"#copy-maxlength",children:[]},{level:3,title:"copy.disableCopy",slug:"copy-disablecopy",link:"#copy-disablecopy",children:[]},{level:3,title:"copy.disableSelection",slug:"copy-disableselection",link:"#copy-disableselection",children:[]}]}],path:"/zh/plugins/copyright.html",pathLocale:"/zh/",extraFields:[]},{title:"docsearch",headers:[{level:2,title:"使用方法",slug:"使用方法",link:"#使用方法",children:[]},{level:2,title:"获取搜索索引",slug:"获取搜索索引",link:"#获取搜索索引",children:[]},{level:2,title:"配置项",slug:"配置项",link:"#配置项",children:[{level:3,title:"apiKey",slug:"apikey",link:"#apikey",children:[]},{level:3,title:"indexName",slug:"indexname",link:"#indexname",children:[]},{level:3,title:"appId",slug:"appid",link:"#appid",children:[]},{level:3,title:"searchParameters",slug:"searchparameters",link:"#searchparameters",children:[]},{level:3,title:"placeholder",slug:"placeholder",link:"#placeholder",children:[]},{level:3,title:"disableUserPersonalization",slug:"disableuserpersonalization",link:"#disableuserpersonalization",children:[]},{level:3,title:"initialQuery",slug:"initialquery",link:"#initialquery",children:[]},{level:3,title:"translations",slug:"translations",link:"#translations",children:[]},{level:3,title:"locales",slug:"locales",link:"#locales",children:[]},{level:3,title:"indexBase",slug:"indexbase",link:"#indexbase",children:[]},{level:3,title:"injectStyles",slug:"injectstyles",link:"#injectstyles",children:[]}]},{level:2,title:"样式",slug:"样式",link:"#样式",children:[]},{level:2,title:"组件",slug:"组件",link:"#组件",children:[{level:3,title:"Docsearch",slug:"docsearch-1",link:"#docsearch-1",children:[]}]}],path:"/zh/plugins/docsearch.html",pathLocale:"/zh/",extraFields:[]},{title:"external-link-icon",headers:[{level:2,title:"使用方法",slug:"使用方法",link:"#使用方法",children:[]},{level:2,title:"选项",slug:"选项",link:"#选项",children:[{level:3,title:"locales",slug:"locales",link:"#locales",children:[]}]},{level:2,title:"Frontmatter",slug:"frontmatter",link:"#frontmatter",children:[{level:3,title:"externalLinkIcon",slug:"externallinkicon",link:"#externallinkicon",children:[]}]},{level:2,title:"样式",slug:"样式",link:"#样式",children:[]},{level:2,title:"组件",slug:"组件",link:"#组件",children:[{level:3,title:"ExternalLinkIcon",slug:"externallinkicon-1",link:"#externallinkicon-1",children:[]}]}],path:"/zh/plugins/external-link-icon.html",pathLocale:"/zh/",extraFields:[]},{title:"git",headers:[{level:2,title:"使用方法",slug:"使用方法",link:"#使用方法",children:[]},{level:2,title:"Git 仓库",slug:"git-仓库",link:"#git-仓库",children:[]},{level:2,title:"配置项",slug:"配置项",link:"#配置项",children:[{level:3,title:"createdTime",slug:"createdtime",link:"#createdtime",children:[]},{level:3,title:"updatedTime",slug:"updatedtime",link:"#updatedtime",children:[]},{level:3,title:"contributors",slug:"contributors",link:"#contributors",children:[]}]},{level:2,title:"Frontmatter",slug:"frontmatter",link:"#frontmatter",children:[{level:3,title:"gitInclude",slug:"gitinclude",link:"#gitinclude",children:[]}]},{level:2,title:"页面数据",slug:"页面数据",link:"#页面数据",children:[{level:3,title:"git.createdTime",slug:"git-createdtime",link:"#git-createdtime",children:[]},{level:3,title:"git.updatedTime",slug:"git-updatedtime",link:"#git-updatedtime",children:[]},{level:3,title:"git.contributors",slug:"git-contributors",link:"#git-contributors",children:[]}]}],path:"/zh/plugins/git.html",pathLocale:"/zh/",extraFields:[]},{title:"google-analytics",headers:[{level:2,title:"使用方法",slug:"使用方法",link:"#使用方法",children:[{level:3,title:"上报事件",slug:"上报事件",link:"#上报事件",children:[]}]},{level:2,title:"选项",slug:"选项",link:"#选项",children:[{level:3,title:"id",slug:"id",link:"#id",children:[]},{level:3,title:"debug",slug:"debug",link:"#debug",children:[]}]}],path:"/zh/plugins/google-analytics.html",pathLocale:"/zh/",extraFields:[]},{title:"medium-zoom",headers:[{level:2,title:"使用方法",slug:"使用方法",link:"#使用方法",children:[]},{level:2,title:"配置项",slug:"配置项",link:"#配置项",children:[{level:3,title:"selector",slug:"selector",link:"#selector",children:[]},{level:3,title:"delay",slug:"delay",link:"#delay",children:[]},{level:3,title:"zoomOptions",slug:"zoomoptions",link:"#zoomoptions",children:[]}]},{level:2,title:"样式",slug:"样式",link:"#样式",children:[]},{level:2,title:"Composition API",slug:"composition-api",link:"#composition-api",children:[{level:3,title:"useMediumZoom",slug:"usemediumzoom",link:"#usemediumzoom",children:[]}]}],path:"/zh/plugins/medium-zoom.html",pathLocale:"/zh/",extraFields:[]},{title:"nprogress",headers:[{level:2,title:"使用方法",slug:"使用方法",link:"#使用方法",children:[]},{level:2,title:"样式",slug:"样式",link:"#样式",children:[]}],path:"/zh/plugins/nprogress.html",pathLocale:"/zh/",extraFields:[]},{title:"palette",headers:[{level:2,title:"使用方法",slug:"使用方法",link:"#使用方法",children:[]},{level:2,title:"调色板和样式",slug:"调色板和样式",link:"#调色板和样式",children:[]},{level:2,title:"使用",slug:"使用",link:"#使用",children:[{level:3,title:"使用调色板",slug:"使用调色板",link:"#使用调色板",children:[]},{level:3,title:"使用样式",slug:"使用样式",link:"#使用样式",children:[]}]},{level:2,title:"配置项",slug:"配置项",link:"#配置项",children:[{level:3,title:"preset",slug:"preset",link:"#preset",children:[]},{level:3,title:"userPaletteFile",slug:"userpalettefile",link:"#userpalettefile",children:[]},{level:3,title:"tempPaletteFile",slug:"temppalettefile",link:"#temppalettefile",children:[]},{level:3,title:"userStyleFile",slug:"userstylefile",link:"#userstylefile",children:[]},{level:3,title:"tempStyleFile",slug:"tempstylefile",link:"#tempstylefile",children:[]},{level:3,title:"importCode",slug:"importcode",link:"#importcode",children:[]}]}],path:"/zh/plugins/palette.html",pathLocale:"/zh/",extraFields:[]},{title:"photo-swipe",headers:[{level:2,title:"使用方法",slug:"使用方法",link:"#使用方法",children:[]},{level:2,title:"选项",slug:"选项",link:"#选项",children:[{level:3,title:"selector",slug:"selector",link:"#selector",children:[]},{level:3,title:"scrollToClose",slug:"scrolltoclose",link:"#scrolltoclose",children:[]},{level:3,title:"delay",slug:"delay",link:"#delay",children:[]},{level:3,title:"locales",slug:"locales",link:"#locales",children:[]}]},{level:2,title:"Frontmatter",slug:"frontmatter",link:"#frontmatter",children:[{level:3,title:"photoswipe",slug:"photoswipe",link:"#photoswipe",children:[]}]},{level:2,title:"客户端配置",slug:"客户端配置",link:"#客户端配置",children:[{level:3,title:"definePhotoSwipeConfig",slug:"definephotoswipeconfig",link:"#definephotoswipeconfig",children:[]}]},{level:2,title:"API",slug:"api",link:"#api",children:[]},{level:2,title:"样式",slug:"样式",link:"#样式",children:[]}],path:"/zh/plugins/photo-swipe.html",pathLocale:"/zh/",extraFields:[]},{title:"prismjs",headers:[{level:2,title:"使用方法",slug:"使用方法",link:"#使用方法",children:[]},{level:2,title:"配置项",slug:"配置项",link:"#配置项",children:[{level:3,title:"preloadLanguages",slug:"preloadlanguages",link:"#preloadlanguages",children:[]}]}],path:"/zh/plugins/prismjs.html",pathLocale:"/zh/",extraFields:[]},{title:"reading-time",headers:[{level:2,title:"使用方法",slug:"使用方法",link:"#使用方法",children:[{level:3,title:"在 Node 侧获取数据",slug:"在-node-侧获取数据",link:"#在-node-侧获取数据",children:[]},{level:3,title:"在客户端侧获取数据",slug:"在客户端侧获取数据",link:"#在客户端侧获取数据",children:[]}]},{level:2,title:"选项",slug:"选项",link:"#选项",children:[{level:3,title:"wordPerMinute",slug:"wordperminute",link:"#wordperminute",children:[]},{level:3,title:"locales",slug:"locales",link:"#locales",children:[]}]},{level:2,title:"客户端 API",slug:"客户端-api",link:"#客户端-api",children:[{level:3,title:"useReadingTimeData",slug:"usereadingtimedata",link:"#usereadingtimedata",children:[]},{level:3,title:"useReadingTimeLocale",slug:"usereadingtimelocale",link:"#usereadingtimelocale",children:[]}]},{level:2,title:"高级使用",slug:"高级使用",link:"#高级使用",children:[]}],path:"/zh/plugins/reading-time.html",pathLocale:"/zh/",extraFields:[]},{title:"redirect",headers:[{level:2,title:"使用方法",slug:"使用方法",link:"#使用方法",children:[{level:3,title:"设置重定向",slug:"设置重定向",link:"#设置重定向",children:[]},{level:3,title:"自动多语言",slug:"自动多语言",link:"#自动多语言",children:[]},{level:3,title:"自动切换语言",slug:"自动切换语言",link:"#自动切换语言",children:[]},{level:3,title:"自定义多语言配置",slug:"自定义多语言配置",link:"#自定义多语言配置",children:[]},{level:3,title:"重定向站点",slug:"重定向站点",link:"#重定向站点",children:[]}]},{level:2,title:"选项",slug:"选项",link:"#选项",children:[{level:3,title:"config",slug:"config",link:"#config",children:[]},{level:3,title:"autoLocale",slug:"autolocale",link:"#autolocale",children:[]},{level:3,title:"switchLocale",slug:"switchlocale",link:"#switchlocale",children:[]},{level:3,title:"localeConfig",slug:"localeconfig",link:"#localeconfig",children:[]},{level:3,title:"localeFallback",slug:"localefallback",link:"#localefallback",children:[]},{level:3,title:"defaultBehavior",slug:"defaultbehavior",link:"#defaultbehavior",children:[]},{level:3,title:"defaultLocale",slug:"defaultlocale",link:"#defaultlocale",children:[]}]},{level:2,title:"Frontmatter",slug:"frontmatter",link:"#frontmatter",children:[{level:3,title:"redirectFrom",slug:"redirectfrom",link:"#redirectfrom",children:[]},{level:3,title:"redirectTo",slug:"redirectto",link:"#redirectto",children:[]}]},{level:2,title:"样式",slug:"样式",link:"#样式",children:[]}],path:"/zh/plugins/redirect.html",pathLocale:"/zh/",extraFields:[]},{title:"register-components",headers:[{level:2,title:"使用方法",slug:"使用方法",link:"#使用方法",children:[]},{level:2,title:"配置项",slug:"配置项",link:"#配置项",children:[{level:3,title:"components",slug:"components",link:"#components",children:[]},{level:3,title:"componentsDir",slug:"componentsdir",link:"#componentsdir",children:[]},{level:3,title:"componentsPatterns",slug:"componentspatterns",link:"#componentspatterns",children:[]},{level:3,title:"getComponentName",slug:"getcomponentname",link:"#getcomponentname",children:[]}]}],path:"/zh/plugins/register-components.html",pathLocale:"/zh/",extraFields:[]},{title:"remove-pwa",headers:[{level:2,title:"使用方法",slug:"使用方法",link:"#使用方法",children:[]},{level:2,title:"选项",slug:"选项",link:"#选项",children:[{level:3,title:"cachePrefix",slug:"cacheprefix",link:"#cacheprefix",children:[]},{level:3,title:"swLocation",slug:"swlocation",link:"#swlocation",children:[]}]}],path:"/zh/plugins/remove-pwa.html",pathLocale:"/zh/",extraFields:[]},{title:"rtl",headers:[{level:2,title:"使用方法",slug:"使用方法",link:"#使用方法",children:[]},{level:2,title:"选项",slug:"选项",link:"#选项",children:[{level:3,title:"locales",slug:"locales",link:"#locales",children:[]},{level:3,title:"selector",slug:"selector",link:"#selector",children:[]}]}],path:"/zh/plugins/rtl.html",pathLocale:"/zh/",extraFields:[]},{title:"search",headers:[{level:2,title:"使用方法",slug:"使用方法",link:"#使用方法",children:[]},{level:2,title:"本地搜索索引",slug:"本地搜索索引",link:"#本地搜索索引",children:[]},{level:2,title:"配置项",slug:"配置项",link:"#配置项",children:[{level:3,title:"locales",slug:"locales",link:"#locales",children:[]},{level:3,title:"hotKeys",slug:"hotkeys",link:"#hotkeys",children:[]},{level:3,title:"maxSuggestions",slug:"maxsuggestions",link:"#maxsuggestions",children:[]},{level:3,title:"isSearchable",slug:"issearchable",link:"#issearchable",children:[]},{level:3,title:"getExtraFields",slug:"getextrafields",link:"#getextrafields",children:[]}]},{level:2,title:"样式",slug:"样式",link:"#样式",children:[]},{level:2,title:"组件",slug:"组件",link:"#组件",children:[{level:3,title:"SearchBox",slug:"searchbox",link:"#searchbox",children:[]}]}],path:"/zh/plugins/search.html",pathLocale:"/zh/",extraFields:[]},{title:"shiki",headers:[{level:2,title:"使用方法",slug:"使用方法",link:"#使用方法",children:[]},{level:2,title:"配置项",slug:"配置项",link:"#配置项",children:[{level:3,title:"langs",slug:"langs",link:"#langs",children:[]},{level:3,title:"theme",slug:"theme",link:"#theme",children:[]},{level:3,title:"themes",slug:"themes",link:"#themes",children:[]}]}],path:"/zh/plugins/shiki.html",pathLocale:"/zh/",extraFields:[]},{title:"theme-data",headers:[{level:2,title:"使用方法",slug:"使用方法",link:"#使用方法",children:[]},{level:2,title:"配置项",slug:"配置项",link:"#配置项",children:[{level:3,title:"themeData",slug:"themedata",link:"#themedata",children:[]}]},{level:2,title:"Composition API",slug:"composition-api",link:"#composition-api",children:[{level:3,title:"useThemeData",slug:"usethemedata",link:"#usethemedata",children:[]},{level:3,title:"useThemeLocaleData",slug:"usethemelocaledata",link:"#usethemelocaledata",children:[]}]}],path:"/zh/plugins/theme-data.html",pathLocale:"/zh/",extraFields:[]},{title:"toc",headers:[{level:2,title:"使用方法",slug:"使用方法",link:"#使用方法",children:[]},{level:2,title:"与 Markdown 目录语法的区别",slug:"与-markdown-目录语法的区别",link:"#与-markdown-目录语法的区别",children:[]},{level:2,title:"配置项",slug:"配置项",link:"#配置项",children:[{level:3,title:"componentName",slug:"componentname",link:"#componentname",children:[]},{level:3,title:"defaultPropsOptions",slug:"defaultpropsoptions",link:"#defaultpropsoptions",children:[]}]},{level:2,title:"组件 Props",slug:"组件-props",link:"#组件-props",children:[{level:3,title:"headers",slug:"headers",link:"#headers",children:[]},{level:3,title:"options",slug:"options",link:"#options",children:[]}]}],path:"/zh/plugins/toc.html",pathLocale:"/zh/",extraFields:[]},{title:"主题",headers:[],path:"/zh/themes/",pathLocale:"/zh/",extraFields:[]},{title:"Bundler Related",headers:[{level:2,title:"Common methods",slug:"common-methods",link:"#common-methods",children:[{level:3,title:"getBundlerName",slug:"getbundlername",link:"#getbundlername",children:[]},{level:3,title:"addCustomElement",slug:"addcustomelement",link:"#addcustomelement",children:[]},{level:3,title:"customizeDevServer",slug:"customizedevserver",link:"#customizedevserver",children:[]}]},{level:2,title:"Vite Related",slug:"vite-related",link:"#vite-related",children:[]},{level:2,title:"Webpack Related",slug:"webpack-related",link:"#webpack-related",children:[]}],path:"/tools/helper/node/bundler.html",pathLocale:"/",extraFields:[]},{title:"Page Related",headers:[{level:2,title:"getPageExcerpt",slug:"getpageexcerpt",link:"#getpageexcerpt",children:[]},{level:2,title:"getPageText",slug:"getpagetext",link:"#getpagetext",children:[]}],path:"/tools/helper/node/page.html",pathLocale:"/",extraFields:[]},{title:"blog",headers:[{level:2,title:"使用",slug:"使用",link:"#使用",children:[]}],path:"/zh/plugins/blog/",pathLocale:"/zh/",extraFields:[]},{title:"配置",headers:[{level:2,title:"插件选项",slug:"插件选项",link:"#插件选项",children:[{level:3,title:"getInfo",slug:"getinfo",link:"#getinfo",children:[]},{level:3,title:"filter",slug:"filter",link:"#filter",children:[]},{level:3,title:"category",slug:"category",link:"#category",children:[]},{level:3,title:"type",slug:"type",link:"#type",children:[]},{level:3,title:"slugify",slug:"slugify",link:"#slugify",children:[]},{level:3,title:"excerpt",slug:"excerpt",link:"#excerpt",children:[]},{level:3,title:"excerptSeparator",slug:"excerptseparator",link:"#excerptseparator",children:[]},{level:3,title:"excerptLength",slug:"excerptlength",link:"#excerptlength",children:[]},{level:3,title:"excerptFilter",slug:"excerptfilter",link:"#excerptfilter",children:[]},{level:3,title:"isCustomElement",slug:"iscustomelement",link:"#iscustomelement",children:[]},{level:3,title:"metaScope",slug:"metascope",link:"#metascope",children:[]},{level:3,title:"hotReload",slug:"hotreload",link:"#hotreload",children:[]}]},{level:2,title:"博客分类配置",slug:"博客分类配置",link:"#博客分类配置",children:[]},{level:2,title:"博客类型配置",slug:"博客类型配置",link:"#博客类型配置",children:[]},{level:2,title:"可组合式 API",slug:"可组合式-api",link:"#可组合式-api",children:[]}],path:"/zh/plugins/blog/config.html",pathLocale:"/zh/",extraFields:[]},{title:"指南",headers:[{level:2,title:"收集文章并生成信息",slug:"收集文章并生成信息",link:"#收集文章并生成信息",children:[]},{level:2,title:"自定义类别和类型",slug:"自定义类别和类型",link:"#自定义类别和类型",children:[]},{level:2,title:"在客户端使用组合 API",slug:"在客户端使用组合-api",link:"#在客户端使用组合-api",children:[]},{level:2,title:"多语言支持",slug:"多语言支持",link:"#多语言支持",children:[]},{level:2,title:"摘要生成",slug:"摘要生成",link:"#摘要生成",children:[]}],path:"/zh/plugins/blog/guide.html",pathLocale:"/zh/",extraFields:[]},{title:"feed",headers:[{level:2,title:"使用",slug:"使用",link:"#使用",children:[]}],path:"/zh/plugins/feed/",pathLocale:"/zh/",extraFields:[]},{title:"频道设置",headers:[{level:2,title:"channel.title",slug:"channel-title",link:"#channel-title",children:[]},{level:2,title:"channel.link",slug:"channel-link",link:"#channel-link",children:[]},{level:2,title:"channel.description",slug:"channel-description",link:"#channel-description",children:[]},{level:2,title:"channel.language",slug:"channel-language",link:"#channel-language",children:[]},{level:2,title:"channel.copyright",slug:"channel-copyright",link:"#channel-copyright",children:[]},{level:2,title:"channel.pubDate",slug:"channel-pubdate",link:"#channel-pubdate",children:[]},{level:2,title:"channel.lastUpdated",slug:"channel-lastupdated",link:"#channel-lastupdated",children:[]},{level:2,title:"channel.ttl",slug:"channel-ttl",link:"#channel-ttl",children:[]},{level:2,title:"channel.image",slug:"channel-image",link:"#channel-image",children:[]},{level:2,title:"channel.icon",slug:"channel-icon",link:"#channel-icon",children:[]},{level:2,title:"channel.author",slug:"channel-author",link:"#channel-author",children:[]},{level:2,title:"channel.hub",slug:"channel-hub",link:"#channel-hub",children:[]}],path:"/zh/plugins/feed/channel.html",pathLocale:"/zh/",extraFields:[]},{title:"插件配置",headers:[{level:2,title:"hostname",slug:"hostname",link:"#hostname",children:[]},{level:2,title:"atom",slug:"atom",link:"#atom",children:[]},{level:2,title:"json",slug:"json",link:"#json",children:[]},{level:2,title:"rss",slug:"rss",link:"#rss",children:[]},{level:2,title:"image",slug:"image",link:"#image",children:[]},{level:2,title:"icon",slug:"icon",link:"#icon",children:[]},{level:2,title:"count",slug:"count",link:"#count",children:[]},{level:2,title:"preservedElements",slug:"preservedelements",link:"#preservedelements",children:[]},{level:2,title:"filter",slug:"filter",link:"#filter",children:[]},{level:2,title:"sorter",slug:"sorter",link:"#sorter",children:[]},{level:2,title:"channel",slug:"channel",link:"#channel",children:[]},{level:2,title:"devServer",slug:"devserver",link:"#devserver",children:[]},{level:2,title:"devHostname",slug:"devhostname",link:"#devhostname",children:[]},{level:2,title:"atomOutputFilename",slug:"atomoutputfilename",link:"#atomoutputfilename",children:[]},{level:2,title:"atomXslTemplate",slug:"atomxsltemplate",link:"#atomxsltemplate",children:[]},{level:2,title:"atomXslFilename",slug:"atomxslfilename",link:"#atomxslfilename",children:[]},{level:2,title:"jsonOutputFilename",slug:"jsonoutputfilename",link:"#jsonoutputfilename",children:[]},{level:2,title:"rssOutputFilename",slug:"rssoutputfilename",link:"#rssoutputfilename",children:[]},{level:2,title:"rssXslTemplate",slug:"rssxsltemplate",link:"#rssxsltemplate",children:[]},{level:2,title:"rssXslFilename",slug:"rssxslfilename",link:"#rssxslfilename",children:[]},{level:2,title:"getter",slug:"getter",link:"#getter",children:[]},{level:2,title:"locales",slug:"locales",link:"#locales",children:[]}],path:"/zh/plugins/feed/config.html",pathLocale:"/zh/",extraFields:[]},{title:"Frontmatter 配置",headers:[{level:2,title:"添加与移除",slug:"添加与移除",link:"#添加与移除",children:[]},{level:2,title:"读取的 Frontmatter 信息",slug:"读取的-frontmatter-信息",link:"#读取的-frontmatter-信息",children:[{level:3,title:"title",slug:"title",link:"#title",children:[]},{level:3,title:"description",slug:"description",link:"#description",children:[]},{level:3,title:"date",slug:"date",link:"#date",children:[]},{level:3,title:"article",slug:"article",link:"#article",children:[]},{level:3,title:"copyright",slug:"copyright",link:"#copyright",children:[]},{level:3,title:"cover / image / banner",slug:"cover-image-banner",link:"#cover-image-banner",children:[]}]},{level:2,title:"Frontmatter 选项",slug:"frontmatter-选项",link:"#frontmatter-选项",children:[{level:3,title:"feed.title",slug:"feed-title",link:"#feed-title",children:[]},{level:3,title:"feed.description",slug:"feed-description",link:"#feed-description",children:[]},{level:3,title:"feed.content",slug:"feed-content",link:"#feed-content",children:[]},{level:3,title:"feed.author",slug:"feed-author",link:"#feed-author",children:[]},{level:3,title:"feed.contributor",slug:"feed-contributor",link:"#feed-contributor",children:[]},{level:3,title:"feed.guid",slug:"feed-guid",link:"#feed-guid",children:[]}]}],path:"/zh/plugins/feed/frontmatter.html",pathLocale:"/zh/",extraFields:[]},{title:"Feed 获取器",headers:[{level:2,title:"getter.title",slug:"getter-title",link:"#getter-title",children:[]},{level:2,title:"getter.link",slug:"getter-link",link:"#getter-link",children:[]},{level:2,title:"getter.description",slug:"getter-description",link:"#getter-description",children:[]},{level:2,title:"getter.content",slug:"getter-content",link:"#getter-content",children:[]},{level:2,title:"getter.author",slug:"getter-author",link:"#getter-author",children:[]},{level:2,title:"getter.category",slug:"getter-category",link:"#getter-category",children:[]},{level:2,title:"getter.enclosure",slug:"getter-enclosure",link:"#getter-enclosure",children:[]},{level:2,title:"getter.publishDate",slug:"getter-publishdate",link:"#getter-publishdate",children:[]},{level:2,title:"getter.lastUpdateDate",slug:"getter-lastupdatedate",link:"#getter-lastupdatedate",children:[]},{level:2,title:"getter.image",slug:"getter-image",link:"#getter-image",children:[]},{level:2,title:"getter.contributor",slug:"getter-contributor",link:"#getter-contributor",children:[]},{level:2,title:"getter.copyright",slug:"getter-copyright",link:"#getter-copyright",children:[]}],path:"/zh/plugins/feed/getter.html",pathLocale:"/zh/",extraFields:[]},{title:"指南",headers:[{level:2,title:"使用",slug:"使用",link:"#使用",children:[]},{level:2,title:"可读的预览",slug:"可读的预览",link:"#可读的预览",children:[]},{level:2,title:"频道设置",slug:"频道设置",link:"#频道设置",children:[]},{level:2,title:"Feed 生成",slug:"feed-生成",link:"#feed-生成",children:[{level:3,title:"多语言配置",slug:"多语言配置",link:"#多语言配置",children:[]}]}],path:"/zh/plugins/feed/guide.html",pathLocale:"/zh/",extraFields:[]},{title:"pwa",headers:[{level:2,title:"使用",slug:"使用",link:"#使用",children:[]}],path:"/zh/plugins/pwa/",pathLocale:"/zh/",extraFields:[]},{title:"配置",headers:[{level:2,title:"选项",slug:"选项",link:"#选项",children:[{level:3,title:"showInstall",slug:"showinstall",link:"#showinstall",children:[]},{level:3,title:"manifest",slug:"manifest",link:"#manifest",children:[]},{level:3,title:"favicon",slug:"favicon",link:"#favicon",children:[]},{level:3,title:"themeColor",slug:"themecolor",link:"#themecolor",children:[]},{level:3,title:"cacheHTML",slug:"cachehtml",link:"#cachehtml",children:[]},{level:3,title:"cacheImage",slug:"cacheimage",link:"#cacheimage",children:[]},{level:3,title:"maxSize",slug:"maxsize",link:"#maxsize",children:[]},{level:3,title:"maxImageSize",slug:"maximagesize",link:"#maximagesize",children:[]},{level:3,title:"update",slug:"update",link:"#update",children:[]},{level:3,title:"apple",slug:"apple",link:"#apple",children:[]},{level:3,title:"msTile",slug:"mstile",link:"#mstile",children:[]},{level:3,title:"foundComponent",slug:"foundcomponent",link:"#foundcomponent",children:[]},{level:3,title:"readyComponent",slug:"readycomponent",link:"#readycomponent",children:[]},{level:3,title:"appendBase",slug:"appendbase",link:"#appendbase",children:[]},{level:3,title:"generateSwConfig",slug:"generateswconfig",link:"#generateswconfig",children:[]},{level:3,title:"locales",slug:"locales",link:"#locales",children:[]}]},{level:2,title:"组合式 API",slug:"组合式-api",link:"#组合式-api",children:[{level:3,title:"usePwaEvent",slug:"usepwaevent",link:"#usepwaevent",children:[]}]},{level:2,title:"工具函数",slug:"工具函数",link:"#工具函数",children:[{level:3,title:"forceUpdate",slug:"forceupdate",link:"#forceupdate",children:[]},{level:3,title:"registerSW",slug:"registersw",link:"#registersw",children:[]},{level:3,title:"skipWaiting",slug:"skipwaiting",link:"#skipwaiting",children:[]},{level:3,title:"unregisterSW",slug:"unregistersw",link:"#unregistersw",children:[]}]},{level:2,title:"样式",slug:"样式",link:"#样式",children:[]}],path:"/zh/plugins/pwa/config.html",pathLocale:"/zh/",extraFields:[]},{title:"指南",headers:[{level:2,title:"介绍",slug:"介绍",link:"#介绍",children:[]},{level:2,title:"网络 App 清单",slug:"网络-app-清单",link:"#网络-app-清单",children:[]},{level:2,title:"缓存控制",slug:"缓存控制",link:"#缓存控制",children:[{level:3,title:"默认缓存",slug:"默认缓存",link:"#默认缓存",children:[]},{level:3,title:"图片缓存",slug:"图片缓存",link:"#图片缓存",children:[]},{level:3,title:"HTML 缓存",slug:"html-缓存",link:"#html-缓存",children:[]},{level:3,title:"大小控制",slug:"大小控制",link:"#大小控制",children:[]}]},{level:2,title:"更新控制",slug:"更新控制",link:"#更新控制",children:[{level:3,title:"更新提示弹窗",slug:"更新提示弹窗",link:"#更新提示弹窗",children:[]}]},{level:2,title:"其他选项",slug:"其他选项",link:"#其他选项",children:[]},{level:2,title:"相关阅读",slug:"相关阅读",link:"#相关阅读",children:[]}],path:"/zh/plugins/pwa/guide.html",pathLocale:"/zh/",extraFields:[]},{title:"seo",headers:[{level:2,title:"使用",slug:"使用",link:"#使用",children:[]}],path:"/zh/plugins/seo/",pathLocale:"/zh/",extraFields:[]},{title:"选项",headers:[{level:2,title:"hostname",slug:"hostname",link:"#hostname",children:[]},{level:2,title:"author",slug:"author",link:"#author",children:[]},{level:2,title:"autoDescription",slug:"autodescription",link:"#autodescription",children:[]},{level:2,title:"canonical",slug:"canonical",link:"#canonical",children:[]},{level:2,title:"fallBackImage",slug:"fallbackimage",link:"#fallbackimage",children:[]},{level:2,title:"restrictions",slug:"restrictions",link:"#restrictions",children:[]},{level:2,title:"twitterID",slug:"twitterid",link:"#twitterid",children:[]},{level:2,title:"isArticle",slug:"isarticle",link:"#isarticle",children:[]},{level:2,title:"ogp",slug:"ogp",link:"#ogp",children:[]},{level:2,title:"jsonLd",slug:"jsonld",link:"#jsonld",children:[]},{level:2,title:"customHead",slug:"customhead",link:"#customhead",children:[]}],path:"/zh/plugins/seo/config.html",pathLocale:"/zh/",extraFields:[]},{title:"指南",headers:[{level:2,title:"开箱即用",slug:"开箱即用",link:"#开箱即用",children:[{level:3,title:"默认的 OGP 生成逻辑",slug:"默认的-ogp-生成逻辑",link:"#默认的-ogp-生成逻辑",children:[]},{level:3,title:"默认的 JSON-LD 生成逻辑",slug:"默认的-json-ld-生成逻辑",link:"#默认的-json-ld-生成逻辑",children:[]}]},{level:2,title:"直接添加 head 标签",slug:"直接添加-head-标签",link:"#直接添加-head-标签",children:[]},{level:2,title:"自定义生成过程",slug:"自定义生成过程",link:"#自定义生成过程",children:[{level:3,title:"页面类型",slug:"页面类型",link:"#页面类型",children:[]},{level:3,title:"OGP",slug:"ogp",link:"#ogp",children:[]},{level:3,title:"JSON-LD",slug:"json-ld",link:"#json-ld",children:[]}]},{level:2,title:"规范链接",slug:"规范链接",link:"#规范链接",children:[{level:3,title:"自定义 head 标签",slug:"自定义-head-标签",link:"#自定义-head-标签",children:[]}]},{level:2,title:"SEO 介绍",slug:"seo-介绍",link:"#seo-介绍",children:[]},{level:2,title:"相关文档",slug:"相关文档",link:"#相关文档",children:[]},{level:2,title:"相关工具",slug:"相关工具",link:"#相关工具",children:[]}],path:"/zh/plugins/seo/guide.html",pathLocale:"/zh/",extraFields:[]},{title:"sitemap",headers:[{level:2,title:"使用",slug:"使用",link:"#使用",children:[]}],path:"/zh/plugins/sitemap/",pathLocale:"/zh/",extraFields:[]},{title:"配置",headers:[{level:2,title:"hostname",slug:"hostname",link:"#hostname",children:[]},{level:2,title:"extraUrls",slug:"extraurls",link:"#extraurls",children:[]},{level:2,title:"excludePaths",slug:"excludepaths",link:"#excludepaths",children:[]},{level:2,title:"devServer",slug:"devserver",link:"#devserver",children:[]},{level:2,title:"devHostname",slug:"devhostname",link:"#devhostname",children:[]},{level:2,title:"sitemapFilename",slug:"sitemapfilename",link:"#sitemapfilename",children:[]},{level:2,title:"sitemapXSLFilename",slug:"sitemapxslfilename",link:"#sitemapxslfilename",children:[]},{level:2,title:"sitemapXSLTemplate",slug:"sitemapxsltemplate",link:"#sitemapxsltemplate",children:[]},{level:2,title:"changefreq",slug:"changefreq",link:"#changefreq",children:[]},{level:2,title:"priority",slug:"priority",link:"#priority",children:[]},{level:2,title:"modifyTimeGetter",slug:"modifytimegetter",link:"#modifytimegetter",children:[]}],path:"/zh/plugins/sitemap/config.html",pathLocale:"/zh/",extraFields:[]},{title:"Frontmatter",headers:[{level:2,title:"sitemap",slug:"sitemap",link:"#sitemap",children:[{level:3,title:"sitemap.changefreq",slug:"sitemap-changefreq",link:"#sitemap-changefreq",children:[]},{level:3,title:"sitemap.priority",slug:"sitemap-priority",link:"#sitemap-priority",children:[]}]}],path:"/zh/plugins/sitemap/frontmatter.html",pathLocale:"/zh/",extraFields:[]},{title:"指南",headers:[{level:2,title:"控制 Sitemap 链接",slug:"控制-sitemap-链接",link:"#控制-sitemap-链接",children:[]},{level:2,title:"输出位置",slug:"输出位置",link:"#输出位置",children:[]},{level:2,title:"更新周期",slug:"更新周期",link:"#更新周期",children:[]},{level:2,title:"优先级",slug:"优先级",link:"#优先级",children:[]},{level:2,title:"修改时间获取",slug:"修改时间获取",link:"#修改时间获取",children:[]},{level:2,title:"Sitemap 介绍",slug:"sitemap-介绍",link:"#sitemap-介绍",children:[]}],path:"/zh/plugins/sitemap/guide.html",pathLocale:"/zh/",extraFields:[]},{title:"默认主题",headers:[{level:2,title:"使用方法",slug:"使用方法",link:"#使用方法",children:[]}],path:"/zh/themes/default/",pathLocale:"/zh/",extraFields:[]},{title:"内置组件",headers:[{level:2,title:"Badge",slug:"badge",link:"#badge",children:[]},{level:2,title:"CodeGroup",slug:"codegroup",link:"#codegroup",children:[]},{level:2,title:"CodeGroupItem",slug:"codegroupitem",link:"#codegroupitem",children:[]}],path:"/zh/themes/default/components.html",pathLocale:"/zh/",extraFields:[]},{title:"配置",headers:[{level:2,title:"基础配置",slug:"基础配置",link:"#基础配置",children:[{level:3,title:"hostname",slug:"hostname",link:"#hostname",children:[]},{level:3,title:"locales",slug:"locales",link:"#locales",children:[]}]},{level:2,title:"Locale 配置",slug:"locale-配置",link:"#locale-配置",children:[{level:3,title:"colorMode",slug:"colormode",link:"#colormode",children:[]},{level:3,title:"colorModeSwitch",slug:"colormodeswitch",link:"#colormodeswitch",children:[]},{level:3,title:"home",slug:"home",link:"#home",children:[]},{level:3,title:"navbar",slug:"navbar",link:"#navbar",children:[]},{level:3,title:"logo",slug:"logo",link:"#logo",children:[]},{level:3,title:"logoDark",slug:"logodark",link:"#logodark",children:[]},{level:3,title:"logoAlt",slug:"logoalt",link:"#logoalt",children:[]},{level:3,title:"repo",slug:"repo",link:"#repo",children:[]},{level:3,title:"sidebar",slug:"sidebar",link:"#sidebar",children:[]},{level:3,title:"sidebarDepth",slug:"sidebardepth",link:"#sidebardepth",children:[]},{level:3,title:"editLink",slug:"editlink",link:"#editlink",children:[]},{level:3,title:"editLinkPattern",slug:"editlinkpattern",link:"#editlinkpattern",children:[]},{level:3,title:"docsRepo",slug:"docsrepo",link:"#docsrepo",children:[]},{level:3,title:"docsBranch",slug:"docsbranch",link:"#docsbranch",children:[]},{level:3,title:"docsDir",slug:"docsdir",link:"#docsdir",children:[]},{level:3,title:"lastUpdated",slug:"lastupdated",link:"#lastupdated",children:[]},{level:3,title:"contributors",slug:"contributors",link:"#contributors",children:[]}]}],path:"/zh/themes/default/config.html",pathLocale:"/zh/",extraFields:[]},{title:"继承",headers:[{level:2,title:"布局插槽",slug:"布局插槽",link:"#布局插槽",children:[]},{level:2,title:"组件替换",slug:"组件替换",link:"#组件替换",children:[]},{level:2,title:"开发一个子主题",slug:"开发一个子主题",link:"#开发一个子主题",children:[]}],path:"/zh/themes/default/extending.html",pathLocale:"/zh/",extraFields:[]},{title:"Frontmatter",headers:[{level:2,title:"所有页面",slug:"所有页面",link:"#所有页面",children:[{level:3,title:"externalLinkIcon",slug:"externallinkicon",link:"#externallinkicon",children:[]},{level:3,title:"navbar",slug:"navbar",link:"#navbar",children:[]},{level:3,title:"pageClass",slug:"pageclass",link:"#pageclass",children:[]}]},{level:2,title:"首页",slug:"首页",link:"#首页",children:[{level:3,title:"home",slug:"home",link:"#home",children:[]},{level:3,title:"heroImage",slug:"heroimage",link:"#heroimage",children:[]},{level:3,title:"heroImageDark",slug:"heroimagedark",link:"#heroimagedark",children:[]},{level:3,title:"heroAlt",slug:"heroalt",link:"#heroalt",children:[]},{level:3,title:"heroHeight",slug:"heroheight",link:"#heroheight",children:[]},{level:3,title:"heroText",slug:"herotext",link:"#herotext",children:[]},{level:3,title:"tagline",slug:"tagline",link:"#tagline",children:[]},{level:3,title:"actions",slug:"actions",link:"#actions",children:[]},{level:3,title:"features",slug:"features",link:"#features",children:[]},{level:3,title:"footer",slug:"footer",link:"#footer",children:[]},{level:3,title:"footerHtml",slug:"footerhtml",link:"#footerhtml",children:[]}]},{level:2,title:"普通页面",slug:"普通页面",link:"#普通页面",children:[{level:3,title:"editLink",slug:"editlink",link:"#editlink",children:[]},{level:3,title:"editLinkPattern",slug:"editlinkpattern",link:"#editlinkpattern",children:[]},{level:3,title:"lastUpdated",slug:"lastupdated",link:"#lastupdated",children:[]},{level:3,title:"contributors",slug:"contributors",link:"#contributors",children:[]},{level:3,title:"sidebar",slug:"sidebar",link:"#sidebar",children:[]},{level:3,title:"sidebarDepth",slug:"sidebardepth",link:"#sidebardepth",children:[]},{level:3,title:"prev",slug:"prev",link:"#prev",children:[]},{level:3,title:"next",slug:"next",link:"#next",children:[]}]}],path:"/zh/themes/default/frontmatter.html",pathLocale:"/zh/",extraFields:[]},{title:"语言配置",headers:[{level:2,title:"repoLabel",slug:"repolabel",link:"#repolabel",children:[]},{level:2,title:"selectLanguageText",slug:"selectlanguagetext",link:"#selectlanguagetext",children:[]},{level:2,title:"selectLanguageAriaLabel",slug:"selectlanguagearialabel",link:"#selectlanguagearialabel",children:[]},{level:2,title:"selectLanguageName",slug:"selectlanguagename",link:"#selectlanguagename",children:[]},{level:2,title:"navbarLabel",slug:"navbarlabel",link:"#navbarlabel",children:[]},{level:2,title:"pageNavbarLabel",slug:"pagenavbarlabel",link:"#pagenavbarlabel",children:[]},{level:2,title:"editLinkText",slug:"editlinktext",link:"#editlinktext",children:[]},{level:2,title:"lastUpdatedText",slug:"lastupdatedtext",link:"#lastupdatedtext",children:[]},{level:2,title:"contributorsText",slug:"contributorstext",link:"#contributorstext",children:[]},{level:2,title:"tip",slug:"tip",link:"#tip",children:[]},{level:2,title:"warning",slug:"warning",link:"#warning",children:[]},{level:2,title:"danger",slug:"danger",link:"#danger",children:[]},{level:2,title:"notFound",slug:"notfound",link:"#notfound",children:[]},{level:2,title:"backToHome",slug:"backtohome",link:"#backtohome",children:[]},{level:2,title:"openInNewWindow",slug:"openinnewwindow",link:"#openinnewwindow",children:[]},{level:2,title:"toggleColorMode",slug:"togglecolormode",link:"#togglecolormode",children:[]},{level:2,title:"toggleSidebar",slug:"togglesidebar",link:"#togglesidebar",children:[]}],path:"/zh/themes/default/locale.html",pathLocale:"/zh/",extraFields:[]},{title:"Markdown",headers:[{level:2,title:"自定义容器",slug:"自定义容器",link:"#自定义容器",children:[]}],path:"/zh/themes/default/markdown.html",pathLocale:"/zh/",extraFields:[]},{title:"插件配置",headers:[{level:2,title:"themePlugins.activeHeaderLinks",slug:"themeplugins-activeheaderlinks",link:"#themeplugins-activeheaderlinks",children:[]},{level:2,title:"themePlugins.backToTop",slug:"themeplugins-backtotop",link:"#themeplugins-backtotop",children:[]},{level:2,title:"themePlugins.container",slug:"themeplugins-container",link:"#themeplugins-container",children:[]},{level:2,title:"themePlugins.copyCode",slug:"themeplugins-copycode",link:"#themeplugins-copycode",children:[]},{level:2,title:"themePlugins.externalLinkIcon",slug:"themeplugins-externallinkicon",link:"#themeplugins-externallinkicon",children:[]},{level:2,title:"themePlugins.git",slug:"themeplugins-git",link:"#themeplugins-git",children:[]},{level:2,title:"themePlugins.mediumZoom",slug:"themeplugins-mediumzoom",link:"#themeplugins-mediumzoom",children:[]},{level:2,title:"themePlugins.nprogress",slug:"themeplugins-nprogress",link:"#themeplugins-nprogress",children:[]},{level:2,title:"themePlugins.seo",slug:"themeplugins-seo",link:"#themeplugins-seo",children:[]},{level:2,title:"themePlugins.sitemap",slug:"themeplugins-sitemap",link:"#themeplugins-sitemap",children:[]}],path:"/zh/themes/default/plugin.html",pathLocale:"/zh/",extraFields:[]},{title:"样式",headers:[{level:2,title:"Palette 文件",slug:"palette-文件",link:"#palette-文件",children:[]},{level:2,title:"Style 文件",slug:"style-文件",link:"#style-文件",children:[]}],path:"/zh/themes/default/styles.html",pathLocale:"/zh/",extraFields:[]},{title:"@vuepress/helper",headers:[],path:"/zh/tools/helper/",pathLocale:"/zh/",extraFields:[]},{title:"客户端相关",headers:[{level:2,title:"可组合 API",slug:"可组合-api",link:"#可组合-api",children:[{level:3,title:"hasGlobalComponent",slug:"hasglobalcomponent",link:"#hasglobalcomponent",children:[]},{level:3,title:"useLocaleConfig",slug:"uselocaleconfig",link:"#uselocaleconfig",children:[]}]}],path:"/zh/tools/helper/client.html",pathLocale:"/zh/",extraFields:[]},{title:"共享方法",headers:[{level:2,title:"数据相关",slug:"数据相关",link:"#数据相关",children:[]},{level:2,title:"类型助手",slug:"类型助手",link:"#类型助手",children:[]},{level:2,title:"字符串相关",slug:"字符串相关",link:"#字符串相关",children:[]},{level:2,title:"对象相关",slug:"对象相关",link:"#对象相关",children:[]},{level:2,title:"日期相关",slug:"日期相关",link:"#日期相关",children:[]},{level:2,title:"链接相关",slug:"链接相关",link:"#链接相关",children:[]}],path:"/zh/tools/helper/shared.html",pathLocale:"/zh/",extraFields:[]},{title:"打包器相关",headers:[{level:2,title:"通用方法",slug:"通用方法",link:"#通用方法",children:[{level:3,title:"getBundlerName",slug:"getbundlername",link:"#getbundlername",children:[]},{level:3,title:"addCustomElement",slug:"addcustomelement",link:"#addcustomelement",children:[]},{level:3,title:"customizeDevServer",slug:"customizedevserver",link:"#customizedevserver",children:[]}]},{level:2,title:"Vite 相关",slug:"vite-相关",link:"#vite-相关",children:[]},{level:2,title:"Webpack 相关",slug:"webpack-相关",link:"#webpack-相关",children:[]}],path:"/zh/tools/helper/node/bundler.html",pathLocale:"/zh/",extraFields:[]},{title:"页面相关",headers:[{level:2,title:"getPageExcerpt",slug:"getpageexcerpt",link:"#getpageexcerpt",children:[]},{level:2,title:"getPageText",slug:"getpagetext",link:"#getpagetext",children:[]}],path:"/zh/tools/helper/node/page.html",pathLocale:"/zh/",extraFields:[]},{title:"",headers:[],path:"/404.html",pathLocale:"/",extraFields:[]},{title:"Tools",headers:[],path:"/tools/",pathLocale:"/",extraFields:[]},{title:"Node",headers:[],path:"/tools/helper/node/",pathLocale:"/",extraFields:[]},{title:"Tools",headers:[],path:"/zh/tools/",pathLocale:"/",extraFields:[]},{title:"Node",headers:[],path:"/zh/tools/helper/node/",pathLocale:"/",extraFields:[]}],Sm=ae(Tm),Pm=()=>Sm,zm=({searchIndex:e,routeLocale:t,query:l,maxSuggestions:n})=>{const i=O(()=>e.value.filter(r=>r.pathLocale===t.value));return O(()=>{const r=l.value.trim().toLowerCase();if(!r)return[];const s=[],o=(a,c)=>{Ds(r,[c.title])&&s.push({link:`${a.path}#${c.slug}`,title:a.title,header:c.title});for(const u of c.children){if(s.length>=n.value)return;o(a,u)}};for(const a of i.value){if(s.length>=n.value)break;if(Ds(r,[a.title,...a.extraFields])){s.push({link:a.path,title:a.title});continue}for(const c of a.headers){if(s.length>=n.value)break;o(a,c)}}return s})},Am=e=>{const t=ae(0);return{focusIndex:t,focusNext:()=>{t.value{t.value>0?t.value-=1:t.value=e.value.length-1}}},Om=fe({name:"SearchBox",props:{locales:{type:Object,required:!1,default:()=>({})},hotKeys:{type:Array,required:!1,default:()=>[]},maxSuggestions:{type:Number,required:!1,default:5}},setup(e){const{locales:t,hotKeys:l,maxSuggestions:n}=Un(e),i=Bt(),r=ol(),s=Pm(),o=ae(null),a=ae(!1),c=ae(""),u=O(()=>t.value[r.value]??{}),d=zm({searchIndex:s,routeLocale:r,query:c,maxSuggestions:n}),{focusIndex:h,focusNext:p,focusPrev:m}=Am(d);Cm({input:o,hotKeys:l});const b=O(()=>a.value&&!!d.value.length),x=()=>{b.value&&m()},y=()=>{b.value&&p()},S=v=>{if(!b.value)return;const w=d.value[v];w&&i.push(w.link).then(()=>{c.value="",h.value=0})};return()=>q("form",{class:"search-box",role:"search"},[q("input",{ref:o,type:"search",placeholder:u.value.placeholder,autocomplete:"off",spellcheck:!1,value:c.value,onFocus:()=>a.value=!0,onBlur:()=>a.value=!1,onInput:v=>c.value=v.target.value,onKeydown:v=>{switch(v.key){case"ArrowUp":{x();break}case"ArrowDown":{y();break}case"Enter":{v.preventDefault(),S(h.value);break}}}}),b.value&&q("ul",{class:"suggestions",onMouseleave:()=>h.value=-1},d.value.map(({link:v,title:w,header:H},I)=>q("li",{class:["suggestion",{focus:h.value===I}],onMouseenter:()=>h.value=I,onMousedown:()=>S(I)},q("a",{href:v,onClick:M=>M.preventDefault()},[q("span",{class:"page-title"},w),H&&q("span",{class:"page-header"},`> ${H}`)]))))])}});var Rm=["s","/"],Im={};const Fm=Im,Dm=Rm,$m=5,Mm=ct({enhance({app:e}){e.component("SearchBox",t=>q(Om,{locales:Fm,hotKeys:Dm,maxSuggestions:$m,...t}))}}),En=[pg,bg,Ag,Dg,qg,Qg,np,um,mm,bm,ym,Mm],Nm=JSON.parse('{"base":"/ecosystem/","lang":"en-US","title":"","description":"","head":[["link",{"rel":"icon","type":"image/png","sizes":"16x16","href":"/images/icons/favicon-16x16.png"}],["link",{"rel":"icon","type":"image/png","sizes":"32x32","href":"/images/icons/favicon-32x32.png"}],["link",{"rel":"manifest","href":"/manifest.webmanifest"}],["meta",{"name":"application-name","content":"VuePress"}],["meta",{"name":"apple-mobile-web-app-title","content":"VuePress"}],["meta",{"name":"apple-mobile-web-app-status-bar-style","content":"black"}],["link",{"rel":"apple-touch-icon","href":"/images/icons/apple-touch-icon.png"}],["link",{"rel":"mask-icon","href":"/images/icons/safari-pinned-tab.svg","color":"#3eaf7c"}],["meta",{"name":"msapplication-TileColor","content":"#3eaf7c"}],["meta",{"name":"theme-color","content":"#3eaf7c"}]],"locales":{"/":{"lang":"en-US","title":"VuePress Ecosystem","description":"VuePress official themes plugins"},"/zh/":{"lang":"zh-CN","title":"VuePress 生态系统","description":"VuePress 官方主题和插件"}}}');var zl=sl(Nm),Hm=Nd,Vm=()=>{const e=Eh({history:Hm(qo("/ecosystem/")),routes:[{name:"vuepress-route",path:"/:catchAll(.*)",components:{}}],scrollBehavior:(t,l,n)=>n||(t.hash?{el:t.hash}:{top:0})});return e.beforeResolve(async(t,l)=>{if(t.path!==l.path||l===bt){const n=sr(t.path);if(n.path!==t.path)return n.path;const i=await n.loader();t.meta={...n.meta,_pageChunk:i}}else t.path===l.path&&(t.meta=l.meta)}),e},Bm=e=>{e.component("ClientOnly",or),e.component("Content",zh),e.component("RouteLink",yt)},jm=(e,t,l)=>{const n=O(()=>t.currentRoute.value.path),i=co((x,y)=>({get(){return x(),t.currentRoute.value.meta._pageChunk},set(S){t.currentRoute.value.meta._pageChunk=S,y()}})),r=O(()=>Gt.resolveLayouts(l)),s=O(()=>Gt.resolveRouteLocale(zl.value.locales,n.value)),o=O(()=>Gt.resolveSiteLocaleData(zl.value,s.value)),a=O(()=>i.value.comp),c=O(()=>i.value.data),u=O(()=>c.value.frontmatter),d=O(()=>Gt.resolvePageHeadTitle(c.value,o.value)),h=O(()=>Gt.resolvePageHead(d.value,u.value,o.value)),p=O(()=>Gt.resolvePageLang(c.value,o.value)),m=O(()=>Gt.resolvePageLayout(c.value,r.value)),b={layouts:r,pageData:c,pageComponent:a,pageFrontmatter:u,pageHead:h,pageHeadTitle:d,pageLang:p,pageLayout:m,redirects:aa,routeLocale:s,routePath:n,routes:Gl,siteData:zl,siteLocaleData:o};return e.provide(nr,b),Object.defineProperties(e.config.globalProperties,{$frontmatter:{get:()=>u.value},$head:{get:()=>h.value},$headTitle:{get:()=>d.value},$lang:{get:()=>p.value},$page:{get:()=>c.value},$routeLocale:{get:()=>s.value},$site:{get:()=>zl.value},$siteLocale:{get:()=>o.value},$withBase:{get:()=>ei}}),b},Um=()=>{const e=Ch(),t=Th();let l=[];const n=()=>{e.value.forEach(s=>{const o=Wm(s);o&&l.push(o)})},i=()=>{const s=[];return e.value.forEach(o=>{const a=Gm(o);a&&s.push(a)}),s},r=()=>{document.documentElement.lang=t.value;const s=i();l.forEach((o,a)=>{const c=s.findIndex(u=>o.isEqualNode(u));c===-1?(o.remove(),delete l[a]):s.splice(c,1)}),s.forEach(o=>document.head.appendChild(o)),l=[...l.filter(o=>!!o),...s]};el(Ph,r),je(()=>{n(),Fe(e,r,{immediate:!1})})},Wm=([e,t,l=""])=>{const n=Object.entries(t).map(([o,a])=>qe(a)?`[${o}=${JSON.stringify(a)}]`:a===!0?`[${o}]`:"").join(""),i=`head > ${e}${n}`;return Array.from(document.querySelectorAll(i)).find(o=>o.innerText===l)||null},Gm=([e,t,l])=>{if(!qe(e))return null;const n=document.createElement(e);return Jn(t)&&Object.entries(t).forEach(([i,r])=>{qe(r)?n.setAttribute(i,r):r===!0&&n.setAttribute(i,"")}),qe(l)&&n.appendChild(document.createTextNode(l)),n},Km=od,qm=async()=>{var l;const e=Km({name:"Vuepress",setup(){var r;Um();for(const s of En)(r=s.setup)==null||r.call(s);const n=En.flatMap(({rootComponents:s=[]})=>s.map(o=>q(o))),i=Sh();return()=>[q(i.value),n]}}),t=Vm();Bm(e),jm(e,t,En);for(const n of En)await((l=n.enhance)==null?void 0:l.call(n,{app:e,router:t,siteData:zl}));return e.use(t),{app:e,router:t}};qm().then(({app:e,router:t})=>{t.isReady().then(()=>{e.mount("#app")})});export{we as _,ie as a,pe as b,ee as c,qm as createVueApp,Vt as d,vu as e,fe as f,O as g,U as o,ll as r,$e as w}; +function __vite__mapDeps(indexes) { + if (!__vite__mapDeps.viteFileDeps) { + __vite__mapDeps.viteFileDeps = ["assets/extending.html-B-2YhQDL.js","assets/extending-a-theme-01-Cqco1E4_.js","assets/extending.html-C_vYwTyq.js"] + } + return indexes.map((i) => __vite__mapDeps.viteFileDeps[i]) +} diff --git a/assets/back-to-top.html-DIJmeRQG.js b/assets/back-to-top.html-DIJmeRQG.js new file mode 100644 index 0000000000..4e49f45618 --- /dev/null +++ b/assets/back-to-top.html-DIJmeRQG.js @@ -0,0 +1,15 @@ +import{_ as e,r as l,o as n,c as o,a as t,b as s,e as p}from"./app-DI49vYHA.js";const c={},i=s("h1",{id:"back-to-top",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#back-to-top"},[s("span",null,"back-to-top")])],-1),r=p(`

This plugin will add a back to top button to your site. The button will be displayed in the bottom right corner of the page when scrolling down. By clicking the button, the page will scroll to the top.

This plugin has been integrated into the default theme.

Usage

npm i -D @vuepress/plugin-back-to-top@next
+
import { backToTopPlugin } from '@vuepress/plugin-back-to-top'
+
+export default {
+  plugins: [backToTopPlugin()],
+}
+

Options

threshold

  • Type: number
  • Default: 100
  • Details: Scroll threshold distance to display back to top button (in pixels)

progress

  • Type: boolean
  • Default: true
  • Details: Whether display progress bar around icon

Styles

You can customize the style of the back to top button via CSS variables:

:root {
+  --back-to-top-z-index: 5;
+  --back-to-top-icon: url("back-to-top.svg");
+  --back-to-top-bg-color: #fff;
+  --back-to-top-color: #3eaf7c;
+  --back-to-top-color-hover: #71cda3;
+  --back-to-top-shadow: rgb(0 0 0 / 20%);
+}
+
`,13);function d(D,h){const a=l("NpmBadge");return n(),o("div",null,[i,t(a,{package:"@vuepress/plugin-back-to-top"}),r])}const b=e(c,[["render",d],["__file","back-to-top.html.vue"]]),y=JSON.parse('{"path":"/plugins/back-to-top.html","title":"back-to-top","lang":"en-US","frontmatter":{},"headers":[{"level":2,"title":"Usage","slug":"usage","link":"#usage","children":[]},{"level":2,"title":"Options","slug":"options","link":"#options","children":[{"level":3,"title":"threshold","slug":"threshold","link":"#threshold","children":[]},{"level":3,"title":"progress","slug":"progress","link":"#progress","children":[]}]},{"level":2,"title":"Styles","slug":"styles","link":"#styles","children":[]}],"git":{"updatedTime":1706960012000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":2}]},"filePathRelative":"plugins/back-to-top.md"}');export{b as comp,y as data}; diff --git a/assets/back-to-top.html-DWZAoYSt.js b/assets/back-to-top.html-DWZAoYSt.js new file mode 100644 index 0000000000..fc2a75eca8 --- /dev/null +++ b/assets/back-to-top.html-DWZAoYSt.js @@ -0,0 +1,15 @@ +import{_ as n,r as l,o as e,c as o,a as p,b as s,e as c}from"./app-DI49vYHA.js";const t={},r=s("h1",{id:"back-to-top",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#back-to-top"},[s("span",null,"back-to-top")])],-1),i=c(`

该插件会给你的站点添加一个 返回顶部 按钮。当页面向下滚动时,该按钮会显示在页面的右下角,点击它就会滚动到页面顶部。

该插件已经集成到默认主题中。

使用方法

npm i -D @vuepress/plugin-back-to-top@next
+
import { backToTopPlugin } from '@vuepress/plugin-back-to-top'
+
+export default {
+  plugins: [backToTopPlugin()],
+}
+

选项

threshold

  • 类型:数字
  • 默认值:100
  • 详情:显示返回顶部按钮的滚动阈值距离(以像素为单位)

progress

  • 类型:布尔值
  • 默认值:true
  • 详情:是否在图标周围显示进度条

样式

你可以通过 CSS 变量来自定义 返回顶部 按钮的样式:

:root {
+  --back-to-top-z-index: 5;
+  --back-to-top-icon: url("back-to-top.svg");
+  --back-to-top-bg-color: #fff;
+  --back-to-top-color: #3eaf7c;
+  --back-to-top-color-hover: #71cda3;
+  --back-to-top-shadow: rgb(0 0 0 / 20%);
+}
+
`,13);function d(D,h){const a=l("NpmBadge");return e(),o("div",null,[r,p(a,{package:"@vuepress/plugin-back-to-top"}),i])}const b=n(t,[["render",d],["__file","back-to-top.html.vue"]]),y=JSON.parse('{"path":"/zh/plugins/back-to-top.html","title":"back-to-top","lang":"zh-CN","frontmatter":{},"headers":[{"level":2,"title":"使用方法","slug":"使用方法","link":"#使用方法","children":[]},{"level":2,"title":"选项","slug":"选项","link":"#选项","children":[{"level":3,"title":"threshold","slug":"threshold","link":"#threshold","children":[]},{"level":3,"title":"progress","slug":"progress","link":"#progress","children":[]}]},{"level":2,"title":"样式","slug":"样式","link":"#样式","children":[]}],"git":{"updatedTime":1706960012000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":2}]},"filePathRelative":"zh/plugins/back-to-top.md"}');export{b as comp,y as data}; diff --git a/assets/baidu-analytics.html-AobDAH-_.js b/assets/baidu-analytics.html-AobDAH-_.js new file mode 100644 index 0000000000..aff66ba880 --- /dev/null +++ b/assets/baidu-analytics.html-AobDAH-_.js @@ -0,0 +1,11 @@ +import{_ as t,r as l,o,c,a,b as s,d as n,e as r}from"./app-DI49vYHA.js";const d={},p=s("h1",{id:"baidu-analytics",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#baidu-analytics"},[s("span",null,"baidu-analytics")])],-1),u={href:"https://tongji.baidu.com/",target:"_blank",rel:"noopener noreferrer"},h=r(`

使用方法

npm i -D @vuepress/plugin-baidu-analytics@next
+
import { baiduAnalyticsPlugin } from '@vuepress/plugin-baidu-analytics'
+
+export default {
+  plugins: [
+    baiduAnalyticsPlugin({
+      // 配置项
+    }),
+  ],
+}
+

上报事件

`,4),m=s("code",null,"hmt",-1),b=s("code",null,"window",-1),D={href:"https://tongji.baidu.com/holmes/Analytics/%E6%8A%80%E6%9C%AF%E6%8E%A5%E5%85%A5%E6%8C%87%E5%8D%97/JS%20API/JS%20API%20%E4%BD%BF%E7%94%A8%E6%89%8B%E5%86%8C",target:"_blank",rel:"noopener noreferrer"},v=s("h2",{id:"选项",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#选项"},[s("span",null,"选项")])],-1),y=s("h3",{id:"id",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#id"},[s("span",null,"id")])],-1),_=s("ul",null,[s("li",null,[n("类型: "),s("code",null,"string")]),s("li",null,[n("详情: 百度统计的 ID ,即 "),s("code",null,"hm.js"),n(" URL 中的查询参数。")])],-1);function g(E,C){const i=l("NpmBadge"),e=l("ExternalLinkIcon");return o(),c("div",null,[p,a(i,{package:"@vuepress/plugin-baidu-analytics"}),s("p",null,[n("将 "),s("a",u,[n("百度统计"),a(e)]),n(" 集成到 VuePress 中。")]),h,s("p",null,[n("在使用该插件之后,一个全局的 "),m,n(" 数组会被挂载到 "),b,n(" 对象上,你可以使用它进行 "),s("a",D,[n("自定义事件的上报"),a(e)]),n(" 。")]),v,y,_])}const k=t(d,[["render",g],["__file","baidu-analytics.html.vue"]]),x=JSON.parse('{"path":"/zh/plugins/baidu-analytics.html","title":"baidu-analytics","lang":"zh-CN","frontmatter":{},"headers":[{"level":2,"title":"使用方法","slug":"使用方法","link":"#使用方法","children":[{"level":3,"title":"上报事件","slug":"上报事件","link":"#上报事件","children":[]}]},{"level":2,"title":"选项","slug":"选项","link":"#选项","children":[{"level":3,"title":"id","slug":"id","link":"#id","children":[]}]}],"git":{"updatedTime":1707328107000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":1}]},"filePathRelative":"zh/plugins/baidu-analytics.md"}');export{k as comp,x as data}; diff --git a/assets/baidu-analytics.html-B5FBeNCL.js b/assets/baidu-analytics.html-B5FBeNCL.js new file mode 100644 index 0000000000..afeefe294b --- /dev/null +++ b/assets/baidu-analytics.html-B5FBeNCL.js @@ -0,0 +1,11 @@ +import{_ as o,r as l,o as r,c,a as e,b as s,d as n,e as i}from"./app-DI49vYHA.js";const p={},d=s("h1",{id:"baidu-analytics",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#baidu-analytics"},[s("span",null,"baidu-analytics")])],-1),u={href:"https://tongji.baidu.com/",target:"_blank",rel:"noopener noreferrer"},h=i(`

Usage

npm i -D @vuepress/plugin-baidu-analytics@next
+
import { baiduAnalyticsPlugin } from '@vuepress/plugin-baidu-analytics'
+
+export default {
+  plugins: [
+    baiduAnalyticsPlugin({
+      // options
+    }),
+  ],
+}
+

Reporting Events

`,4),m=s("code",null,"hmt",-1),v=s("code",null,"window",-1),b={href:"https://tongji.baidu.com/holmes/Analytics/%E6%8A%80%E6%9C%AF%E6%8E%A5%E5%85%A5%E6%8C%87%E5%8D%97/JS%20API/JS%20API%20%E4%BD%BF%E7%94%A8%E6%89%8B%E5%86%8C",target:"_blank",rel:"noopener noreferrer"},g=i('

Options

id

  • Type: string

  • Details: The ID of Baidu Analytics, which is the query of hm.js URL.

',3);function y(D,_){const t=l("NpmBadge"),a=l("ExternalLinkIcon");return r(),c("div",null,[d,e(t,{package:"@vuepress/plugin-baidu-analytics"}),s("p",null,[n("Integrate "),s("a",u,[n("Baidu Analytics"),e(a)]),n(" into VuePress.")]),h,s("p",null,[n("After using this plugin, the global "),m,n(" array is available on the "),v,n(" object, and you can use it for "),s("a",b,[n("custom events reporting"),e(a)]),n(".")]),g])}const f=o(p,[["render",y],["__file","baidu-analytics.html.vue"]]),C=JSON.parse('{"path":"/plugins/baidu-analytics.html","title":"baidu-analytics","lang":"en-US","frontmatter":{},"headers":[{"level":2,"title":"Usage","slug":"usage","link":"#usage","children":[{"level":3,"title":"Reporting Events","slug":"reporting-events","link":"#reporting-events","children":[]}]},{"level":2,"title":"Options","slug":"options","link":"#options","children":[{"level":3,"title":"id","slug":"id","link":"#id","children":[]}]}],"git":{"updatedTime":1707328107000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":1}]},"filePathRelative":"plugins/baidu-analytics.md"}');export{f as comp,C as data}; diff --git a/assets/bundler.html-9zmlqkgM.js b/assets/bundler.html-9zmlqkgM.js new file mode 100644 index 0000000000..56ca70106d --- /dev/null +++ b/assets/bundler.html-9zmlqkgM.js @@ -0,0 +1,176 @@ +import{_ as s,o as n,c as a,e as l}from"./app-DI49vYHA.js";const e={},p=l(`

Bundler Related

Bundler function is for appending or modifying bundler options in theme and plugins.

All functions should be called in extendsBundlerOptions lifecycle hook.

TIP

We are omitting that in examples. The actual code should be like this:

// import functions you need
+import { addCustomElement } from '@vuepress/helper'
+
+export const yourPlugin = {
+  // ...
+  extendsBundlerOptions: (bundlerOptions, app) => {
+    // add them here
+    addCustomElement(bundlerOptions, app, 'my-custom-element')
+  },
+}
+

Common methods

getBundlerName

Get current bundler name.

export const getBundlerName: (app: App) => string
+
Example
// @vuepress/bundler-vite
+getBundleName(app) === 'vite' // true
+// @vuepress/bundler-webpack
+getBundleName(app) === 'webpack' // true
+

addCustomElement

Add a custom element declaration to the current bundler.

/**
+ * Add tags as customElement
+ *
+ * @param bundlerOptions VuePress Bundler config
+ * @param app VuePress Node App
+ * @param customElements tags recognized as custom element
+ */
+export const addCustomElement = (
+  bundlerOptions: unknown,
+  app: App,
+  customElement: string[] | string | RegExp
+) => void;
+
Example
import { addCustomElement } from '@vuepress/helper'
+
+addCustomElement(bundlerConfig, app, 'my-custom-element')
+addCustomElement(bundlerOptions, app, [
+  'custom-element1',
+  'custom-element2',
+  // all tags start with \`math-\`
+  /^math-/,
+])
+

customizeDevServer

Provides contents for specific path in dev server.

export interface DevServerOptions {
+  /**
+   * Path to be responded
+   */
+  path: string;
+  /**
+   * Respond function
+   */
+  response: (request?: IncomingMessage) => Promise<string | Buffer>;
+
+  /**
+   * error msg
+   */
+  errMsg?: string;
+}
+
+/**
+ * Handle specific path when running VuePress Dev Server
+ *
+ * @param bundlerOptions VuePress Bundler config
+ * @param app VuePress Node App
+ * @param path Path to be responded
+ * @param response respond function
+ * @param errMsg error msg
+ */
+export const customizeDevServer: (
+  bundlerOptions: unknown,
+  app: App,
+  {
+    errMsg:"The server encountered an error",
+    response: responseHandler,
+    path,
+  }: CustomServerOptions
+) => void;
+
Example
import { useCustomDevServer } from '@vuepress/helper'
+
+// handle \`/api/\` path
+useCustomDevServer(bundlerOptions, app, {
+  path: '/api/',
+  response: async () => getData(),
+  errMsg: 'Unexpected api error',
+})
+
  • addViteOptimizeDepsInclude

    Add modules to Vite optimizeDeps.include list

    TIP

    If a package meets one of the following conditions, you should consider adding it here.

    • It's in CJS format
    • It's dependencies include CJS package
    • It's dynamically imported via import()
  • addViteOptimizeDepsExclude

    Add modules to Vite optimizeDeps.exclude list

    If a package and its dependencies are all pure ESM packages, you should consider adding it here.

  • addViteSsrExternal

    Add modules to Vite ssr.external list

    If a package is a pure ESM package and does not use aliases or define variables, you should consider adding it here.

  • addViteSsrNoExternal

    Add modules to Vite ssr.noExternal list

    If an alias or define is used within a package, you must add it here.

    /**
    + * Add modules to Vite \`optimizeDeps.include\` list
    + */
    +export const addViteOptimizeDepsInclude: (
    +  bundlerOptions: unknown,
    +  app: App,
    +  module: string | string[],
    +) => void
    +
    +/**
    + * Add modules to Vite \`optimizeDeps.exclude\` list
    + */
    +export const addViteOptimizeDepsExclude: (
    +  bundlerOptions: unknown,
    +  app: App,
    +  module: string | string[],
    +) => void
    +
    +/**
    + * Add modules to Vite \`ssr.external\` list
    + */
    +export const addViteSsrExternal: (
    +  bundlerOptions: unknown,
    +  app: App,
    +  module: string | string[],
    +) => void
    +
    +/**
    + * Add modules to Vite \`ssr.noExternal\` list
    + */
    +export const addViteSsrNoExternal: (
    +  bundlerOptions: unknown,
    +  app: App,
    +  module: string | string[],
    +) => void
    +
    Examples
    import {
    +  addViteOptimizeDepsInclude,
    +  addViteOptimizeDepsExclude,
    +  addViteSsrExternal,
    +  addViteSsrNoExternal,
    +} from '@vuepress/helper'
    +
    +addViteOptimizeDepsInclude(bundlerOptions, app, ['vue', 'vue-router'])
    +addViteOptimizeDepsExclude(bundlerOptions, app, 'packageA')
    +addViteSsrNoExternal(bundlerOptions, app, ['vue', 'vue-router'])
    +addViteSsrExternal(bundlerOptions, app, 'packageA')
    +
  • addViteConfig

    A function for you to add vite config

    export const addViteConfig: (
    +  bundlerOptions: unknown,
    +  app: App,
    +  config: Record<string, unknown>,
    +) => void
    +
    Example
    import { addViteConfig } from '@vuepress/helper'
    +
    +addViteConfig(bundlerOptions, app, {
    +  build: {
    +    charset: 'utf8',
    +  },
    +})
    +
  • mergeViteConfig

    A function for you to merge vite config.

    WARNING

    Your users may choose to use other bundler so it's pretty bad to declare vite as deps!

    export const mergeViteConfig: (
    +  defaults: Record<string, any>,
    +  overrides: Record<string, any>,
    +) => Record<string, any>
    +
    Example
    import { mergeViteConfig } from '@vuepress/helper'
    +
    +config.viteOptions = mergeViteConfig(config.viteOptions, {
    +  build: {
    +    charset: 'utf8',
    +  },
    +})
    +
  • chainWebpack

    Chain webpack config.

    export const chainWebpack: (
    +  bundlerOptions: unknown,
    +  app: App,
    +  chainWebpack: (
    +    config: WebpackChainConfig,
    +    isServer: boolean,
    +    isBuild: boolean,
    +  ) => void,
    +) => void
    +
    Example
    import { chainWebpack } from '@vuepress/helper'
    +
    +chainWebpack(bundlerOptions, app, (config, isServer, isBuild) => {
    +  // do some customize here
    +})
    +
  • configWebpack

    Config Webpack

    export const configWebpack: (
    +  bundlerOptions: unknown,
    +  app: App,
    +  configureWebpack: (
    +    config: WebpackConfiguration,
    +    isServer: boolean,
    +    isBuild: boolean,
    +  ) => void,
    +) => void
    +
    Example
    import { configWebpack } from '@vuepress/helper'
    +
    +configWebpack(bundlerOptions, app, (config, isServer, isBuild) => {
    +  // do some customize here
    +})
    +
`,21),o=[p];function c(i,r){return n(),a("div",null,o)}const D=s(e,[["render",c],["__file","bundler.html.vue"]]),d=JSON.parse('{"path":"/tools/helper/node/bundler.html","title":"Bundler Related","lang":"en-US","frontmatter":{},"headers":[{"level":2,"title":"Common methods","slug":"common-methods","link":"#common-methods","children":[{"level":3,"title":"getBundlerName","slug":"getbundlername","link":"#getbundlername","children":[]},{"level":3,"title":"addCustomElement","slug":"addcustomelement","link":"#addcustomelement","children":[]},{"level":3,"title":"customizeDevServer","slug":"customizedevserver","link":"#customizedevserver","children":[]}]},{"level":2,"title":"Vite Related","slug":"vite-related","link":"#vite-related","children":[]},{"level":2,"title":"Webpack Related","slug":"webpack-related","link":"#webpack-related","children":[]}],"git":{"updatedTime":1706960075000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":1}]},"filePathRelative":"tools/helper/node/bundler.md"}');export{D as comp,d as data}; diff --git a/assets/bundler.html-lrm24igQ.js b/assets/bundler.html-lrm24igQ.js new file mode 100644 index 0000000000..cb19ee7b7d --- /dev/null +++ b/assets/bundler.html-lrm24igQ.js @@ -0,0 +1,179 @@ +import{_ as s,o as n,c as a,e as l}from"./app-DI49vYHA.js";const e={},p=l(`

打包器相关

打包器函数用于在主题和插件中追加或修改打包器选项。

所有函数都应在 extendsBundlerOptions 生命周期挂钩中调用。

提示

我们在示例中省略了它。 实际代码应该是这样的:

// 导入你需要的函数
+import { addCustomElement } from '@vuepress/helper'
+
+export const yourPlugin = {
+  // ...
+  extendsBundlerOptions: (bundlerOptions, app) => {
+    // 在此添加它们
+    addCustomElement(bundlerOptions, app, 'my-custom-element')
+  },
+}
+

通用方法

getBundlerName

获取当前打包器的名称。

export const getBundlerName: (app: App) => string
+
示例
// @vuepress/bundler-vite
+getBundleName(app) === 'vite' // true
+// @vuepress/bundler-webpack
+getBundleName(app) === 'webpack' // true
+

addCustomElement

将自定义元素声明添加到当前的打包器。

interface CustomElementCommonOptions {
+  app: App;
+  config: unknown;
+}
+/**
+ * Add tags as customElement
+ *
+ * @param bundlerOptions VuePress Bundler config
+ * @param app VuePress Node App
+ * @param customElements tags recognized as custom element
+ */
+export const addCustomElement = (
+  bundlerOptions: unknown,
+  app: App,
+  customElement: string[] | string | RegExp
+) => void;
+
示例
import { addCustomElement } from '@vuepress/helper'
+
+addCustomElement(bundlerConfig, app, 'my-custom-element')
+addCustomElement(bundlerOptions, app, [
+  'custom-element1',
+  'custom-element2',
+  // all tags start with \`math-\`
+  /^math-/,
+])
+

customizeDevServer

为开发服务器中的特定路径提供内容。

export interface DevServerOptions {
+  /**
+   * Path to be responded
+   */
+  path: string;
+  /**
+   * Respond function
+   */
+  response: (request?: IncomingMessage) => Promise<string | Buffer>;
+
+  /**
+   * error msg
+   */
+  errMsg?: string;
+}
+
+/**
+ * Handle specific path when running VuePress Dev Server
+ *
+ * @param bundlerOptions VuePress Bundler config
+ * @param app VuePress Node App
+ * @param path Path to be responded
+ * @param response respond function
+ * @param errMsg error msg
+ */
+export const customizeDevServer: (
+  bundlerOptions: unknown,
+  app: App,
+  {
+    errMsg:"The server encountered an error",
+    response: responseHandler,
+    path,
+  }: CustomServerOptions
+) => void;
+
示例
import { useCustomDevServer } from '@vuepress/helper'
+
+// handle \`/api/\` path
+useCustomDevServer(bundlerOptions, app, {
+  path: '/api/',
+  response: async () => getData(),
+  errMsg: 'Unexpected api error',
+})
+

Vite 相关

  • addViteOptimizeDepsInclude

    向 Vite optimizeDeps.include 列表中添加模块

    提示

    如果一个包满足下列条件之一,你应该考虑将它添加至此。

    • 为 CJS 格式
    • 包的依赖包含 CJS 包
    • 包通过 import() 动态导入
  • addViteOptimizeDepsExclude

    向 Vite optimizeDeps.exclude 列表中添加模块

    如果一个包和它的依赖都是纯 ESM 包,你应该考虑将它添加至此。

  • addViteSsrExternal

    向 Vite ssr.external 列表中添加模块

    如果一个包是纯 ESM 包,且未使用别名 (alias) 或定义变量 (define),你应该考虑将它添加至此。

  • addViteSsrNoExternal

    向 Vite ssr.noExternal 列表中添加模块

    如果一个包内使用了别名 (alias) 或定义变量 (define),你必须将它添加至此。

    /**
    + * Add modules to Vite \`optimizeDeps.include\` list
    + */
    +export const addViteOptimizeDepsInclude: (
    +  bundlerOptions: unknown,
    +  app: App,
    +  module: string | string[],
    +) => void
    +
    +/**
    + * Add modules to Vite \`optimizeDeps.exclude\` list
    + */
    +export const addViteOptimizeDepsExclude: (
    +  bundlerOptions: unknown,
    +  app: App,
    +  module: string | string[],
    +) => void
    +
    +/**
    + * Add modules to Vite \`ssr.external\` list
    + */
    +export const addViteSsrExternal: (
    +  bundlerOptions: unknown,
    +  app: App,
    +  module: string | string[],
    +) => void
    +
    +/**
    + * Add modules to Vite \`ssr.noExternal\` list
    + */
    +export const addViteSsrNoExternal: (
    +  bundlerOptions: unknown,
    +  app: App,
    +  module: string | string[],
    +) => void
    +
    示例
    import {
    +  addViteOptimizeDepsInclude,
    +  addViteOptimizeDepsExclude,
    +  addViteSsrExternal,
    +  addViteSsrNoExternal,
    +} from '@vuepress/helper'
    +
    +addViteOptimizeDepsInclude(bundlerOptions, app, ['vue', 'vue-router'])
    +addViteOptimizeDepsExclude(bundlerOptions, app, 'packageA')
    +addViteSsrNoExternal(bundlerOptions, app, ['vue', 'vue-router'])
    +addViteSsrExternal(bundlerOptions, app, 'packageA')
    +
  • addViteConfig

    A function for you to add vite config

    export const addViteConfig: (
    +  bundlerOptions: unknown,
    +  app: App,
    +  config: Record<string, unknown>,
    +) => void
    +
    Example
    import { addViteConfig } from '@vuepress/helper'
    +
    +addViteConfig(bundlerOptions, app, {
    +  build: {
    +    charset: 'utf8',
    +  },
    +})
    +
  • mergeViteConfig

    无需导入 vite 即可合并 vite 配置的功能

    export const mergeViteConfig: (
    +  defaults: Record<string, any>,
    +  overrides: Record<string, any>,
    +) => Record<string, any>
    +

    注意

    你不应将 vite 作为依赖,因为你的的用户可能选择其他打包器!

    示例
    import { mergeViteConfig } from "@vuepress/helper";
    +
    +config.viteOptions mergeViteConfig(config.viteOptions, {
    +  build: {
    +    charset: "utf8",
    +  },
    +});
    +

Webpack 相关

  • chainWebpack

    链式修改 webpack 配置.

    export const chainWebpack: (
    +  { app, config }: WebpackCommonOptions,
    +  chainWebpack: (
    +    config: WebpackChainConfig,
    +    isServer: boolean,
    +    isBuild: boolean,
    +  ) => void,
    +) => void
    +
    示例
    import { chainWebpack } from '@vuepress/helper'
    +
    +chainWebpack(bundlerOptions, app, (config, isServer, isBuild) => {
    +  // do some customize here
    +})
    +
  • configWebpack

    配置 Webpack

    export const configWebpack: (
    +  bundlerOptions: unknown,
    +  app: App,
    +  configureWebpack: (
    +    config: WebpackConfiguration,
    +    isServer: boolean,
    +    isBuild: boolean,
    +  ) => void,
    +) => void
    +
    实例
    import { configWebpack } from '@vuepress/helper'
    +
    +configWebpack(bundlerOptions, app, (config, isServer, isBuild) => {
    +  // do some customize here
    +})
    +
`,21),o=[p];function c(i,r){return n(),a("div",null,o)}const D=s(e,[["render",c],["__file","bundler.html.vue"]]),d=JSON.parse('{"path":"/zh/tools/helper/node/bundler.html","title":"打包器相关","lang":"zh-CN","frontmatter":{},"headers":[{"level":2,"title":"通用方法","slug":"通用方法","link":"#通用方法","children":[{"level":3,"title":"getBundlerName","slug":"getbundlername","link":"#getbundlername","children":[]},{"level":3,"title":"addCustomElement","slug":"addcustomelement","link":"#addcustomelement","children":[]},{"level":3,"title":"customizeDevServer","slug":"customizedevserver","link":"#customizedevserver","children":[]}]},{"level":2,"title":"Vite 相关","slug":"vite-相关","link":"#vite-相关","children":[]},{"level":2,"title":"Webpack 相关","slug":"webpack-相关","link":"#webpack-相关","children":[]}],"git":{"updatedTime":1706960075000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":1}]},"filePathRelative":"zh/tools/helper/node/bundler.md"}');export{D as comp,d as data}; diff --git a/assets/catalog.html-DGD3v_PG.js b/assets/catalog.html-DGD3v_PG.js new file mode 100644 index 0000000000..101d9e6944 --- /dev/null +++ b/assets/catalog.html-DGD3v_PG.js @@ -0,0 +1,93 @@ +import{_ as i,r as l,o as c,c as r,a,b as s,d as n,e}from"./app-DI49vYHA.js";const d={},D=s("h1",{id:"catalog",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#catalog"},[s("span",null,"catalog")])],-1),u=e(`

The plugin can automatically generate catalog pages and provide catalog components.

Usage

npm i -D @vuepress/plugin-catalog@next
+
import { catalogPlugin } from '@vuepress/plugin-catalog'
+
+export default {
+  plugins: [
+    catalogPlugin({
+      // Your options
+    }),
+  ],
+}
+

First, you should set catalog info in routeMeta:

import { catalogPlugin } from '@vuepress/plugin-catalog'
+
+export default {
+  extendsPage: (page) => {
+    // set catalog info in routeMeta
+    page.routeMeta = {
+      // catalog title
+      title: page.title,
+      // ... other information
+    }
+  },
+}
+
`,6),g=s("code",null,"defineCatalogInfoGetter",-1),y=s("code",null,"@vuepress/plugin-catalog/client",-1),v={href:"https://vuejs.press/guide/configuration.html#client-config-file",target:"_blank",rel:"noopener noreferrer"},m=e(`
import { defineCatalogInfoGetter } from '@vuepress/plugin-catalog/client'
+
+export default {
+  setup: () => {
+    defineCatalogInfoGetter((meta) =>
+      meta.title ? { title: meta.title } : null,
+    )
+  },
+}
+

Catalog info should contains:

  • title: catalog title
  • order: catalog order (optional)
  • content: catalog content component (optional)

Sorting with order

The plugin will sort pages by order in the following way:

// order positive numbers from small to large
+Project with order 1
+Project with order 2
+...
+Project with order 10
+...
+// Project without order
+Project without order
+Project without order
+...
+// order negative numbers from small to large
+Project with order -10
+// ...
+Project with order -2
+Project with order -1
+

Options

`,5),C={id:"level",tabindex:"-1"},h={class:"header-anchor",href:"#level"},b=s("ul",null,[s("li",null,[n("Type: "),s("code",null,"1 | 2 | 3")]),s("li",null,[n("Default: "),s("code",null,"3")]),s("li",null,"Details: Max depth of catalog items.")],-1),f={id:"index",tabindex:"-1"},E={class:"header-anchor",href:"#index"},x=e(`
  • Type: boolean
  • Default: false
  • Details: Whether show index for catalog

frontmatter

  • Type: (path: string) => Record<string, any>

  • Required: No

  • Details: Frontmatter getter for the generated page.

  • Example:

    import { catalogPlugin } from '@vuepress/plugin-catalog'
    +
    +export default {
    +  plugins: [
    +    catalogPlugin({
    +      frontmatter: (path) => ({
    +        // frontmatter you want
    +        // you may customize title, author. time, etc.
    +      }),
    +    }),
    +  ],
    +}
    +

exclude

  • Type: (RegExp | string)[]

  • Default: []

  • Details:

    Catalog page path to be excluded during generation.

    • "/foo/" means only exclude catalog page generation at /foo/ folder.
    • /^\\/foo\\// means exclude catalog page generation at /foo/ folder and its subfolders.

    404 pages will be automatically excluded.

component

  • Type: string
  • Required: No
  • Details: Component name to use as catalog.

locales

  • Type: CatalogPluginLocaleConfig

    interface CatalogPluginLocaleData {
    +  /**
    +   * Catalog title
    +   */
    +  title: string
    +
    +  /**
    +   * Empty hint
    +   */
    +  empty: string
    +}
    +
    +interface CatalogPluginLocaleConfig {
    +  [localePath: string]: CatalogPluginLocaleData
    +}
    +
  • Required: No

  • Details: Locales config for catalog component.

Built-in Supported Languages
  • Simplified Chinese (zh-CN)
  • Traditional Chinese (zh-TW)
  • English (United States) (en-US)
  • German (de-DE)
  • German (Australia) (de-AT)
  • Russian (ru-RU)
  • Ukrainian (uk-UA)
  • Vietnamese (vi-VN)
  • Portuguese (Brazil) (pt-BR)
  • Polish (pl-PL)
  • French (fr-FR)
  • Spanish (es-ES)
  • Slovak (sk-SK)
  • Japanese (ja-JP)
  • Turkish (tr-TR)
  • Korean (ko-KR)
  • Finnish (fi-FI)
  • Indonesian (id-ID)
  • Dutch (nl-NL)

Client options

defineCatalogInfoGetter

interface CatalogInfo {
+  /** Catalog title */
+  title: string
+  /** Catalog order */
+  order?: number
+  /** Catalog content */
+  content?: Component
+}
+
+type CatalogInfoGetter = (meta: Record<string, unknown>) => CatalogInfo | null
+
+const defineCatalogInfoGetter: (options: CatalogInfoGetter) => void
+

Customize how to extract catalog info from meta.

Components

Catalog

  • Details:

    The plugin will globally register a <Catalog /> component by default (unless you set the component option).

    You can use <Catalog /> in the theme layout or directly in the Markdown file.

    The component supports four props:

    • level: Change the display depth (maximum support 3 levels), default is 3.
    • base: Display catalog of the specified folder, default is the current folder directory.
    • index: Add an index number to the directory item, default is no number.
    • hideHeading: Hide the component title, default is to display the Catalog title.

Styles

You can customize the style of catalog via CSS variables:

:root {
+  --catalog-bg-color: #fff;
+  --catalog-bg-secondary-color: #f8f8f8;
+  --catalog-border-color: #e5e5e5;
+  --catalog-active-color: #3eaf7c;
+  --catalog-hover-color: #71cda3;
+}
+
`,20);function k(_,F){const t=l("NpmBadge"),p=l("ExternalLinkIcon"),o=l("Badge");return c(),r("div",null,[D,a(t,{package:"@vuepress/plugin-catalog"}),u,s("p",null,[n("You can then import "),g,n(" from "),y,n(" and use it in "),s("a",v,[n("client config file"),a(p)]),n(" to extract catalog info from meta.")]),m,s("h3",C,[s("a",h,[s("span",null,[n("level "),a(o,{text:"Built-in component only"})])])]),b,s("h3",f,[s("a",E,[s("span",null,[n("index "),a(o,{text:"Built-in component only"})])])]),x])}const B=i(d,[["render",k],["__file","catalog.html.vue"]]),P=JSON.parse('{"path":"/plugins/catalog.html","title":"catalog","lang":"en-US","frontmatter":{},"headers":[{"level":2,"title":"Usage","slug":"usage","link":"#usage","children":[]},{"level":2,"title":"Options","slug":"options","link":"#options","children":[{"level":3,"title":"level","slug":"level","link":"#level","children":[]},{"level":3,"title":"index","slug":"index","link":"#index","children":[]},{"level":3,"title":"frontmatter","slug":"frontmatter","link":"#frontmatter","children":[]},{"level":3,"title":"exclude","slug":"exclude","link":"#exclude","children":[]},{"level":3,"title":"component","slug":"component","link":"#component","children":[]},{"level":3,"title":"locales","slug":"locales","link":"#locales","children":[]}]},{"level":2,"title":"Client options","slug":"client-options","link":"#client-options","children":[{"level":3,"title":"defineCatalogInfoGetter","slug":"definecataloginfogetter","link":"#definecataloginfogetter","children":[]}]},{"level":2,"title":"Components","slug":"components","link":"#components","children":[{"level":3,"title":"Catalog","slug":"catalog-1","link":"#catalog-1","children":[]}]},{"level":2,"title":"Styles","slug":"styles","link":"#styles","children":[]}],"git":{"updatedTime":1708274662000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":3}]},"filePathRelative":"plugins/catalog.md"}');export{B as comp,P as data}; diff --git a/assets/catalog.html-ZgM6JxDU.js b/assets/catalog.html-ZgM6JxDU.js new file mode 100644 index 0000000000..7d8596ac01 --- /dev/null +++ b/assets/catalog.html-ZgM6JxDU.js @@ -0,0 +1,93 @@ +import{_ as c,r as l,o as t,c as r,a,b as s,d as n,e}from"./app-DI49vYHA.js";const d={},D=s("h1",{id:"catalog",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#catalog"},[s("span",null,"catalog")])],-1),u=e(`

此插件可以自动生成目录页面,也提供目录组件。

使用方法

npm i -D @vuepress/plugin-catalog@next
+
import { catalogPlugin } from '@vuepress/plugin-catalog'
+
+export default {
+  plugins: [
+    catalogPlugin({
+      // 你的选项
+    }),
+  ],
+}
+

首先,你应该在路由元信息中设置目录信息:

import { catalogPlugin } from '@vuepress/plugin-catalog'
+
+export default {
+  extendsPage: (page) => {
+    // 在 routeMeta 中设置目录信息
+    page.routeMeta = {
+      // 目录标题
+      title: page.title,
+      // ... 其他信息
+    }
+  },
+}
+
`,6),v=s("code",null,"defineCatalogInfoGetter",-1),y={href:"https://vuejs.press/zh/guide/configuration.html#%E5%AE%A2%E6%88%B7%E7%AB%AF%E9%85%8D%E7%BD%AE%E6%96%87%E4%BB%B6",target:"_blank",rel:"noopener noreferrer"},g=e(`
import { defineCatalogInfoGetter } from '@vuepress/plugin-catalog/client'
+
+export default {
+  setup: () => {
+    defineCatalogInfoGetter((meta) =>
+      meta.title ? { title: meta.title } : null,
+    )
+  },
+}
+

目录信息应包含:

  • title: 目录标题
  • order: 目录顺序 (可选)
  • content: 目录内容组件 (可选)

通过 order 排序

插件将按以下方式通过 order 对页面进行排序:

// 从小到大依次排列正数
+order 1 的项目
+order 2 的项目
+...
+order 10 的项目
+...
+// 无 order 的项目
+无 order 的项目
+无 order 的项目
+...
+// 从小到大依次排列负数
+order -10 的项目
+// ...
+order -2 的项目
+order -1 的项目
+

选项

`,5),C={id:"level",tabindex:"-1"},m={class:"header-anchor",href:"#level"},b=s("ul",null,[s("li",null,[n("类型:"),s("code",null,"1 | 2 | 3")]),s("li",null,[n("默认值:"),s("code",null,"3")]),s("li",null,"详情:目录项级别的最大深度。")],-1),h={id:"index",tabindex:"-1"},E={class:"header-anchor",href:"#index"},f=e(`
  • 类型:boolean
  • 默认值:false
  • 详情:目录是否显示索引

frontmatter

  • 类型:(path: string) => Record<string, any>

  • 详情:生成页面的 Frontmatter 获取器。

  • 示例:

    import { catalogPlugin } from '@vuepress/plugin-catalog'
    +
    +export default {
    +  plugins: [
    +    catalogPlugin({
    +      frontmatter: (path) => ({
    +        // 你想要的 frontmatter
    +        // 你可以自定义标题、作者、时间等
    +      }),
    +    }),
    +  ],
    +}
    +

exclude

  • 类型:(RegExp | string)[]

  • 默认值:[]

  • 详情:

    生成中需要排除的目录页路径。

    • "/foo/" 意味着仅排除 /foo/ 文件夹的目录页生成。
    • /^\\/foo\\// 意味着排除 /foo/ 文件夹及其子文件夹的目录页生成。

    404 页面会被自动排除。

component

  • 类型:string
  • 详情:用作目录的组件名称。

locales

  • 类型:CatalogPluginLocaleConfig

    interface CatalogPluginLocaleData {
    +  /**
    +   * 目录标题
    +   */
    +  title: string
    +
    +  /**
    +   * 空目录提示
    +   */
    +  empty: string
    +}
    +
    +interface CatalogPluginLocaleConfig {
    +  [localePath: string]: CatalogPluginLocaleData
    +}
    +
  • 必填: 否

  • 详情:目录组件国际化配置。

内置支持语言
  • 简体中文 (zh-CN)
  • 繁体中文 (zh-TW)
  • 英文(美国) (en-US)
  • 德语 (de-DE)
  • 德语(澳大利亚) (de-AT)
  • 俄语 (ru-RU)
  • 乌克兰语 (uk-UA)
  • 越南语 (vi-VN)
  • 葡萄牙语(巴西) (pt-BR)
  • 波兰语 (pl-PL)
  • 法语 (fr-FR)
  • 西班牙语 (es-ES)
  • 斯洛伐克 (sk-SK)
  • 日语 (ja-JP)
  • 土耳其语 (tr-TR)
  • 韩语 (ko-KR)
  • 芬兰语 (fi-FI)
  • 印尼语 (id-ID)
  • 荷兰语 (nl-NL)
  • 印尼语 (id-ID)
  • 荷兰语 (nl-NL)

客户端选项

defineCatalogInfoGetter

interface CatalogInfo {
+  /** 目录标题 */
+  title: string
+  /** 目录顺序 */
+  order?: number
+  /** 目录内容 */
+  content?: Component
+}
+
+type CatalogInfoGetter = (meta: Record<string, unknown>) => CatalogInfo | null
+
+const defineCatalogInfoGetter: (options: CatalogInfoGetter) => void
+

自定义如何从 meta 中提取目录信息。

组件

Catalog

  • 详情:

    该插件默认会全局注册一个 <Catalog /> 组件(除非你设置了 component 选项)。

    你可以在主题布局中或直接在 Markdown 文件中使用 <Catalog />

    组件支持四个属性:

    • level:更改显示层次深度(最大仅支持 3 层),默认为 3
    • base:显示指定文件夹的目录,默认显示当前文件夹目录。
    • index:为目录项添加索引号,默认无标号。
    • hideHeading:隐藏组件标题,默认会显示 目录 标题。

样式

你可以通过 CSS 变量来自定义目录样式:

:root {
+  --catalog-bg-color: #fff;
+  --catalog-bg-secondary-color: #f8f8f8;
+  --catalog-border-color: #e5e5e5;
+  --catalog-active-color: #3eaf7c;
+  --catalog-hover-color: #71cda3;
+}
+
`,20);function x(k,_){const p=l("NpmBadge"),i=l("ExternalLinkIcon"),o=l("Badge");return t(),r("div",null,[D,a(p,{package:"@vuepress/plugin-catalog"}),u,s("p",null,[n("你可以之后导入 "),v,n(" 并在 "),s("a",y,[n("客户端配置文件"),a(i)]),n(" 中使用它来从元信息中提取目录信息。")]),g,s("h3",C,[s("a",m,[s("span",null,[n("level "),a(o,{text:"仅限内置组件"})])])]),b,s("h3",h,[s("a",E,[s("span",null,[n("index "),a(o,{text:"仅限内置组件"})])])]),f])}const F=c(d,[["render",x],["__file","catalog.html.vue"]]),B=JSON.parse('{"path":"/zh/plugins/catalog.html","title":"catalog","lang":"zh-CN","frontmatter":{},"headers":[{"level":2,"title":"使用方法","slug":"使用方法","link":"#使用方法","children":[]},{"level":2,"title":"选项","slug":"选项","link":"#选项","children":[{"level":3,"title":"level","slug":"level","link":"#level","children":[]},{"level":3,"title":"index","slug":"index","link":"#index","children":[]},{"level":3,"title":"frontmatter","slug":"frontmatter","link":"#frontmatter","children":[]},{"level":3,"title":"exclude","slug":"exclude","link":"#exclude","children":[]},{"level":3,"title":"component","slug":"component","link":"#component","children":[]},{"level":3,"title":"locales","slug":"locales","link":"#locales","children":[]}]},{"level":2,"title":"客户端选项","slug":"客户端选项","link":"#客户端选项","children":[{"level":3,"title":"defineCatalogInfoGetter","slug":"definecataloginfogetter","link":"#definecataloginfogetter","children":[]}]},{"level":2,"title":"组件","slug":"组件","link":"#组件","children":[{"level":3,"title":"Catalog","slug":"catalog-1","link":"#catalog-1","children":[]}]},{"level":2,"title":"样式","slug":"样式","link":"#样式","children":[]}],"git":{"updatedTime":1708274662000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":3}]},"filePathRelative":"zh/plugins/catalog.md"}');export{F as comp,B as data}; diff --git a/assets/channel.html-CzU5ykfZ.js b/assets/channel.html-CzU5ykfZ.js new file mode 100644 index 0000000000..148d624157 --- /dev/null +++ b/assets/channel.html-CzU5ykfZ.js @@ -0,0 +1,15 @@ +import{_ as a,r as s,o as i,c,b as n,d as e,a as t,e as o}from"./app-DI49vYHA.js";const h={},r=o(`

频道设置

channel 插件选项用于配置 feed 的频道。

channel.title

  • 类型:string
  • 默认值:SiteConfig.title

频道的标题

  • 类型:string
  • 默认值:部署的网址 (通过 options.hostnamecontext.base 生成)

频道地址

channel.description

  • 类型:string
  • 默认值:SiteConfig.description

频道描述信息

channel.language

  • 类型:string
  • 默认值:
    • siteConfig.locales['/'].locales
    • 如果上述未提供,回退到 "en-US"

频道使用的语言

  • 类型:string
  • 默认值:
    • 尝试读取 channel 选项中的 author.name 生成 Copyright by $author
  • 建议自行设置:

频道版权信息

channel.pubDate

  • 类型:string (需是合法的 Date ISOString)
  • 默认值:每次插件构建时刻
  • 建议自行设置:

频道内容的发布时间

channel.lastUpdated

  • 类型:string (需是合法的 Date ISOString)
  • 默认值:每次插件构建时刻

频道内容的上次更新时间

channel.ttl

  • 类型:number
  • 建议自行设置:

内容有效时间,即获取后保持缓存而不进行新获取的时间

channel.image

  • 类型:string
  • 建议自行设置:

这是一个会在频道中使用的图片,建议设置正方形图片、尺寸最好不小于 512×512。

channel.icon

  • 类型:string
  • 建议自行设置:

一个代表频道的图标,建议设置正方形图片、尺寸最好不小于 128×128,背景色透明。

channel.author

  • 类型:FeedAuthor
  • 建议自行设置:

频道的作者。

FeedAuthor 格式
interface FeedAuthor {
+  /** 作者姓名 */
+  name: string
+  /** 作者电子邮箱 */
+  email?: string
+  /** 作者网站 */
+  url?: string
+  /**
+   * 作者头像地址
+   *
+   * 正方形,最好不小于 128×128,透明背景
+   */
+  avatar?: string
+}
+

channel.hub

  • 类型:string

Websub 的链接。Websub 需要服务器后端,与 VuePress 主旨不符,如无特殊需要忽略即可。

`,39),d={class:"custom-container tip"},p=n("p",{class:"custom-container-title"},"WebSub",-1),u={href:"https://w3c.github.io/websub/#subscription-migration",target:"_blank",rel:"noopener noreferrer"};function g(b,m){const l=s("ExternalLinkIcon");return i(),c("div",null,[r,n("div",d,[p,n("p",null,[e("有关信息,详见 "),n("a",u,[e("Websub"),t(l)]),e("。")])])])}const y=a(h,[["render",g],["__file","channel.html.vue"]]),f=JSON.parse('{"path":"/zh/plugins/feed/channel.html","title":"频道设置","lang":"zh-CN","frontmatter":{},"headers":[{"level":2,"title":"channel.title","slug":"channel-title","link":"#channel-title","children":[]},{"level":2,"title":"channel.link","slug":"channel-link","link":"#channel-link","children":[]},{"level":2,"title":"channel.description","slug":"channel-description","link":"#channel-description","children":[]},{"level":2,"title":"channel.language","slug":"channel-language","link":"#channel-language","children":[]},{"level":2,"title":"channel.copyright","slug":"channel-copyright","link":"#channel-copyright","children":[]},{"level":2,"title":"channel.pubDate","slug":"channel-pubdate","link":"#channel-pubdate","children":[]},{"level":2,"title":"channel.lastUpdated","slug":"channel-lastupdated","link":"#channel-lastupdated","children":[]},{"level":2,"title":"channel.ttl","slug":"channel-ttl","link":"#channel-ttl","children":[]},{"level":2,"title":"channel.image","slug":"channel-image","link":"#channel-image","children":[]},{"level":2,"title":"channel.icon","slug":"channel-icon","link":"#channel-icon","children":[]},{"level":2,"title":"channel.author","slug":"channel-author","link":"#channel-author","children":[]},{"level":2,"title":"channel.hub","slug":"channel-hub","link":"#channel-hub","children":[]}],"git":{"updatedTime":1706801200000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":3}]},"filePathRelative":"zh/plugins/feed/channel.md"}');export{y as comp,f as data}; diff --git a/assets/channel.html-MBD5TiIP.js b/assets/channel.html-MBD5TiIP.js new file mode 100644 index 0000000000..8d40a177c2 --- /dev/null +++ b/assets/channel.html-MBD5TiIP.js @@ -0,0 +1,15 @@ +import{_ as s,r as i,o as t,c,b as e,d as n,a as o,e as a}from"./app-DI49vYHA.js";const h={},r=a('

Channel Config

The channel plugin option is used to config the feed channel.

channel.title

  • Type: string
  • Default: SiteConfig.title

Channel title

  • Type: string
  • Default: Deployment link (generated by options.hostname and context.base)

Channel address

channel.description

  • Type: string
  • Default: SiteConfig.description

Channel description

channel.language

  • Type: string

  • Default:

    • siteConfig.locales['/'].lang
    • If the above is not provided, fall back to "en-US"

The language of the channel

  • Type: string

  • Default:

    • Try to read the author.name in channel options, and fall back to Copyright by $author
  • Recommended to set manually: Yes

Channel copyright information

channel.pubDate

  • Type: string (must be a valid Date ISOString)
  • Default: time when the plugin is called each time
  • Recommended to set manually: Yes

Publish date of the Channel

channel.lastUpdated

  • Type: string (must be a valid Date ISOString)
  • Default: time when the plugin is called each time

Last update time of channel content

channel.ttl

  • Type: number
  • Recommended to set manually: Yes

The effective time of the content. It's the time to keep the cache after request without making new requests.

channel.image

  • Type: string
  • Recommended to set manually: Yes

A picture presenting the channel. A square picture with a size not smaller than 512×512 is recommended.

channel.icon

  • Type: string
  • Recommended to set manually: Yes

An icon representing a channel, a square picture, with not less than 128×128 in size, and transparent background color is recommended.

channel.author

  • Type: FeedAuthor
  • Recommended to set manually: Yes

The author of the channel.

',35),p={class:"custom-container details"},d=a(`FeedAuthor format
interface FeedAuthor {
+  /** Author name */
+  name: string
+  /** Author's email */
+  email?: string
+  /** Author's site */
+  url?: string
+  /**
+   * Author's avatar address
+   *
+   * Square, preferably not less than 128×128 with transparent background
+   */
+  avatar?: string
+}
+

channel.hub

  • Type: string

Link to Websub. Websub requires a server backend, which is inconsistent with VuePress, so ignore it if there is no special need.

`,5),u={class:"custom-container tip"},g=e("p",{class:"custom-container-title"},"WebSub",-1),m={href:"https://w3c.github.io/websub/#subscription-migration",target:"_blank",rel:"noopener noreferrer"};function b(y,f){const l=i("ExternalLinkIcon");return t(),c("div",null,[r,e("details",p,[d,e("div",u,[g,e("p",null,[n("For details, see "),e("a",m,[n("Websub"),o(l)]),n(".")])])])])}const D=s(h,[["render",b],["__file","channel.html.vue"]]),k=JSON.parse('{"path":"/plugins/feed/channel.html","title":"Channel Config","lang":"en-US","frontmatter":{},"headers":[{"level":2,"title":"channel.title","slug":"channel-title","link":"#channel-title","children":[]},{"level":2,"title":"channel.link","slug":"channel-link","link":"#channel-link","children":[]},{"level":2,"title":"channel.description","slug":"channel-description","link":"#channel-description","children":[]},{"level":2,"title":"channel.language","slug":"channel-language","link":"#channel-language","children":[]},{"level":2,"title":"channel.copyright","slug":"channel-copyright","link":"#channel-copyright","children":[]},{"level":2,"title":"channel.pubDate","slug":"channel-pubdate","link":"#channel-pubdate","children":[]},{"level":2,"title":"channel.lastUpdated","slug":"channel-lastupdated","link":"#channel-lastupdated","children":[]},{"level":2,"title":"channel.ttl","slug":"channel-ttl","link":"#channel-ttl","children":[]},{"level":2,"title":"channel.image","slug":"channel-image","link":"#channel-image","children":[]},{"level":2,"title":"channel.icon","slug":"channel-icon","link":"#channel-icon","children":[]},{"level":2,"title":"channel.author","slug":"channel-author","link":"#channel-author","children":[]}],"git":{"updatedTime":1706762763000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":2}]},"filePathRelative":"plugins/feed/channel.md"}');export{D as comp,k as data}; diff --git a/assets/client.html-DnXt2EtS.js b/assets/client.html-DnXt2EtS.js new file mode 100644 index 0000000000..01759345fd --- /dev/null +++ b/assets/client.html-DnXt2EtS.js @@ -0,0 +1,22 @@ +import{_ as s,o as a,c as n,e as l}from"./app-DI49vYHA.js";const e={},o=l(`

客户端相关

可组合 API

hasGlobalComponent

检查组件是否已全局注册。

提示

  1. 组件的局部导入不影响结果。
  2. 当在 setup 之外调用时,你需要将 app 实例作为第二个参数传递。
export const hasGlobalComponent: (name: string, app?: App) => boolean
+
示例
// 如果你全局注册了 \`<my-component>\`
+hasGlobalComponent('MyComponent') // true
+hasGlobalComponent('my-component') // true
+
+hasGlobalComponent('MyComponent2') // false
+

useLocaleConfig

从语言环境设置中获取当前语言环境配置。

export const useLocaleConfig: <T extends LocaleData>(
+  localesConfig: RequiredLocaleConfig<T>,
+) => ComputedRef<T>
+
示例
const localesCOnfig = {
+  '/': 'Title',
+  '/zh/': '标题',
+}
+
+const locale = useLocaleConfig(localesConfig)
+
+// under \`/page\`
+locale.value // 'Title'
+
+// under \`/zh/page\`
+locale.value // '标题'
+
`,11),p=[o];function c(t,i){return a(),n("div",null,p)}const d=s(e,[["render",c],["__file","client.html.vue"]]),D=JSON.parse('{"path":"/zh/tools/helper/client.html","title":"客户端相关","lang":"zh-CN","frontmatter":{},"headers":[{"level":2,"title":"可组合 API","slug":"可组合-api","link":"#可组合-api","children":[{"level":3,"title":"hasGlobalComponent","slug":"hasglobalcomponent","link":"#hasglobalcomponent","children":[]},{"level":3,"title":"useLocaleConfig","slug":"uselocaleconfig","link":"#uselocaleconfig","children":[]}]}],"git":{"updatedTime":1706960075000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":1}]},"filePathRelative":"zh/tools/helper/client.md"}');export{d as comp,D as data}; diff --git a/assets/client.html-GE5WwRt5.js b/assets/client.html-GE5WwRt5.js new file mode 100644 index 0000000000..6e21e2c8b3 --- /dev/null +++ b/assets/client.html-GE5WwRt5.js @@ -0,0 +1,22 @@ +import{_ as s,o as a,c as n,e as l}from"./app-DI49vYHA.js";const e={},o=l(`

Client Related

Composables APIs

hasGlobalComponent

Check whether a component is registered globally.

TIP

  1. Local import of the component does not affect the result.
  2. When calling outside setup scope, you need to pass the app instance as the second parameter.
export const hasGlobalComponent: (name: string, app?: App) => boolean
+
Example
// if you globally register \`<my-component>\`
+hasGlobalComponent('MyComponent') // true
+hasGlobalComponent('my-component') // true
+
+hasGlobalComponent('MyComponent2') // false
+

useLocaleConfig

Get current locale config from locales settings.

export const useLocaleConfig: <T extends LocaleData>(
+  localesConfig: RequiredLocaleConfig<T>,
+) => ComputedRef<T>
+
Example
const localesCOnfig = {
+  '/': 'Title',
+  '/zh/': '标题',
+}
+
+const locale = useLocaleConfig(localesConfig)
+
+// under \`/page\`
+locale.value // 'Title'
+
+// under \`/zh/page\`
+locale.value // '标题'
+
`,11),p=[o];function t(c,i){return a(),n("div",null,p)}const d=s(e,[["render",t],["__file","client.html.vue"]]),D=JSON.parse('{"path":"/tools/helper/client.html","title":"Client Related","lang":"en-US","frontmatter":{},"headers":[{"level":2,"title":"Composables APIs","slug":"composables-apis","link":"#composables-apis","children":[{"level":3,"title":"hasGlobalComponent","slug":"hasglobalcomponent","link":"#hasglobalcomponent","children":[]},{"level":3,"title":"useLocaleConfig","slug":"uselocaleconfig","link":"#uselocaleconfig","children":[]}]}],"git":{"updatedTime":1706960075000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":1}]},"filePathRelative":"tools/helper/client.md"}');export{d as comp,D as data}; diff --git a/assets/components.html-BLsJeb2o.js b/assets/components.html-BLsJeb2o.js new file mode 100644 index 0000000000..9be691ebce --- /dev/null +++ b/assets/components.html-BLsJeb2o.js @@ -0,0 +1,32 @@ +import{_ as u,r as l,o as m,c as D,a as e,b as s,d as n,w as a,e as p}from"./app-DI49vYHA.js";const y={},v=s("h1",{id:"built-in-components",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#built-in-components"},[s("span",null,"Built-in Components")])],-1),C={id:"badge",tabindex:"-1"},h={class:"header-anchor",href:"#badge"},b=p(`
  • Props:

    • type
      • Type: 'tip' | 'warning' | 'danger'
      • Default: 'tip'
    • text
      • Type: string
      • Default: ''
    • vertical
      • Type: 'top' | 'middle' | 'bottom' | undefined
      • Default: undefined
  • Example:

Input

- VuePress - <Badge type="tip" text="v2" vertical="top" />
+- VuePress - <Badge type="warning" text="v2" vertical="middle" />
+- VuePress - <Badge type="danger" text="v2" vertical="bottom" />
+

Output

`,4),g=p(`

CodeGroup

CodeGroupItem

  • Props:

    • title
      • Type: string
      • Required: true
    • active
      • Type: boolean
      • Default: false
  • Details:

    This component must be placed inside a CodeGroup component.

    Use the active prop to set the initial active item, or the first item will be activated by default.

  • Example:

Input

<CodeGroup>
+  <CodeGroupItem title="pnpm">
+
+\`\`\`bash:no-line-numbers
+pnpm install
+\`\`\`
+
+  </CodeGroupItem>
+
+  <CodeGroupItem title="yarn">
+
+\`\`\`bash:no-line-numbers
+yarn install
+\`\`\`
+
+  </CodeGroupItem>
+
+  <CodeGroupItem title="npm" active>
+
+\`\`\`bash:no-line-numbers
+npm install
+\`\`\`
+
+  </CodeGroupItem>
+</CodeGroup>
+

Output

`,7),_=s("div",{class:"language-bash","data-ext":"sh","data-title":"sh"},[s("pre",{class:"shiki dark-plus",style:{"background-color":"#1E1E1E",color:"#D4D4D4"},tabindex:"0"},[s("code",null,[s("span",{class:"line"},[s("span",{style:{color:"#DCDCAA"}},"pnpm"),s("span",{style:{color:"#CE9178"}}," install")]),n(` +`),s("span",{class:"line"})])])],-1),E=s("div",{class:"language-bash","data-ext":"sh","data-title":"sh"},[s("pre",{class:"shiki dark-plus",style:{"background-color":"#1E1E1E",color:"#D4D4D4"},tabindex:"0"},[s("code",null,[s("span",{class:"line"},[s("span",{style:{color:"#DCDCAA"}},"yarn"),s("span",{style:{color:"#CE9178"}}," install")]),n(` +`),s("span",{class:"line"})])])],-1),f=s("div",{class:"language-bash","data-ext":"sh","data-title":"sh"},[s("pre",{class:"shiki dark-plus",style:{"background-color":"#1E1E1E",color:"#D4D4D4"},tabindex:"0"},[s("code",null,[s("span",{class:"line"},[s("span",{style:{color:"#DCDCAA"}},"npm"),s("span",{style:{color:"#CE9178"}}," install")]),n(` +`),s("span",{class:"line"})])])],-1),k={class:"custom-container warning"},x=s("p",{class:"custom-container-title"},"WARNING",-1),q=s("p",null,[n("You must add an empty line between the starting tag of "),s("code",null,""),n(" and the code fence, otherwise the code fence will not be parsed correctly by Markdown.")],-1),G=s("p",null,"All content must be valid Markdown first, and then a Vue SFC.",-1),w={href:"https://v2.vuepress.vuejs.org/advanced/cookbook/markdown-and-vue-sfc.html",target:"_blank",rel:"noopener noreferrer"};function I(F,B){const i=l("NpmBadge"),o=l("Badge"),t=l("CodeGroupItem"),c=l("CodeGroup"),r=l("ExternalLinkIcon"),d=l("RouteLink");return m(),D("div",null,[v,e(i,{package:"@vuepress/theme-default"}),s("h2",C,[s("a",h,[s("span",null,[n("Badge "),e(o,{text:"badge"})])])]),b,s("ul",null,[s("li",null,[n("VuePress - "),e(o,{type:"tip",text:"v2",vertical:"top"})]),s("li",null,[n("VuePress - "),e(o,{type:"warning",text:"v2",vertical:"middle"})]),s("li",null,[n("VuePress - "),e(o,{type:"danger",text:"v2",vertical:"bottom"})])]),g,e(c,null,{default:a(()=>[e(t,{title:"pnpm"},{default:a(()=>[_]),_:1}),e(t,{title:"yarn"},{default:a(()=>[E]),_:1}),e(t,{title:"npm",active:""},{default:a(()=>[f]),_:1})]),_:1}),s("div",k,[x,q,G,s("p",null,[n("Learn more: "),s("a",w,[n("Cookbook > Markdown and Vue SFC"),e(r)])]),s("p",null,[n("Alternatively, you can use the "),e(d,{to:"/themes/default/markdown.html#custom-containers"},{default:a(()=>[n("custom containers")]),_:1}),n(".")])])])}const A=u(y,[["render",I],["__file","components.html.vue"]]),N=JSON.parse('{"path":"/themes/default/components.html","title":"Built-in Components","lang":"en-US","frontmatter":{},"headers":[{"level":2,"title":"Badge","slug":"badge","link":"#badge","children":[]},{"level":2,"title":"CodeGroup","slug":"codegroup","link":"#codegroup","children":[]},{"level":2,"title":"CodeGroupItem","slug":"codegroupitem","link":"#codegroupitem","children":[]}],"git":{"updatedTime":1706625181000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":2}]},"filePathRelative":"themes/default/components.md"}');export{A as comp,N as data}; diff --git a/assets/components.html-D_m26OMv.js b/assets/components.html-D_m26OMv.js new file mode 100644 index 0000000000..b0300d6bd0 --- /dev/null +++ b/assets/components.html-D_m26OMv.js @@ -0,0 +1,32 @@ +import{_ as u,r as e,o as D,c as m,a as l,b as s,d as n,w as a,e as p}from"./app-DI49vYHA.js";const y={},v=s("h1",{id:"内置组件",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#内置组件"},[s("span",null,"内置组件")])],-1),C={id:"badge",tabindex:"-1"},h={class:"header-anchor",href:"#badge"},g=p(`
  • Props:

    • type
      • 类型: 'tip' | 'warning' | 'danger'
      • 默认值: 'tip'
    • text
      • 类型: string
      • 默认值: ''
    • vertical
      • 类型: 'top' | 'middle' | 'bottom' | undefined
      • 默认值: undefined
  • 示例:

输入

- VuePress - <Badge type="tip" text="v2" vertical="top" />
+- VuePress - <Badge type="warning" text="v2" vertical="middle" />
+- VuePress - <Badge type="danger" text="v2" vertical="bottom" />
+

输出

`,4),b=p(`

CodeGroup

CodeGroupItem

  • Props:

    • title
      • 类型: string
      • 是否必需: true
    • active
      • 类型: boolean
      • 默认值: false
  • 详情:

    该组件必须放置在 CodeGroup 组件的内部。

    可以通过 active Prop 来设置初始激活的元素。如果不设置,默认激活第一个元素。

  • 示例:

输入

<CodeGroup>
+  <CodeGroupItem title="pnpm">
+
+\`\`\`bash:no-line-numbers
+pnpm install
+\`\`\`
+
+  </CodeGroupItem>
+
+  <CodeGroupItem title="yarn">
+
+\`\`\`bash:no-line-numbers
+yarn install
+\`\`\`
+
+  </CodeGroupItem>
+
+  <CodeGroupItem title="npm" active>
+
+\`\`\`bash:no-line-numbers
+npm install
+\`\`\`
+
+  </CodeGroupItem>
+</CodeGroup>
+

输出

`,7),E=s("div",{class:"language-bash","data-ext":"sh","data-title":"sh"},[s("pre",{class:"shiki dark-plus",style:{"background-color":"#1E1E1E",color:"#D4D4D4"},tabindex:"0"},[s("code",null,[s("span",{class:"line"},[s("span",{style:{color:"#DCDCAA"}},"pnpm"),s("span",{style:{color:"#CE9178"}}," install")]),n(` +`),s("span",{class:"line"})])])],-1),_=s("div",{class:"language-bash","data-ext":"sh","data-title":"sh"},[s("pre",{class:"shiki dark-plus",style:{"background-color":"#1E1E1E",color:"#D4D4D4"},tabindex:"0"},[s("code",null,[s("span",{class:"line"},[s("span",{style:{color:"#DCDCAA"}},"yarn"),s("span",{style:{color:"#CE9178"}}," install")]),n(` +`),s("span",{class:"line"})])])],-1),k=s("div",{class:"language-bash","data-ext":"sh","data-title":"sh"},[s("pre",{class:"shiki dark-plus",style:{"background-color":"#1E1E1E",color:"#D4D4D4"},tabindex:"0"},[s("code",null,[s("span",{class:"line"},[s("span",{style:{color:"#DCDCAA"}},"npm"),s("span",{style:{color:"#CE9178"}}," install")]),n(` +`),s("span",{class:"line"})])])],-1),f={class:"custom-container warning"},x=s("p",{class:"custom-container-title"},"注意",-1),q=s("p",null,[n("你必须在 "),s("code",null,""),n(" 的开始标签和代码块之间添加一个空行,否则代码块无法被 Markdown 正确解析。")],-1),G=s("p",null,"所有内容首先都必须是合法的 Markdown ,然后才是一个 Vue SFC 。",-1),F={href:"https://v2.vuepress.vuejs.org/zh/advanced/cookbook/markdown-and-vue-sfc.html",target:"_blank",rel:"noopener noreferrer"};function B(I,w){const c=e("NpmBadge"),o=e("Badge"),t=e("CodeGroupItem"),i=e("CodeGroup"),r=e("ExternalLinkIcon"),d=e("RouteLink");return D(),m("div",null,[v,l(c,{package:"@vuepress/theme-default"}),s("h2",C,[s("a",h,[s("span",null,[n("Badge "),l(o,{text:"badge"})])])]),g,s("ul",null,[s("li",null,[n("VuePress - "),l(o,{type:"tip",text:"v2",vertical:"top"})]),s("li",null,[n("VuePress - "),l(o,{type:"warning",text:"v2",vertical:"middle"})]),s("li",null,[n("VuePress - "),l(o,{type:"danger",text:"v2",vertical:"bottom"})])]),b,l(i,null,{default:a(()=>[l(t,{title:"pnpm"},{default:a(()=>[E]),_:1}),l(t,{title:"yarn"},{default:a(()=>[_]),_:1}),l(t,{title:"npm",active:""},{default:a(()=>[k]),_:1})]),_:1}),s("div",f,[x,q,G,s("p",null,[n("了解更多: "),s("a",F,[n("Cookbook > Markdown 与 Vue SFC"),l(r)])]),s("p",null,[n("或者你可以选择使用 "),l(d,{to:"/zh/themes/default/markdown.html#%E8%87%AA%E5%AE%9A%E4%B9%89%E5%AE%B9%E5%99%A8"},{default:a(()=>[n("自定义容器")]),_:1}),n(" 。")])])])}const V=u(y,[["render",B],["__file","components.html.vue"]]),P=JSON.parse('{"path":"/zh/themes/default/components.html","title":"内置组件","lang":"zh-CN","frontmatter":{},"headers":[{"level":2,"title":"Badge","slug":"badge","link":"#badge","children":[]},{"level":2,"title":"CodeGroup","slug":"codegroup","link":"#codegroup","children":[]},{"level":2,"title":"CodeGroupItem","slug":"codegroupitem","link":"#codegroupitem","children":[]}],"git":{"updatedTime":1706625181000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":2}]},"filePathRelative":"zh/themes/default/components.md"}');export{V as comp,P as data}; diff --git a/assets/config.html-BLNioz4j.js b/assets/config.html-BLNioz4j.js new file mode 100644 index 0000000000..6488d17095 --- /dev/null +++ b/assets/config.html-BLNioz4j.js @@ -0,0 +1,140 @@ +import{_ as o,r as p,o as i,c,b as s,d as n,a as e,e as l}from"./app-DI49vYHA.js";const r={},t=l('

配置

选项

showInstall

  • 类型:boolean

  • 默认值:false

  • 详情:

    是否在 Service Worker 首次成功注册时显示 PWA 安装按钮

manifest

',5),d=l('
  • 类型:ManifestOption

  • 详情:

    填充一个将被解析为 manifest.webmanifest 的对象。

    提示

    如果未设置某些选项,它们会回退到插件预设值。

    • name: siteConfig.title || siteConfig.locales['/'].title || "Site"
    • short_name: siteConfig.title || siteConfig.locales['/'].title || "Site"
    • description: siteConfig.description || siteConfig.locales['/'].description || "A site built with vuepress"
    • lang: siteConfig.locales['/'].lang || "en-US"
    • start_url: context.base
    • scope: context.base
    • display: "standalone"
    • theme_color: "#46bd87"
    • background_color: "#ffffff"
    • orientation: "portrait-primary"
    • prefer_related_applications: false
  • ',2),D=s("p",null,"参考:",-1),u={href:"https://developer.mozilla.org/zh-CN/docs/Web/Manifest",target:"_blank",rel:"noopener noreferrer"},v={href:"https://w3c.github.io/manifest/",target:"_blank",rel:"noopener noreferrer"},y=l('

    favicon

    • 类型:string

    • 详情:

      favicon.ico 地址,填入绝对路径。

      注意

      我们建议你为你的站点生成 favicon。

    themeColor

    • 类型:string
    • 默认值:"#46bd87"
    • 详情:PWA 的主题色。

    cacheHTML

    • 类型:boolean
    • 默认值:false
    • 详情:是否缓存主页和 404 错误页之外的 HTML 文件

    cacheImage

    • 类型:boolean
    • 默认值:false
    • 详情:是否缓存图片。

    maxSize

    • 类型:number

    • 默认值:2048

    • 详情:

      允许缓存的最大大小 (以 KB 为单位)

      注意

      此选项具有最高优先级,任何超过此值的文件都会被排除。

      所以你如果生成了很大的 HTML 或 JS 文件,请考虑调高此值,否则你的 PWA 可能无法在离线模式下正常运行。

    maxImageSize

    • 类型:number

    • 默认值:1024

    • 详情:

      图片允许缓存的最大大小 (以 KB 为单位)

      该选项不能大于 maxSize 选项

    update

    • 类型:"disabled" | "available" | "hint" | "force"

    • 默认值:"available"

    • 详情:

      发现新内容时的控制逻辑。

      • "disabled": 即使有新的 service worker 也不做任何事情,新的 service work 开始等待后,会在用户下次访问时接管页面,让用户获得新内容。

      • "available": 仅当新的 service worker 可用时才显示更新弹出窗口

      • "hint": 显示更新内容可用提示,并允许用户立即刷新。当新的 SW 成功注册后,将转为更新内容就绪弹窗。

        当你希望用户立即查看新文档时,这很有帮助。

        提示

        如果用户在新 SW 就绪前选择刷新,当前的 Service Worker 将被注销,并且请求将开始向 Web 发出。新的 service worker 将开始安装并在安装后接管页面。

      • "force": 立即注销当前 Service Worker 然后刷新以获取新内容

        警告

        虽然这可以确保用户访问的是最新内容,但这可能会影响访问体验。

      提示

      文档的更新方式由以前的版本控制,因此当前选项仅影响此版本的下一次更新。

    apple

    更高支持苹果的特殊设置,忽略它们是安全的。

    apple.icon

    • 类型:string
    • 详情:填入苹果使用的图标地址,推荐 152×152 大小

    apple.statusBarColor

    • 类型:"black" | "white"
    • 默认值:"black"
    • 详情:Safari 状态栏颜色

    apple.maskIcon

    • 类型:string
    • 详情:Safari 图标

    msTile

    针对微软磁贴的特殊设置,忽略它们是安全的。

    msTile.image

    • 类型:string
    • 详情:磁贴图标

    msTile.color

    • 类型:string
    • 默认值:themeColor
    • 详情:磁贴颜色。

    foundComponent

    • 类型:string
    • 默认值:"PWAFoundPopup"
    • 详情:自定义的提示弹窗组件路径。

    readyComponent

    • 类型:string
    • 默认值:"PWAReadyPopup"
    • 详情:自定义的更新弹窗组件路径。

    appendBase

    • 类型:boolean
    • 默认值:false
    • 详情:是否为选项中所有绝对链接添加 base。

    generateSwConfig

    ',35),m=s("p",null,"详情:",-1),C=s("code",null,"workbox-build",-1),h={href:"https://developers.google.com/web/tools/workbox/reference-docs/latest/module-workbox-build#.generateSW",target:"_blank",rel:"noopener noreferrer"},b=l(`

    locales

    • 类型:PWAPluginLocaleConfig

      interface PWAPluginLocaleData {
      +  /**
      +   * 安装按钮文字
      +   */
      +  install: string
      +
      +  /**
      +   * iOS 安装文字
      +   */
      +  iOSInstall: string
      +
      +  /**
      +   * 取消按钮文字
      +   */
      +  cancel: string
      +
      +  /**
      +   * 关闭按钮文字
      +   */
      +  close: string
      +
      +  /**
      +   * 上一张图片文字
      +   */
      +  prevImage: string
      +
      +  /**
      +   * 下一张图片文字
      +   */
      +  nextImage: string
      +
      +  /**
      +   * 安装解释
      +   */
      +  explain: string
      +
      +  /**
      +   * 描述标签文字
      +   */
      +  desc: string
      +
      +  /**
      +   * 特性标签文字
      +   */
      +  feature: string
      +
      +  /**
      +   * 更新内容提示文字
      +   */
      +  hint: string
      +
      +  /**
      +   * 更新内容可用文字
      +   */
      +  update: string
      +}
      +
      +interface PWAPluginLocaleConfig {
      +  [localePath: string]: PWAPluginLocaleData
      +}
      +
    • 详情:

      PWA 插件的国际化配置。

    内置支持语言
    • 简体中文 (zh-CN)
    • 繁体中文 (zh-TW)
    • 英文(美国) (en-US)
    • 德语 (de-DE)
    • 德语(澳大利亚) (de-AT)
    • 俄语 (ru-RU)
    • 乌克兰语 (uk-UA)
    • 越南语 (vi-VN)
    • 葡萄牙语(巴西) (pt-BR)
    • 波兰语 (pl-PL)
    • 法语 (fr-FR)
    • 西班牙语 (es-ES)
    • 斯洛伐克 (sk-SK)
    • 日语 (ja-JP)
    • 土耳其语 (tr-TR)
    • 韩语 (ko-KR)
    • 芬兰语 (fi-FI)
    • 印尼语 (id-ID)
    • 荷兰语 (nl-NL)

    组合式 API

    usePwaEvent

    `,5),g=s("p",null,"详情:",-1),f=s("p",null,"返回此插件的事件派发器。",-1),E={href:"https://github.com/yyx990803/register-service-worker",target:"_blank",rel:"noopener noreferrer"},A=s("li",null,[s("p",null,"示例:")],-1),k=l(`
    import { usePwaEvent } from '@vuepress/plugin-pwa/client'
    +
    +export default {
    +  setup() {
    +    const event = usePwaEvent()
    +    event.on('ready', (registration) => {
    +      console.log('Service worker is active.')
    +    })
    +  },
    +}
    +

    工具函数

    forceUpdate

    • 详情:

      当发现新内容时强制刷新页面。

    • 示例:

    import { forceUpdate } from '@vuepress/plugin-pwa/client'
    +import { onMounted } from 'vue'
    +
    +export default {
    +  setup() {
    +    onMounted(() => {
    +      forceUpdate()
    +    })
    +  },
    +}
    +

    registerSW

    • 详情:

      手动注册 Service Worker。

    • 参数:

    参数类型描述
    serviceWorkerPathstringService worker 的路径
    hooksobjectService worker 的钩子
    showStatusboolean在控制台输出状态日志
    interface Hooks {
    +  registrationOptions?: RegistrationOptions
    +  ready?: (registration: ServiceWorkerRegistration) => void
    +  registered?: (registration: ServiceWorkerRegistration) => void
    +  cached?: (registration: ServiceWorkerRegistration) => void
    +  updated?: (registration: ServiceWorkerRegistration) => void
    +  updatefound?: (registration: ServiceWorkerRegistration) => void
    +  offline?: () => void
    +  error?: (error: Error) => void
    +}
    +
    • 示例:
    import { registerSW } from '@vuepress/plugin-pwa/client'
    +import { onMounted } from 'vue'
    +
    +export default {
    +  setup() {
    +    onMounted(() => {
    +      registerSW('/service-worker.js', {
    +        ready(registration) {
    +          console.log('Service worker is active.')
    +        },
    +      })
    +    })
    +  },
    +}
    +

    skipWaiting

    • 详情:

      激活等待中的 Service Worker。

    • 参数:

    参数类型描述
    registrationServiceWorkerRegistration想要激活的 Service Worker 的注册
    • 示例:
    import { usePwaEvent, skipWaiting } from '@vuepress/plugin-pwa/client'
    +
    +export default {
    +  setup() {
    +    const event = usePwaEvent()
    +
    +    event.on('updated', (registration) => {
    +      console.log('The waiting service worker is available.')
    +      // activate the waiting service worker
    +      skipWaiting(registration)
    +    })
    +  },
    +}
    +

    unregisterSW

    • 详情:

      手动注销 Service Worker。

    • 示例:

    import { unregisterSW } from '@vuepress/plugin-pwa/client'
    +import { onMounted } from 'vue'
    +
    +export default {
    +  setup() {
    +    onMounted(() => {
    +      unregisterSW()
    +    })
    +  },
    +}
    +

    样式

    你可以通过 CSS 变量来自定义样式:

    :root {
    +  --pwa-z-index: 10;
    +  --pwa-color: #2c3e50;
    +  --pwa-bg-color: #ffffff;
    +  --pwa-border-color: #3eaf7c;
    +  --pwa-shadow-color: rgb(0 0 0 / 15%);
    +  --pwa-btn-text-color: #ffffff;
    +  --pwa-btn-bg-color: #3eaf7c;
    +  --pwa-btn-hover-bg-color: #4abf8a;
    +  --pwa-content-color: #333;
    +  --pwa-content-light-color: #666;
    +}
    +
    `,22);function x(w,F){const a=p("ExternalLinkIcon");return i(),c("div",null,[t,s("ul",null,[d,s("li",null,[D,s("ul",null,[s("li",null,[s("a",u,[n("MDN Web Docs: Web App Manifest"),e(a)])]),s("li",null,[s("a",v,[n("W3C Manifest"),e(a)])])])])]),y,s("ul",null,[s("li",null,[m,s("p",null,[n("传递给 "),C,n(" 的选项,具体详情,请见 "),s("a",h,[n("Workbox 文档"),e(a)])])])]),b,s("ul",null,[s("li",null,[g,f,s("p",null,[n("你可以添加监听器函数到 "),s("a",E,[n("register-service-worker"),e(a)]),n(" 提供的事件。")])]),A]),k])}const S=o(r,[["render",x],["__file","config.html.vue"]]),q=JSON.parse('{"path":"/zh/plugins/pwa/config.html","title":"配置","lang":"zh-CN","frontmatter":{},"headers":[{"level":2,"title":"选项","slug":"选项","link":"#选项","children":[{"level":3,"title":"showInstall","slug":"showinstall","link":"#showinstall","children":[]},{"level":3,"title":"manifest","slug":"manifest","link":"#manifest","children":[]},{"level":3,"title":"favicon","slug":"favicon","link":"#favicon","children":[]},{"level":3,"title":"themeColor","slug":"themecolor","link":"#themecolor","children":[]},{"level":3,"title":"cacheHTML","slug":"cachehtml","link":"#cachehtml","children":[]},{"level":3,"title":"cacheImage","slug":"cacheimage","link":"#cacheimage","children":[]},{"level":3,"title":"maxSize","slug":"maxsize","link":"#maxsize","children":[]},{"level":3,"title":"maxImageSize","slug":"maximagesize","link":"#maximagesize","children":[]},{"level":3,"title":"update","slug":"update","link":"#update","children":[]},{"level":3,"title":"apple","slug":"apple","link":"#apple","children":[]},{"level":3,"title":"msTile","slug":"mstile","link":"#mstile","children":[]},{"level":3,"title":"foundComponent","slug":"foundcomponent","link":"#foundcomponent","children":[]},{"level":3,"title":"readyComponent","slug":"readycomponent","link":"#readycomponent","children":[]},{"level":3,"title":"appendBase","slug":"appendbase","link":"#appendbase","children":[]},{"level":3,"title":"generateSwConfig","slug":"generateswconfig","link":"#generateswconfig","children":[]},{"level":3,"title":"locales","slug":"locales","link":"#locales","children":[]}]},{"level":2,"title":"组合式 API","slug":"组合式-api","link":"#组合式-api","children":[{"level":3,"title":"usePwaEvent","slug":"usepwaevent","link":"#usepwaevent","children":[]}]},{"level":2,"title":"工具函数","slug":"工具函数","link":"#工具函数","children":[{"level":3,"title":"forceUpdate","slug":"forceupdate","link":"#forceupdate","children":[]},{"level":3,"title":"registerSW","slug":"registersw","link":"#registersw","children":[]},{"level":3,"title":"skipWaiting","slug":"skipwaiting","link":"#skipwaiting","children":[]},{"level":3,"title":"unregisterSW","slug":"unregistersw","link":"#unregistersw","children":[]}]},{"level":2,"title":"样式","slug":"样式","link":"#样式","children":[]}],"git":{"updatedTime":1708365979000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":1}]},"filePathRelative":"zh/plugins/pwa/config.md"}');export{S as comp,q as data}; diff --git a/assets/config.html-BLh0zTBN.js b/assets/config.html-BLh0zTBN.js new file mode 100644 index 0000000000..66e6018811 --- /dev/null +++ b/assets/config.html-BLh0zTBN.js @@ -0,0 +1 @@ +import{_ as t,r as p,o as s,c as n,b as e,d as l,a as o,w as r,e as i}from"./app-DI49vYHA.js";const d={},c=i('

    配置

    hostname

    • 类型:string

    • 必填:是

    • 详情:

      当前网站部署到的域名,插件需要此选项才能工作。

    extraUrls

    • 类型:string[]

    • 详情:

      需要额外包含的网址。

      如果你有一些不包含在 VuePress 路由中的链接 (如: 存放在 public 文件夹下的页面或其他插件或工具直接生成的页面),你可能需要设置此项。

    • 示例:['/about.html', '/api/']

    excludePaths

    • 类型:string[]

    • 默认值:['/404.html']

    • 详情:

      不需要收录的页面路径,请以绝对路径开头。

      默认情况下 VuePress 自动生成的所有路径 (除 404 页) 都会被添加进 Sitemap。

    devServer

    • 类型:boolean

    • 默认值:false

    • 详情:

      是否在开发服务器中启用

    提示

    由于性能原因,我们不提供热更新。重启开发服务器以同步你的变更。

    devHostname

    • 类型:string

    • 默认值:"http://localhost:${port}"

    • 详情:

      开发服务器使用的主机名

    sitemapFilename

    • 类型:string

    • 默认值:"sitemap.xml"

    • 详情:

      输出的文件名,相对于输出目录。

    sitemapXSLFilename

    • 类型:string

    • 默认值:"sitemap.xsl"

    • 详情:

      输出的 xsl 文件名,相对于输出目录。

    sitemapXSLTemplate

    • 类型:string

    • 默认值:"@vuepress-plugin/sitemap/templates/sitemap.xsl"

    • 详情:

      用作模板的 XSL 文件内容

    changefreq

    ',19),h=e("li",null,[e("p",null,[l("类型:"),e("code",null,'"always" | "hourly" | "daily" | "weekly" | "monthly" | "yearly" | "never"')])],-1),m=e("li",null,[e("p",null,[l("默认值:"),e("code",null,'"daily"')])],-1),u=e("p",null,"详情:",-1),f=i('

    priority

    • 类型:number

    • 默认值:0.5

    • 详情:

      页面优先级,范围 01

    modifyTimeGetter

    • 类型:(page: Page, app: App) => string

    • 详情:

      最后修改事件的获得器,需要返回一个 ISO 字符形式的时间,默认会自动通过 Git 插件生成。

    ',4);function g(x,v){const a=p("RouteLink");return s(),n("div",null,[c,e("ul",null,[h,m,e("li",null,[u,e("p",null,[l("页面默认更新频率,会被 Frontmatter 中的 "),o(a,{to:"/zh/plugins/sitemap/frontmatter.html#sitemap-changefreq"},{default:r(()=>[l("changefreq")]),_:1}),l(" 选项覆盖。")])])]),f])}const y=t(d,[["render",g],["__file","config.html.vue"]]),b=JSON.parse('{"path":"/zh/plugins/sitemap/config.html","title":"配置","lang":"zh-CN","frontmatter":{},"headers":[{"level":2,"title":"hostname","slug":"hostname","link":"#hostname","children":[]},{"level":2,"title":"extraUrls","slug":"extraurls","link":"#extraurls","children":[]},{"level":2,"title":"excludePaths","slug":"excludepaths","link":"#excludepaths","children":[]},{"level":2,"title":"devServer","slug":"devserver","link":"#devserver","children":[]},{"level":2,"title":"devHostname","slug":"devhostname","link":"#devhostname","children":[]},{"level":2,"title":"sitemapFilename","slug":"sitemapfilename","link":"#sitemapfilename","children":[]},{"level":2,"title":"sitemapXSLFilename","slug":"sitemapxslfilename","link":"#sitemapxslfilename","children":[]},{"level":2,"title":"sitemapXSLTemplate","slug":"sitemapxsltemplate","link":"#sitemapxsltemplate","children":[]},{"level":2,"title":"changefreq","slug":"changefreq","link":"#changefreq","children":[]},{"level":2,"title":"priority","slug":"priority","link":"#priority","children":[]},{"level":2,"title":"modifyTimeGetter","slug":"modifytimegetter","link":"#modifytimegetter","children":[]}],"git":{"updatedTime":1706801200000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":4}]},"filePathRelative":"zh/plugins/sitemap/config.md"}');export{y as comp,b as data}; diff --git a/assets/config.html-BiDaVbw2.js b/assets/config.html-BiDaVbw2.js new file mode 100644 index 0000000000..dc7dce4361 --- /dev/null +++ b/assets/config.html-BiDaVbw2.js @@ -0,0 +1,18 @@ +import{_ as o,r as i,o as p,c as r,b as e,d as s,a,w as n,e as t}from"./app-DI49vYHA.js";const c={},d=t(`

    Plugin Config

    hostname

    • Type: string
    • Required: Yes

    The domain name of the deployment site.

    atom

    • Type: boolean
    • Default: false

    Whether to output Atom syntax files.

    json

    • Type: boolean
    • Default: false

    Whether output JSON syntax files.

    rss

    • Type: boolean
    • Default: false

    Whether to output RSS syntax files.

    image

    • Type: string

    A large image/icon of the feed, probably used as banner.

    icon

    • Type: string

    A small icon of the feed, probably used as favicon.

    count

    • Type: number
    • Default: 100

    Set the maximum number of items in the feed. After all pages are sorted, the first count items will be intercepted.

    If your site has a lot of articles, you may consider this option to reduce feed file size.

    preservedElements

    • Type: (RegExp | string)[] | (tagName: string) => boolean

    Custom element or component which should be preserved in feed.

    By default, all unknown tags will be removed.

    filter

    • Type: (page: Page)=> boolean

    • Default:

      ;({ frontmatter, filePathRelative }: Page): boolean =>
      +  !(
      +    frontmatter.home ||
      +    !filePathRelative ||
      +    frontmatter.article === false ||
      +    frontmatter.feed === false
      +  )
      +

    A custom filter function, used to filter feed items.

    sorter

    • Type: (pageA: Page, pageB: Page)=> number

    • Default:

      // dateSorter is from @vuepress/helper
      +;(pageA: Page, pageB: Page): number =>
      +  dateSorter(
      +    pageA.data.git?.createdTime
      +      ? new Date(pageA.data.git?.createdTime)
      +      : pageA.frontmatter.date,
      +    pageB.data.git?.createdTime
      +      ? new Date(pageB.data.git?.createdTime)
      +      : pageB.frontmatter.date,
      +  )
      +

    Custom sorter function for feed items.

    The default sorting behavior is by file adding time coming from git (needs @vuepress/plugin-git).

    TIP

    You should enable @vuepress/plugin-git to get the newest created pages as feed items. Otherwise, the feed items will be sorted by the default order of pages in VuePress.

    channel

    channel option is used to config Feed Channels.

    `,37),u=t('

    devServer

    • Type: boolean
    • Default: false

    Whether enabled in devServer.

    TIP

    For performance reasons, we do not provide hot reload. Reboot your devServer to sync your changes.

    devHostname

    • Type: string
    • Default: "http://localhost:${port}"

    Hostname to use in devServer

    atomOutputFilename

    • Type: string
    • Default: "atom.xml"

    Atom syntax output filename, relative to dest folder.

    atomXslTemplate

    • Type: string
    • Default: Content of @vuepress/plugin-feed/templates/atom.xsl

    Atom xsl template file content.

    atomXslFilename

    • Type: string
    • Default: "atom.xsl"

    Atom xsl filename, relative to dest folder.

    jsonOutputFilename

    • Type: string
    • Default: "feed.json"

    JSON syntax output filename, relative to dest folder.

    rssOutputFilename

    • Type: string
    • Default: "rss.xml"

    RSS syntax output filename, relative to dest folder.

    rssXslTemplate

    • Type: string
    • Default: Content of @vuepress/plugin-feed/templates/rss.xsl

    RSS xsl template file content.

    rssXslFilename

    • Type: string
    • Default: "rss.xsl"

    RSS syntax xsl filename, relative to dest folder.

    getter

    ',29),h=e("div",{class:"custom-container tip"},[e("p",{class:"custom-container-title"},"The plugin has a built-in getter, only set this if you want full control of feed generation.")],-1),D=e("h2",{id:"locales",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#locales"},[e("span",null,"locales")])],-1),m=e("ul",null,[e("li",null,[s("Type: "),e("code",null,"Record")])],-1),f=e("p",null,"You can use it to specific options for each locale.",-1),y=e("p",null,[s("Any options above are supported except "),e("code",null,"hostname"),s(".")],-1);function g(v,C){const l=i("RouteLink");return p(),r("div",null,[d,e("p",null,[s("For available options, please see "),a(l,{to:"/plugins/feed/channel.html"},{default:n(()=>[s("Config → Channel")]),_:1})]),u,e("p",null,[s("Feed generation controller, see "),a(l,{to:"/plugins/feed/getter.html"},{default:n(()=>[s("Feed Getter")]),_:1}),s(".")]),h,D,m,f,y])}const x=o(c,[["render",g],["__file","config.html.vue"]]),F=JSON.parse('{"path":"/plugins/feed/config.html","title":"Plugin Config","lang":"en-US","frontmatter":{},"headers":[{"level":2,"title":"hostname","slug":"hostname","link":"#hostname","children":[]},{"level":2,"title":"atom","slug":"atom","link":"#atom","children":[]},{"level":2,"title":"json","slug":"json","link":"#json","children":[]},{"level":2,"title":"rss","slug":"rss","link":"#rss","children":[]},{"level":2,"title":"image","slug":"image","link":"#image","children":[]},{"level":2,"title":"icon","slug":"icon","link":"#icon","children":[]},{"level":2,"title":"count","slug":"count","link":"#count","children":[]},{"level":2,"title":"preservedElements","slug":"preservedelements","link":"#preservedelements","children":[]},{"level":2,"title":"filter","slug":"filter","link":"#filter","children":[]},{"level":2,"title":"sorter","slug":"sorter","link":"#sorter","children":[]},{"level":2,"title":"channel","slug":"channel","link":"#channel","children":[]},{"level":2,"title":"devServer","slug":"devserver","link":"#devserver","children":[]},{"level":2,"title":"devHostname","slug":"devhostname","link":"#devhostname","children":[]},{"level":2,"title":"atomOutputFilename","slug":"atomoutputfilename","link":"#atomoutputfilename","children":[]},{"level":2,"title":"atomXslTemplate","slug":"atomxsltemplate","link":"#atomxsltemplate","children":[]},{"level":2,"title":"atomXslFilename","slug":"atomxslfilename","link":"#atomxslfilename","children":[]},{"level":2,"title":"jsonOutputFilename","slug":"jsonoutputfilename","link":"#jsonoutputfilename","children":[]},{"level":2,"title":"rssOutputFilename","slug":"rssoutputfilename","link":"#rssoutputfilename","children":[]},{"level":2,"title":"rssXslTemplate","slug":"rssxsltemplate","link":"#rssxsltemplate","children":[]},{"level":2,"title":"rssXslFilename","slug":"rssxslfilename","link":"#rssxslfilename","children":[]},{"level":2,"title":"getter","slug":"getter","link":"#getter","children":[]},{"level":2,"title":"locales","slug":"locales","link":"#locales","children":[]}],"git":{"updatedTime":1706854470000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":3}]},"filePathRelative":"plugins/feed/config.md"}');export{x as comp,F as data}; diff --git a/assets/config.html-BqDWUA_G.js b/assets/config.html-BqDWUA_G.js new file mode 100644 index 0000000000..4219b65e77 --- /dev/null +++ b/assets/config.html-BqDWUA_G.js @@ -0,0 +1,141 @@ +import{_ as p,r as t,o as c,c as r,b as s,d as e,a as l,w as o,e as n}from"./app-DI49vYHA.js";const d={},u=n('

    Config

    Basic Config

    hostname

    • Type: string

    • Details:

      Hostname to be deployed, e.g.: https://example.com

    locales

    ',5),h=s("li",null,[s("p",null,[e("Type: "),s("code",null,"{ [path: string]: Partial }")])],-1),D=s("li",null,[s("p",null,[e("Default: "),s("code",null,"{}")])],-1),v=s("p",null,"Details:",-1),m=s("p",null,"Specify locales for i18n support.",-1),b=s("p",null,[e("All the options inside the "),s("a",{href:"#locale-config"},"Locale Config"),e(" section can be used in locales.")],-1),y=s("code",null,"locales",-1),f=s("p",null,"Also see:",-1),g={href:"https://v2.vuepress.vuejs.org/guide/i18n.html",target:"_blank",rel:"noopener noreferrer"},C=n('

    Locale Config

    Config of this section can be used as normal config, and can also be used in the locales option.

    colorMode

    ',3),E=s("li",null,[s("p",null,[e("Type: "),s("code",null,"'auto' | 'light' | 'dark'")])],-1),_=s("li",null,[s("p",null,[e("Default: "),s("code",null,"'auto'")])],-1),k=s("p",null,"Details:",-1),x=s("p",null,"Default color mode.",-1),w=s("code",null,"'auto'",-1),A={href:"https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-color-scheme",target:"_blank",rel:"noopener noreferrer"},F=s("li",null,[s("p",null,"Also see:"),s("ul",null,[s("li",null,[s("a",{href:"#colormodeswitch"},"Default Theme > Config > colorModeSwitch")])])],-1),T=s("h3",{id:"colormodeswitch",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#colormodeswitch"},[s("span",null,"colorModeSwitch")])],-1),S=n("
  • Type: boolean

  • Default: true

  • Details:

    Enable color mode switching or not.

    If set to true, a button to switch color mode will be displayed in the navbar.

  • ",3),I=s("p",null,"Also see:",-1),j=s("li",null,[s("a",{href:"#colormode"},"Default Theme > Config > colorMode")],-1),L=n(`

    home

    • Type: string

    • Default: /

    • Details:

      Specify the path of the homepage.

      This will be used for:

      • the logo link of the navbar
      • the back to home link of the 404 page
    • Type: false | (NavbarItem | NavbarGroup | string)[]

    • Default: []

    • Details:

      Configuration of navbar.

      Set to false to disable navbar.

      To configure the navbar items, you can set it to a navbar array, each item of which could be a NavbarItem object, a NavbarGroup object, or a string:

      • A NavbarItem object should have a text field and a link field, could have an optional activeMatch field.
      • A NavbarGroup object should have a text field and a children field. The children field should be a navbar array, too.
      • A string should be the path to the target page file. It will be converted to a NavbarItem object, using the page title as text, and the page route path as link.
    • Example 1:

    export default {
    +  theme: defaultTheme({
    +    navbar: [
    +      // NavbarItem
    +      {
    +        text: 'Foo',
    +        link: '/foo/',
    +      },
    +      // NavbarGroup
    +      {
    +        text: 'Group',
    +        children: ['/group/foo.md', '/group/bar.md'],
    +      },
    +      // string - page file path
    +      '/bar/README.md',
    +    ],
    +  }),
    +}
    +
    • Example 2:
    export default {
    +  theme: defaultTheme({
    +    navbar: [
    +      // nested group - max depth is 2
    +      {
    +        text: 'Group',
    +        children: [
    +          {
    +            text: 'SubGroup',
    +            children: ['/group/sub/foo.md', '/group/sub/bar.md'],
    +          },
    +        ],
    +      },
    +      // control when should the item be active
    +      {
    +        text: 'Group 2',
    +        children: [
    +          {
    +            text: 'Always active',
    +            link: '/',
    +            // this item will always be active
    +            activeMatch: '/',
    +          },
    +          {
    +            text: 'Active on /foo/',
    +            link: '/not-foo/',
    +            // this item will be active when current route path starts with /foo/
    +            // regular expression is supported
    +            activeMatch: '^/foo/',
    +          },
    +        ],
    +      },
    +    ],
    +  }),
    +}
    +
    • Type: null | string

    • Details:

      Specify the url of logo image.

      The logo image will be displayed at the left end of the navbar.

      Set to null to disable logo.

    • Example:

    export default {
    +  theme: defaultTheme({
    +    // public file path
    +    logo: '/hero.png',
    +    // url
    +    logo: 'https://vuejs.org/images/logo.png',
    +  }),
    +}
    +
    `,10),N={href:"https://v2.vuepress.vuejs.org/guide/assets.html#public-files",target:"_blank",rel:"noopener noreferrer"},G=n(`

    logoDark

    logoAlt

    • Type: null | string

    • Details:

      Specify the alt text of the logo image.

      If not specified, defaults to be the same as the site title.

    repo

    • Type: string

    • Details:

      Specify the repository url of your project.

      This will be used as the link of the repository link, which will be displayed as the last item of the navbar.

    export default {
    +  theme: defaultTheme({
    +    // If you set it in the form of \`organization/repository\`
    +    // we will take it as a GitHub repo
    +    repo: 'vuejs/vuepress',
    +    // You can also set it to a URL directly
    +    repo: 'https://gitlab.com/foo/bar',
    +  }),
    +}
    +
    `,8),M=s("li",null,[s("p",null,[e("Type: "),s("code",null,"false | 'auto' | SidebarConfigArray | SidebarConfigObject")])],-1),B=s("li",null,[s("p",null,[e("Default: "),s("code",null,"'auto'")])],-1),R=s("p",null,"Details:",-1),P=s("p",null,"Configuration of sidebar.",-1),U=n("

    Set to false to disable sidebar.

    If you set it to 'auto', the sidebar will be automatically generated from the page headers.

    To configure the sidebar items manually, you can set this option to a sidebar array, each item of which could be a SidebarItem object or a string:

    • A SidebarItem object should have a text field, could have an optional link field, an optional children field and an optional collapsible field. The children field should be a sidebar array. The collapsible field controls whether the item is collapsible.
    • A string should be the path to the target page file. It will be converted to a SidebarItem object, whose text is the page title, link is the page route path, and children is automatically generated from the page headers.

    If you want to set different sidebar for different sub paths, you can set this option to a sidebar object:

    • The key should be the path prefix.
    • The value should be a sidebar array or set to 'heading' to automatically generate the sidebar from the page headers for just the corresponding path.
    ",6),Y=s("li",null,[s("p",null,"Example 1:")],-1),V=n(`
    export default {
    +  theme: defaultTheme({
    +    // sidebar array
    +    // all pages will use the same sidebar
    +    sidebar: [
    +      // SidebarItem
    +      {
    +        text: 'Foo',
    +        link: '/foo/',
    +        children: [
    +          // SidebarItem
    +          {
    +            text: 'github',
    +            link: 'https://github.com',
    +            children: [],
    +          },
    +          // string - page file path
    +          '/foo/bar.md',
    +        ],
    +      },
    +      // string - page file path
    +      '/bar/README.md',
    +    ],
    +  }),
    +}
    +
    • Example 2:
    export default {
    +  theme: defaultTheme({
    +    // sidebar object
    +    // pages under different sub paths will use different sidebar
    +    sidebar: {
    +      '/guide/': [
    +        {
    +          text: 'Guide',
    +          children: ['/guide/introduction.md', '/guide/getting-started.md'],
    +        },
    +      ],
    +      '/reference/': 'heading',
    +    },
    +  }),
    +}
    +
    • Example 3:
    export default {
    +  theme: defaultTheme({
    +    // collapsible sidebar
    +    sidebar: {
    +      '/reference/': [
    +        {
    +          text: 'VuePress Reference',
    +          collapsible: true,
    +          children: ['/reference/cli.md', '/reference/config.md'],
    +        },
    +        {
    +          text: 'Bundlers Reference',
    +          collapsible: true,
    +          children: [
    +            '/reference/bundler/vite.md',
    +            '/reference/bundler/webpack.md',
    +          ],
    +        },
    +      ],
    +    },
    +  }),
    +}
    +

    sidebarDepth

    `,6),H=s("li",null,[s("p",null,[e("Type: "),s("code",null,"number")])],-1),O=s("li",null,[s("p",null,[e("Default: "),s("code",null,"2")])],-1),z=n("

    Details:

    Set the maximum depth of the sidebar children which are automatically generated from the page headers.

    • Set to 0 to disable all levels of headers.
    • Set to 1 to include <h2> headers.
    • Set to 2 to include <h2> and <h3> headers.
    • ...
    ",3),J={href:"https://v2.vuepress.vuejs.org/reference/config.html#markdown-headers",target:"_blank",rel:"noopener noreferrer"},W=s("p",null,[e("The default value of "),s("code",null,"markdown.headers.level"),e(" is "),s("code",null,"[2, 3]"),e(", so the default max value of "),s("code",null,"sidebarDepth"),e(" is "),s("code",null,"2"),e(".")],-1),q=s("h3",{id:"editlink",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#editlink"},[s("span",null,"editLink")])],-1),K=s("li",null,[s("p",null,[e("Type: "),s("code",null,"boolean")])],-1),Q=s("li",null,[s("p",null,[e("Default: "),s("code",null,"true")])],-1),X=s("p",null,"Details:",-1),Z=s("p",null,[e("Enable the "),s("em",null,"edit this page"),e(" link or not.")],-1),$=n(`

    editLinkPattern

    • Type: string

    • Details:

      Specify the pattern of the edit this page link.

      This will be used for generating the edit this page link.

      If you don't set this option, the pattern will be inferred from the docsRepo option. But if your documentation repository is not hosted on a common platform, for example, GitHub, GitLab, Bitbucket, Gitee, etc., you have to set this option explicitly to make the edit this page link work.

    • Usage:

      PatternDescription
      :repoThe docs repo url, i.e. docsRepo
      :branchThe docs repo branch, i.e. docsBranch
      :pathThe path of the page source file, i.e. docsDir joins the relative path of the page file
    • Example:

    export default {
    +  theme: defaultTheme({
    +    docsRepo: 'https://gitlab.com/owner/name',
    +    docsBranch: 'master',
    +    docsDir: 'docs',
    +    editLinkPattern: ':repo/-/edit/:branch/:path',
    +  }),
    +}
    +

    The generated link will look like 'https://gitlab.com/owner/name/-/edit/master/docs/path/to/file.md'.

    docsRepo

    • Type: string

    • Details:

      Specify the repository url of your documentation source files.

      This will be used for generating the edit this page link.

      If you don't set this option, it will use the repo option by default. But if your documentation source files are in a different repository, you will need to set this option.

    docsBranch

    • Type: string

    • Default: 'main'

    • Details:

      Specify the repository branch of your documentation source files.

      This will be used for generating the edit this page link.

    docsDir

    • Type: string

    • Default: ''

    • Details:

      Specify the directory of your documentation source files in the repository.

      This will be used for generating the edit this page link.

    lastUpdated

    `,11),ss=s("li",null,[s("p",null,[e("Type: "),s("code",null,"boolean")])],-1),es=s("li",null,[s("p",null,[e("Default: "),s("code",null,"true")])],-1),ls=s("p",null,"Details:",-1),ns=s("p",null,[e("Enable the "),s("em",null,"last updated timestamp"),e(" or not.")],-1),as=s("code",null,"false",-1),os=s("h3",{id:"contributors",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#contributors"},[s("span",null,"contributors")])],-1),is=s("li",null,[s("p",null,[e("Type: "),s("code",null,"boolean")])],-1),ts=s("li",null,[s("p",null,[e("Default: "),s("code",null,"true")])],-1),ps=s("p",null,"Details:",-1),cs=s("p",null,[e("Enable the "),s("em",null,"contributors list"),e(" or not.")],-1),rs=s("code",null,"false",-1);function ds(us,hs){const a=t("RouteLink"),i=t("ExternalLinkIcon");return c(),r("div",null,[u,s("ul",null,[h,D,s("li",null,[v,m,b,s("p",null,[e("This option will only take effect in default theme, so don't confuse with "),y,e(" in "),l(a,{to:"/themes/default/config.html#locales"},{default:o(()=>[e("Site Config")]),_:1}),e(".")])]),s("li",null,[f,s("ul",null,[s("li",null,[s("a",g,[e("Guide > I18n"),l(i)])])])])]),C,s("ul",null,[E,_,s("li",null,[k,x,s("p",null,[e("If set to "),w,e(", the initial color mode will be automatically set according to "),s("a",A,[e("prefers-color-scheme"),l(i)]),e(".")])]),F]),T,s("ul",null,[S,s("li",null,[I,s("ul",null,[j,s("li",null,[l(a,{to:"/themes/default/locale.html#togglecolormode"},{default:o(()=>[e("Default Theme > Locale Config > toggleColorMode")]),_:1})])])])]),L,s("ul",null,[s("li",null,[e("Also see: "),s("ul",null,[s("li",null,[s("a",N,[e("Guide > Assets > Public Files"),l(i)])])])])]),G,s("ul",null,[M,B,s("li",null,[R,P,s("p",null,[e("You can override this global option via "),l(a,{to:"/themes/default/frontmatter.html#sidebar"},{default:o(()=>[e("sidebar")]),_:1}),e(" frontmatter in your pages.")]),U]),Y]),V,s("ul",null,[H,O,s("li",null,[z,s("p",null,[e("The max value depends on which levels of headers you have extracted via "),s("a",J,[e("markdown.headers.level"),l(i)]),e(".")]),W,s("p",null,[e("You can override this global option via "),l(a,{to:"/themes/default/frontmatter.html#sidebardepth"},{default:o(()=>[e("sidebarDepth")]),_:1}),e(" frontmatter in your pages.")])])]),q,s("ul",null,[K,Q,s("li",null,[X,Z,s("p",null,[e("You can override this global option via "),l(a,{to:"/themes/default/frontmatter.html#editlink"},{default:o(()=>[e("editLink")]),_:1}),e(" frontmatter in your pages.")])])]),$,s("ul",null,[ss,es,s("li",null,[ls,ns,s("p",null,[e("You can override this global option via "),l(a,{to:"/themes/default/frontmatter.html#lastupdated"},{default:o(()=>[e("lastUpdated")]),_:1}),e(" frontmatter in your pages. Notice that if you have already set this option to "),as,e(", this feature will be disabled totally and could not be enabled in locales nor page frontmatter.")])])]),os,s("ul",null,[is,ts,s("li",null,[ps,cs,s("p",null,[e("You can override this global option via "),l(a,{to:"/themes/default/frontmatter.html#contributors"},{default:o(()=>[e("contributors")]),_:1}),e(" frontmatter in your pages. Notice that if you have already set this option to "),rs,e(", this feature will be disabled totally and could not be enabled in locales nor page frontmatter.")])])])])}const vs=p(d,[["render",ds],["__file","config.html.vue"]]),ms=JSON.parse('{"path":"/themes/default/config.html","title":"Config","lang":"en-US","frontmatter":{},"headers":[{"level":2,"title":"Basic Config","slug":"basic-config","link":"#basic-config","children":[{"level":3,"title":"hostname","slug":"hostname","link":"#hostname","children":[]},{"level":3,"title":"locales","slug":"locales","link":"#locales","children":[]}]},{"level":2,"title":"Locale Config","slug":"locale-config","link":"#locale-config","children":[{"level":3,"title":"colorMode","slug":"colormode","link":"#colormode","children":[]},{"level":3,"title":"colorModeSwitch","slug":"colormodeswitch","link":"#colormodeswitch","children":[]},{"level":3,"title":"home","slug":"home","link":"#home","children":[]},{"level":3,"title":"navbar","slug":"navbar","link":"#navbar","children":[]},{"level":3,"title":"logo","slug":"logo","link":"#logo","children":[]},{"level":3,"title":"logoDark","slug":"logodark","link":"#logodark","children":[]},{"level":3,"title":"logoAlt","slug":"logoalt","link":"#logoalt","children":[]},{"level":3,"title":"repo","slug":"repo","link":"#repo","children":[]},{"level":3,"title":"sidebar","slug":"sidebar","link":"#sidebar","children":[]},{"level":3,"title":"sidebarDepth","slug":"sidebardepth","link":"#sidebardepth","children":[]},{"level":3,"title":"editLink","slug":"editlink","link":"#editlink","children":[]},{"level":3,"title":"editLinkPattern","slug":"editlinkpattern","link":"#editlinkpattern","children":[]},{"level":3,"title":"docsRepo","slug":"docsrepo","link":"#docsrepo","children":[]},{"level":3,"title":"docsBranch","slug":"docsbranch","link":"#docsbranch","children":[]},{"level":3,"title":"docsDir","slug":"docsdir","link":"#docsdir","children":[]},{"level":3,"title":"lastUpdated","slug":"lastupdated","link":"#lastupdated","children":[]},{"level":3,"title":"contributors","slug":"contributors","link":"#contributors","children":[]}]}],"git":{"updatedTime":1706734148000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":7}]},"filePathRelative":"themes/default/config.md"}');export{vs as comp,ms as data}; diff --git a/assets/config.html-D8ayWeGZ.js b/assets/config.html-D8ayWeGZ.js new file mode 100644 index 0000000000..b83c3aa410 --- /dev/null +++ b/assets/config.html-D8ayWeGZ.js @@ -0,0 +1,45 @@ +import{_ as s,o as n,c as a,e as l}from"./app-DI49vYHA.js";const e={},p=l(`

    Config

    hostname

    • Type: string

    • Required: Yes

    • Details:

      Deploy hostname.

    author

    • Type: Author

      type AuthorName = string
      +
      +interface AuthorInfo {
      +  /**
      +   * Author name
      +   */
      +  name: string
      +
      +  /**
      +   * Author website
      +   */
      +  url?: string
      +
      +  /**
      +   * Author email
      +   */
      +  email?: string
      +}
      +
      +type Author = AuthorName | AuthorName[] | AuthorInfo | AuthorInfo[]
      +
    • Required: No

    • Details:

      Default author.

    autoDescription

    • Type: boolean

    • Default: true

    • Details:

      Whether generate description automatically

    canonical

    • Type: string | ((page: Page) => string | null)

    • Details:

      Canonical link

    fallBackImage

    • Type: string

    • Details:

      Fallback Image link when no image are found

    restrictions

    • Type: string

    • Details:

      The age rating of the content, the format is [int]+, such as "13+".

    twitterID

    • Type: string

    • Details:

      Fill in your twitter username.

    isArticle

    • Type: (page: Page) => boolean

    • Details:

      Use this option to judge whether the page is an article.

    ogp

    • Type:

      function ogp(
      +  /** OGP info inferred by plugin */
      +  ogp: SeoContent,
      +  /** Page Object */
      +  page: Page,
      +  /** VuePress App */
      +  app: App,
      +): SeoContent
      +
    • Required: No

    • Details:

      Custom OPG Generator.

      You can use this options to edit OGP tags.

    jsonLd

    • Type:

      function jsonLd(
      +  /** JSON-LD Object inferred by plugin */
      +  jsonLD: ArticleSchema | BlogPostingSchema | WebPageSchema,
      +  /** Page Object */
      +  page: Page,
      +  /** VuePress App */
      +  app: App,
      +): ArticleSchema | BlogPostingSchema | WebPageSchema
      +
    • Required: No

    • Details:

      Custom JSON-LD Generator.

      You can use this options to edit JSON-LD properties.

    customHead

    • Type:

      function customHead(
      +  /** Head tag config */
      +  head: HeadConfig[],
      +  /** Page Object */
      +  page: Page,
      +  /** VuePress App */
      +  app: App,
      +): void
      +
    • Required: No

    • Details:

      You can use this options to edit tags injected to <head>.

    `,23),i=[p];function o(t,c){return n(),a("div",null,i)}const d=s(e,[["render",o],["__file","config.html.vue"]]),u=JSON.parse('{"path":"/plugins/seo/config.html","title":"Config","lang":"en-US","frontmatter":{},"headers":[{"level":2,"title":"hostname","slug":"hostname","link":"#hostname","children":[]},{"level":2,"title":"author","slug":"author","link":"#author","children":[]},{"level":2,"title":"autoDescription","slug":"autodescription","link":"#autodescription","children":[]},{"level":2,"title":"canonical","slug":"canonical","link":"#canonical","children":[]},{"level":2,"title":"fallBackImage","slug":"fallbackimage","link":"#fallbackimage","children":[]},{"level":2,"title":"restrictions","slug":"restrictions","link":"#restrictions","children":[]},{"level":2,"title":"twitterID","slug":"twitterid","link":"#twitterid","children":[]},{"level":2,"title":"isArticle","slug":"isarticle","link":"#isarticle","children":[]},{"level":2,"title":"ogp","slug":"ogp","link":"#ogp","children":[]},{"level":2,"title":"jsonLd","slug":"jsonld","link":"#jsonld","children":[]},{"level":2,"title":"customHead","slug":"customhead","link":"#customhead","children":[]}],"git":{"updatedTime":1706762763000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":2}]},"filePathRelative":"plugins/seo/config.md"}');export{d as comp,u as data}; diff --git a/assets/config.html-DGoed1yi.js b/assets/config.html-DGoed1yi.js new file mode 100644 index 0000000000..7719138111 --- /dev/null +++ b/assets/config.html-DGoed1yi.js @@ -0,0 +1,140 @@ +import{_ as i,r as o,o as p,c as t,b as s,d as n,a as l,e}from"./app-DI49vYHA.js";const r={},c=e('

    Config

    Options

    showInstall

    • Type: boolean
    • Default: false
    • Details: Whether display install button when Service Worker is first registered successfully.

    manifest

    ',5),d=e('
  • Type: AppManifest

  • Details:

    You can fill with an object which will be parsed to manifest.webmanifest.

    TIP

    Some options have their fallback if you don't set them.

    • name: siteConfig.title || siteConfig.locales['/'].title || "Site"
    • short_name: siteConfig.title || siteConfig.locales['/'].title || "Site"
    • description: siteConfig.description || siteConfig.locales['/'].description || "A site built with vuepress"
    • lang: siteConfig.locales['/'].lang || "en-US"
    • start_url: context.base
    • scope: context.base
    • display: "standalone"
    • theme_color: "#46bd87"
    • background_color: "#ffffff"
    • orientation: "portrait-primary"
    • prefer_related_applications: false
  • ',2),D=s("p",null,"Reference:",-1),u={href:"https://developer.mozilla.org/en-US/docs/Web/Manifest",target:"_blank",rel:"noopener noreferrer"},v={href:"https://www.w3.org/TR/appmanifest/",target:"_blank",rel:"noopener noreferrer"},y=e('

    favicon

    • Type: string

    • Details:

      Link of favicon.ico.

      WARNING

      We recommend you to set favicon for your site.

    themeColor

    • Type: string

    • Default: "#46bd87"

    • Details:

      Theme Color of the pwa.

    cacheHTML

    • Type: boolean

    • Default: false

    • Details:

      Whether cache HTML files besides home page and 404 page.

    cacheImage

    • Type: boolean

    • Default: false

    • Details:

      Whether cache pictures

    maxSize

    • Type: number

    • Default: 2048

    • Details:

      Max size allowed to be cached, with KB unit

      WARNING

      This option has the highest priority, and any files exceeding this value will be excluded.

      So if you generate very large HTML or JS files, please consider increasing this value, otherwise your PWA may not work normally in offline mode.

    maxImageSize

    • Type: number

    • Default: 1024

    • Details: Max picture size allowed to be cached, with KB unit

      The value must not be greater than maxSize option

    update

    • Type: "disabled" | "available" | "hint" | "force"

    • Default: "available"

    • Details: Control logic when new content is found.

      • "disabled": Do nothing even when new service worker is available. After new service work succeeds installing and starts waiting, it will control page and provide new content in next visit.

      • "available": Only display update popup when the new service worker is available

      • "hint": Display a hint to let user choose to refresh immediately

        This is helpful when you want users to see new docs immediately.

        TIP

        If users choose to refresh, the current service worker will be unregister, and request will start coming to web. Later the new service worker will start installing and control current page after installed.

      • "force": unregister current service worker immediately then refresh to get new content

        DANGER

        Although this ensures users are viewing the latest content, it may affect viewing experiences.

      TIP

      How docs are updated is controlled by a previous version, so the current option only effect next update from this version.

    apple

    Special settings for better supporting Safari, ignoring these options are safe.

    apple.icon

    • Type: string
    • Details: Icon link used by Safari.

    apple.statusBarColor

    • Type: "black" | "white"
    • Default: "black"
    • Details: Status bar color for Safari

    apple.maskIcon

    • Type: string
    • Details: Safari mask icon

    msTile

    Special settings for Microsoft tiles, ignoring these options are safe.

    msTile.image

    • Type: string
    • Details: Tile image

    msTile.color

    • Type: string
    • Default value: themeColor
    • Details: Tile color

    foundComponent

    • Type: string
    • Default: "PWAFoundPopup"
    • Details: Path of custom hint popup component.

    readyComponent

    • Type: string
    • Default: "PWAReadyPopup"
    • Details: Path of custom update popup component.

    appendBase

    • Type: boolean
    • Default: false
    • Details: Whether append base to all absolute links in options.

    generateSwConfig

    ',35),h=s("p",null,"Details:",-1),m=s("code",null,"workbox-build",-1),C={href:"https://developers.google.com/web/tools/workbox/reference-docs/latest/module-workbox-build#.generateSW",target:"_blank",rel:"noopener noreferrer"},g=e(`

    locales

    • Type: PWAPluginLocaleConfig

      interface PWAPluginLocaleData {
      +  /**
      +   * Install button text
      +   */
      +  install: string
      +
      +  /**
      +   * iOS install hint text
      +   */
      +  iOSInstall: string
      +
      +  /**
      +   * Cancel button text
      +   */
      +  cancel: string
      +
      +  /**
      +   * Close button text
      +   */
      +  close: string
      +
      +  /**
      +   * Previous image text
      +   */
      +  prevImage: string
      +
      +  /**
      +   * Next image text
      +   */
      +  nextImage: string
      +
      +  /**
      +   * Install explain text
      +   */
      +  explain: string
      +
      +  /**
      +   * Description label text
      +   */
      +  desc: string
      +
      +  /**
      +   * Feature label text
      +   */
      +  feature: string
      +
      +  /**
      +   * Update hint text
      +   */
      +  hint: string
      +
      +  /**
      +   * Update available text
      +   */
      +  update: string
      +}
      +
      +interface PWAPluginLocaleConfig {
      +  [localePath: string]: PWAPluginLocaleData
      +}
      +
    • Details: Locales config for pwa plugin.

    Built-in Supported Languages
    • Simplified Chinese (zh-CN)
    • Traditional Chinese (zh-TW)
    • English (United States) (en-US)
    • German (de-DE)
    • German (Australia) (de-AT)
    • Russian (ru-RU)
    • Ukrainian (uk-UA)
    • Vietnamese (vi-VN)
    • Portuguese (Brazil) (pt-BR)
    • Polish (pl-PL)
    • French (fr-FR)
    • Spanish (es-ES)
    • Slovak (sk-SK)
    • Japanese (ja-JP)
    • Turkish (tr-TR)
    • Korean (ko-KR)
    • Finnish (fi-FI)
    • Indonesian (id-ID)
    • Dutch (nl-NL)

    Composition API

    usePwaEvent

    `,5),b=s("p",null,"Details:",-1),f=s("p",null,"Returns the event emitter of this plugin.",-1),E={href:"https://github.com/yyx990803/register-service-worker",target:"_blank",rel:"noopener noreferrer"},w=s("li",null,[s("p",null,"Example:")],-1),k=e(`
    import { usePwaEvent } from '@vuepress/plugin-pwa/client'
    +
    +export default {
    +  setup() {
    +    const event = usePwaEvent()
    +    event.on('ready', (registration) => {
    +      console.log('Service worker is active.')
    +    })
    +  },
    +}
    +

    Utilities

    forceUpdate

    • Details:

      Force update the page when an update is found.

    • Example:

    import { forceUpdate } from '@vuepress/plugin-pwa/client'
    +import { onMounted } from 'vue'
    +
    +export default {
    +  setup() {
    +    onMounted(() => {
    +      forceUpdate()
    +    })
    +  },
    +}
    +

    registerSW

    • Details:

      Register service worker manually.

    • Parameters:

    ParameterTypeDescription
    serviceWorkerPathstringPath of the service worker
    hooksobjectHooks of service worker
    showStatusbooleanLog service worker status in console
    interface Hooks {
    +  registrationOptions?: RegistrationOptions
    +  ready?: (registration: ServiceWorkerRegistration) => void
    +  registered?: (registration: ServiceWorkerRegistration) => void
    +  cached?: (registration: ServiceWorkerRegistration) => void
    +  updated?: (registration: ServiceWorkerRegistration) => void
    +  updatefound?: (registration: ServiceWorkerRegistration) => void
    +  offline?: () => void
    +  error?: (error: Error) => void
    +}
    +
    • Example:
    import { registerSW } from '@vuepress/plugin-pwa/client'
    +import { onMounted } from 'vue'
    +
    +export default {
    +  setup() {
    +    onMounted(() => {
    +      registerSW('/service-worker.js', {
    +        ready(registration) {
    +          console.log('Service worker is active.')
    +        },
    +      })
    +    })
    +  },
    +}
    +

    skipWaiting

    • Details:

      Activate the waiting service worker.

    • Parameters:

    ParameterTypeDescription
    registrationServiceWorkerRegistrationThe registration of the service worker you want activate
    • Example:
    import { usePwaEvent, skipWaiting } from '@vuepress/plugin-pwa/client'
    +
    +export default {
    +  setup() {
    +    const event = usePwaEvent()
    +
    +    event.on('updated', (registration) => {
    +      console.log('The waiting service worker is available.')
    +      // activate the waiting service worker
    +      skipWaiting(registration)
    +    })
    +  },
    +}
    +

    unregisterSW

    • Details:

      Unregister service worker manually.

    • Example:

    import { unregisterSW } from '@vuepress/plugin-pwa/client'
    +import { onMounted } from 'vue'
    +
    +export default {
    +  setup() {
    +    onMounted(() => {
    +      unregisterSW()
    +    })
    +  },
    +}
    +

    Styles

    You can customize the style via CSS variables:

    :root {
    +  --pwa-z-index: 10;
    +  --pwa-color: #2c3e50;
    +  --pwa-bg-color: #ffffff;
    +  --pwa-border-color: #3eaf7c;
    +  --pwa-shadow-color: rgb(0 0 0 / 15%);
    +  --pwa-btn-text-color: #ffffff;
    +  --pwa-btn-bg-color: #3eaf7c;
    +  --pwa-btn-hover-bg-color: #4abf8a;
    +  --pwa-content-color: #333;
    +  --pwa-content-light-color: #666;
    +}
    +
    `,22);function A(x,F){const a=o("ExternalLinkIcon");return p(),t("div",null,[c,s("ul",null,[d,s("li",null,[D,s("ul",null,[s("li",null,[s("a",u,[n("MDN Web Docs: Web App Manifest"),l(a)])]),s("li",null,[s("a",v,[n("W3C: Web App Manifest"),l(a)])])])])]),y,s("ul",null,[s("li",null,[h,s("p",null,[n("Options passed to "),m,n(", for details, see "),s("a",C,[n("Workbox documentation"),l(a)])])])]),g,s("ul",null,[s("li",null,[b,f,s("p",null,[n("You can add listener function to events that provided by "),s("a",E,[n("register-service-worker"),l(a)]),n(".")])]),w]),k])}const _=i(r,[["render",A],["__file","config.html.vue"]]),q=JSON.parse('{"path":"/plugins/pwa/config.html","title":"Config","lang":"en-US","frontmatter":{},"headers":[{"level":2,"title":"Options","slug":"options","link":"#options","children":[{"level":3,"title":"showInstall","slug":"showinstall","link":"#showinstall","children":[]},{"level":3,"title":"manifest","slug":"manifest","link":"#manifest","children":[]},{"level":3,"title":"favicon","slug":"favicon","link":"#favicon","children":[]},{"level":3,"title":"themeColor","slug":"themecolor","link":"#themecolor","children":[]},{"level":3,"title":"cacheHTML","slug":"cachehtml","link":"#cachehtml","children":[]},{"level":3,"title":"cacheImage","slug":"cacheimage","link":"#cacheimage","children":[]},{"level":3,"title":"maxSize","slug":"maxsize","link":"#maxsize","children":[]},{"level":3,"title":"maxImageSize","slug":"maximagesize","link":"#maximagesize","children":[]},{"level":3,"title":"update","slug":"update","link":"#update","children":[]},{"level":3,"title":"apple","slug":"apple","link":"#apple","children":[]},{"level":3,"title":"msTile","slug":"mstile","link":"#mstile","children":[]},{"level":3,"title":"foundComponent","slug":"foundcomponent","link":"#foundcomponent","children":[]},{"level":3,"title":"readyComponent","slug":"readycomponent","link":"#readycomponent","children":[]},{"level":3,"title":"appendBase","slug":"appendbase","link":"#appendbase","children":[]},{"level":3,"title":"generateSwConfig","slug":"generateswconfig","link":"#generateswconfig","children":[]},{"level":3,"title":"locales","slug":"locales","link":"#locales","children":[]}]},{"level":2,"title":"Composition API","slug":"composition-api","link":"#composition-api","children":[{"level":3,"title":"usePwaEvent","slug":"usepwaevent","link":"#usepwaevent","children":[]}]},{"level":2,"title":"Utilities","slug":"utilities","link":"#utilities","children":[{"level":3,"title":"forceUpdate","slug":"forceupdate","link":"#forceupdate","children":[]},{"level":3,"title":"registerSW","slug":"registersw","link":"#registersw","children":[]},{"level":3,"title":"skipWaiting","slug":"skipwaiting","link":"#skipwaiting","children":[]},{"level":3,"title":"unregisterSW","slug":"unregistersw","link":"#unregistersw","children":[]}]},{"level":2,"title":"Styles","slug":"styles","link":"#styles","children":[]}],"git":{"updatedTime":1708365979000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":1}]},"filePathRelative":"plugins/pwa/config.md"}');export{_ as comp,q as data}; diff --git a/assets/config.html-DYMVh00U.js b/assets/config.html-DYMVh00U.js new file mode 100644 index 0000000000..d39da05294 --- /dev/null +++ b/assets/config.html-DYMVh00U.js @@ -0,0 +1,45 @@ +import{_ as s,o as n,c as a,e as l}from"./app-DI49vYHA.js";const e={},p=l(`

    选项

    hostname

    • 类型:string

    • 必填:是

    • 详情:

      部署域名

    author

    • 类型:Author

      type AuthorName = string
      +
      +interface AuthorInfo {
      +  /**
      +   * 作者姓名
      +   */
      +  name: string
      +
      +  /**
      +   * 作者网站
      +   */
      +  url?: string
      +
      +  /**
      +   * 作者 Email
      +   */
      +  email?: string
      +}
      +
      +type Author = AuthorName | AuthorName[] | AuthorInfo | AuthorInfo[]
      +
    • 详情:

      默认作者

    autoDescription

    • 类型:boolean

    • 默认值:true

    • 详情:

      是否自动生成描述

    canonical

    • 类型:string | ((page: Page) => string | null)

    • 详情:

      首选链接

    fallBackImage

    • 类型:string

    • 详情:

      当找不到图片时的回退图片链接

    restrictions

    • 类型:string

    • 详情:

      内容的年龄分级,格式为 [int]+,如 "13+"

    twitterID

    • 类型:string

    • 详情:

      你的 twitter 用户名

    isArticle

    • 类型:(page: Page) => boolean

    • 详情:

      你可以使用此选项判断一个页面是否是文章。

    ogp

    • 类型:

      function ogp(
      +  /** 插件推断的 OGP 信息 */
      +  ogp: SeoContent,
      +  /** 页面对象 */
      +  page: Page,
      +  /** VuePress App */
      +  app: App,
      +): SeoContent
      +
    • 详情:

      自定义 OGP 生成器

      你可以使用此选项来注入新的或覆盖掉默认生成的 OGP 标签。

    jsonLd

    • 类型:

      function jsonLd(
      +  /** 由插件推断出的 JSON-LD 对象 */
      +  jsonLD: ArticleSchema | BlogPostingSchema | WebPageSchema,
      +  /** 页面对象 */
      +  page: Page,
      +  /** VuePress App */
      +  app: App,
      +): ArticleSchema | BlogPostingSchema | WebPageSchema
      +
    • 详情:

      自定义 JSON-LD 生成器

      你可以使用此选项来注入新的或覆盖掉默认生成的 JSON-LD 标签。

    customHead

    • 类型:

      function customHead(
      +  /** head 标签配置 */
      +  head: HeadConfig[],
      +  /** 页面对象 */
      +  page: Page,
      +  /** VuePress App */
      +  app: App,
      +): void
      +
    • 详情:

      你可以使用此选项来直接注入任意格式的标签到 <head>

    `,23),i=[p];function o(c,t){return n(),a("div",null,i)}const d=s(e,[["render",o],["__file","config.html.vue"]]),D=JSON.parse('{"path":"/zh/plugins/seo/config.html","title":"选项","lang":"zh-CN","frontmatter":{},"headers":[{"level":2,"title":"hostname","slug":"hostname","link":"#hostname","children":[]},{"level":2,"title":"author","slug":"author","link":"#author","children":[]},{"level":2,"title":"autoDescription","slug":"autodescription","link":"#autodescription","children":[]},{"level":2,"title":"canonical","slug":"canonical","link":"#canonical","children":[]},{"level":2,"title":"fallBackImage","slug":"fallbackimage","link":"#fallbackimage","children":[]},{"level":2,"title":"restrictions","slug":"restrictions","link":"#restrictions","children":[]},{"level":2,"title":"twitterID","slug":"twitterid","link":"#twitterid","children":[]},{"level":2,"title":"isArticle","slug":"isarticle","link":"#isarticle","children":[]},{"level":2,"title":"ogp","slug":"ogp","link":"#ogp","children":[]},{"level":2,"title":"jsonLd","slug":"jsonld","link":"#jsonld","children":[]},{"level":2,"title":"customHead","slug":"customhead","link":"#customhead","children":[]}],"git":{"updatedTime":1706801200000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":3}]},"filePathRelative":"zh/plugins/seo/config.md"}');export{d as comp,D as data}; diff --git a/assets/config.html-Dc4epx8_.js b/assets/config.html-Dc4epx8_.js new file mode 100644 index 0000000000..58fa846509 --- /dev/null +++ b/assets/config.html-Dc4epx8_.js @@ -0,0 +1,143 @@ +import{_ as o,r as i,o as c,c as t,b as s,a,w as e,e as p,d as n}from"./app-DI49vYHA.js";const r={},d=p('

    配置

    插件选项

    getInfo

    ',3),D=s("li",null,[s("p",null,[n("类型: "),s("code",null,"(page: Page) => Record")])],-1),y=s("li",null,[s("p",null,"必填: 否")],-1),u=s("p",null,"参考:",-1),v=s("li",null,[s("p",null,"详情:"),s("p",null,"获取文章信息的函数。"),s("p",null,"获取到的信息会被稍后注入至路由元数据,以便你可以在客户端中通过组合式 API 获取。")],-1),C=s("h3",{id:"filter",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#filter"},[s("span",null,"filter")])],-1),m=s("li",null,[s("p",null,[n("类型: "),s("code",null,"(page: Page) => boolean")])],-1),E=s("li",null,[s("p",null,[n("默认值: "),s("code",null,"(page) => Boolean(page.filePathRelative) && !page.frontmatter.home")])],-1),g=s("p",null,"参考:",-1),b=s("li",null,[s("p",null,"详情:"),s("p",null,"页面过滤器,此函数用于鉴别页面是否作为文章。"),s("p",null,"默认情况下,所有从 Markdown 源文件中生成的非主页页面,会被作为文章。")],-1),h=s("h3",{id:"category",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#category"},[s("span",null,"category")])],-1),B=s("li",null,[s("p",null,[n("类型: "),s("code",null,"BlogCategoryOptions[]")])],-1),A=s("li",null,[s("p",null,"必填: 否")],-1),_=s("p",null,"详情:",-1),f=s("li",null,[s("p",null,"详情:"),s("p",null,[n("博客分类配置,详见 "),s("a",{href:"#%E5%8D%9A%E5%AE%A2%E5%88%86%E7%B1%BB%E9%85%8D%E7%BD%AE"},"博客分类配置"),n("。")])],-1),k=s("h3",{id:"type",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#type"},[s("span",null,"type")])],-1),x=s("li",null,[s("p",null,[n("类型: "),s("code",null,"BlogTypeOptions[]")])],-1),F=s("li",null,[s("p",null,"必填: 否")],-1),R=s("p",null,"参考:",-1),P=s("li",null,[s("p",null,"详情:"),s("p",null,[n("博客分类配置,详见 "),s("a",{href:"#%E5%8D%9A%E5%AE%A2%E7%B1%BB%E5%9E%8B%E9%85%8D%E7%BD%AE"},"博客类型配置"),n("。")])],-1),T=p('

    slugify

    • 类型: (name: string) => string
    • 默认值: (name) => name.replace(/ _/g, '-').replace(/[:?*|\\\\/<>]/g, "").toLowerCase()
    • 详情:Slugify 函数,用于转换 key 在路由中注册的形式。

    excerpt

    • 类型: boolean
    • 默认值: true
    • 详情:是否生成摘要。

    excerptSeparator

    • 类型: string
    • 默认值: <!-- more -->
    • 详情:摘要分隔符。

    excerptLength

    ',7),w=s("li",null,[s("p",null,[n("类型: "),s("code",null,"number")])],-1),z=s("li",null,[s("p",null,[n("默认值: "),s("code",null,"300")])],-1),L=s("p",null,"参考:",-1),I=s("li",null,[s("p",null,"详情:"),s("p",null,"自动生成的摘要的长度。"),s("div",{class:"custom-container tip"},[s("p",{class:"custom-container-title"},"提示"),s("p",null,[n("摘要的长度会尽可能的接近这个值。如果设置为 "),s("code",null,"0"),n(",意味着不自动生成摘要。")])])],-1),N=s("h3",{id:"excerptfilter",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#excerptfilter"},[s("span",null,"excerptFilter")])],-1),S=s("li",null,[s("p",null,[n("类型: "),s("code",null,"(page: Page) => boolean")])],-1),M=s("li",null,[s("p",null,[n("默认值: "),s("code",null,"filter"),n(" 选项")])],-1),O=s("p",null,"参考:",-1),q=s("li",null,[s("p",null,"详情: 页面过滤器,此函数用于鉴别插件是否需要生成摘要。"),s("div",{class:"custom-container tip"},[s("p",{class:"custom-container-title"},"提示"),s("p",null,[n("你可以使用此函数来跳过你不需要生成摘要的页面。例如:如果用户在 frontmatter 中设置了 "),s("code",null,"excerpt"),n(" 或 "),s("code",null,"description"),n(",你可能希望直接使用它们。")])])],-1),V=s("h3",{id:"iscustomelement",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#iscustomelement"},[s("span",null,"isCustomElement")])],-1),H=s("li",null,[s("p",null,[n("类型: "),s("code",null,"(tagName: string) => boolean")])],-1),J=s("li",null,[s("p",null,[n("默认值: "),s("code",null,"() => false")])],-1),j=s("p",null,"参考:",-1),G=s("li",null,[s("p",null,"详情: 被认为是自定义元素的标签。"),s("p",null,"用于判断一个标签是否是自定义元素,因为在摘要中,所有的未知标签都会被移除。")],-1),K=p(`

    metaScope

    • 类型: string

    • 默认值: "_blog"

    • 详情: 注入文章信息至路由元数据时使用的键名。

      提示

      设置为空字符串会直接注入路由元数据 (而不是一个键下)。

    hotReload

    • 类型: boolean

    • 默认值: 是否使用 --debug 标记

    • 详情: 是否在开发服务器中启用实时热重载。

      致主题开发者

      默认情况下它是禁用的,因为它确实会对具有很多分类和类别的站点产生性能影响,并且在编辑 Markdown 时会减慢热重载的速度。

      如果用户正在添加或组织类别或标签,你可以告诉他们启用此功能,其余的时间最好禁用它。

      此外,你可以尝试检测用户项目中的页面数并决定是否启用它。

    博客分类配置

    博客分类配置应为一个数组,每一项控制一个分类规则。

    interface BlogCategoryOptions {
    +  /**
    +   * 唯一的分类名称
    +   */
    +  key: string
    +
    +  /**
    +   * 从页面中获取分类的函数
    +   */
    +  getter: (page: Page) => string[]
    +
    +  /**
    +   * 页面排序器
    +   */
    +  sorter?: (pageA: Page, pageB: Page) => number
    +
    +  /**
    +   * 待注册的页面路径图案
    +   *
    +   * @description \`:key\` 将会被替换为原 key 的 slugify 结果
    +   *
    +   * @default \`/:key/\`
    +   */
    +  path?: string | false
    +
    +  /**
    +   * 页面布局组件名称
    +   *
    +   * @default 'Layout'
    +   */
    +  layout?: string
    +
    +  /**
    +   * Front Matter 配置
    +   */
    +  frontmatter?: (localePath: string) => Record<string, string>
    +
    +  /**
    +   * 待注册的项目页面路径图案或自定义函数
    +   *
    +   * @description 当填入字符串的时候, \`:key\` 和 \`:name\` 会被自动替换为原始的 key、name 的 slugify 结果。
    +   *
    +   * @default \`/:key/:name/\`
    +   */
    +  itemPath?: string | ((name: string) => string) | false
    +
    +  /**
    +   * 项目页面布局组件名称
    +   *
    +   * @default 'Layout'
    +   */
    +  itemLayout?: string
    +
    +  /**
    +   * 项目 Front Matter 配置
    +   */
    +  itemFrontmatter?: (name: string, localePath: string) => Record<string, string>
    +}
    +

    博客类型配置

    博客类型配置应为一个数组,每一项控制一个类型规则。

    interface BlogTypeOptions {
    +  /**
    +   * 唯一的类型名称
    +   */
    +  key: string
    +
    +  /**
    +   * 一个过滤函数来决定页面是否满足此类型
    +   */
    +  filter: (page: Page) => boolean
    +
    +  /**
    +   * 页面排序器
    +   */
    +  sorter?: (pageA: Page, pageB: Page) => number
    +
    +  /**
    +   * 待注册的页面路径
    +   *
    +   * @default '/:key/'
    +   */
    +  path?: string | false
    +
    +  /**
    +   * 页面布局组件名称
    +   *
    +   * @default 'Layout'
    +   */
    +  layout?: string
    +
    +  /**
    +   * Front Matter 配置
    +   */
    +  frontmatter?: (localePath: string) => Record<string, string>
    +}
    +

    可组合式 API

    你可以从 @vuepress/plugin-blog/client 导入下列 API:

    • 博客分类

      const useBlogCategory: <
      +  T extends Record<string, unknown> = Record<string, unknown>,
      +>(
      +  key?: string,
      +) => ComputedRef<BlogCategoryData<T>>
      +

      参数 key 为需要获取的键名。如果未传入 key,会尝试使用与当前路径匹配的 key。

    • 博客类型

      const useBlogType: <
      +  T extends Record<string, unknown> = Record<string, unknown>,
      +>(
      +  key?: string,
      +) => ComputedRef<BlogTypeData<T>>
      +

      参数 key 为需要获取的键名。如果未传入 key,会尝试使用与当前路径匹配的 key。

    详细的返回值如下:

    interface Article<T extends Record<string, unknown> = Record<string, unknown>> {
    +  /** 文章路径 */
    +  path: string
    +  /** 文章信息 */
    +  info: T
    +}
    +
    +interface BlogCategoryData<
    +  T extends Record<string, unknown> = Record<string, unknown>,
    +> {
    +  /** 分类路径 */
    +  path: string
    +
    +  /**
    +   * 仅当当前路径和某个子项目匹配时可用
    +   */
    +  currentItems?: Article<T>[]
    +
    +  /** 分类映射 */
    +  map: {
    +    /** 当前分类下全局唯一的 key */
    +    [key: string]: {
    +      /** 对应键值的分类路径 */
    +      path: string
    +      /** 对应键值的项目 */
    +      items: Article<T>[]
    +    }
    +  }
    +}
    +
    +interface BlogTypeData<
    +  T extends Record<string, unknown> = Record<string, unknown>,
    +> {
    +  /** 类别路径 */
    +  path: string
    +
    +  /** 当前类别下的项目 */
    +  items: Article<T>[]
    +}
    +
    `,15);function Q(U,W){const l=i("RouteLink");return c(),t("div",null,[d,s("ul",null,[D,y,s("li",null,[u,s("ul",null,[s("li",null,[a(l,{to:"/zh/plugins/blog/guide.html#%E6%94%B6%E9%9B%86%E6%96%87%E7%AB%A0%E5%B9%B6%E7%94%9F%E6%88%90%E4%BF%A1%E6%81%AF"},{default:e(()=>[n("指南 → 收集文章并生成信息")]),_:1})])])]),v]),C,s("ul",null,[m,E,s("li",null,[g,s("ul",null,[s("li",null,[a(l,{to:"/zh/plugins/blog/guide.html#%E6%94%B6%E9%9B%86%E6%96%87%E7%AB%A0%E5%B9%B6%E7%94%9F%E6%88%90%E4%BF%A1%E6%81%AF"},{default:e(()=>[n("指南 → 收集文章并生成信息")]),_:1})])])]),b]),h,s("ul",null,[B,A,s("li",null,[_,s("ul",null,[s("li",null,[a(l,{to:"/zh/plugins/blog/guide.html#%E8%87%AA%E5%AE%9A%E4%B9%89%E7%B1%BB%E5%88%AB%E5%92%8C%E7%B1%BB%E5%9E%8B"},{default:e(()=>[n("指南 → 自定义类别和类型")]),_:1})])])]),f]),k,s("ul",null,[x,F,s("li",null,[R,s("ul",null,[s("li",null,[a(l,{to:"/zh/plugins/blog/guide.html#%E8%87%AA%E5%AE%9A%E4%B9%89%E7%B1%BB%E5%88%AB%E5%92%8C%E7%B1%BB%E5%9E%8B"},{default:e(()=>[n("指南 → 自定义类别和类型")]),_:1})])])]),P]),T,s("ul",null,[w,z,s("li",null,[L,s("ul",null,[s("li",null,[a(l,{to:"/zh/plugins/blog/guide.html#%E6%91%98%E8%A6%81%E7%94%9F%E6%88%90"},{default:e(()=>[n("指南 → 摘要生成")]),_:1})])])]),I]),N,s("ul",null,[S,M,s("li",null,[O,s("ul",null,[s("li",null,[a(l,{to:"/zh/plugins/blog/guide.html#%E6%91%98%E8%A6%81%E7%94%9F%E6%88%90"},{default:e(()=>[n("指南 → 摘要生成")]),_:1})])])]),q]),V,s("ul",null,[H,J,s("li",null,[j,s("ul",null,[s("li",null,[a(l,{to:"/zh/plugins/blog/guide.html#%E6%91%98%E8%A6%81%E7%94%9F%E6%88%90"},{default:e(()=>[n("指南 → 摘要生成")]),_:1})])])]),G]),K])}const Y=o(r,[["render",Q],["__file","config.html.vue"]]),Z=JSON.parse('{"path":"/zh/plugins/blog/config.html","title":"配置","lang":"zh-CN","frontmatter":{},"headers":[{"level":2,"title":"插件选项","slug":"插件选项","link":"#插件选项","children":[{"level":3,"title":"getInfo","slug":"getinfo","link":"#getinfo","children":[]},{"level":3,"title":"filter","slug":"filter","link":"#filter","children":[]},{"level":3,"title":"category","slug":"category","link":"#category","children":[]},{"level":3,"title":"type","slug":"type","link":"#type","children":[]},{"level":3,"title":"slugify","slug":"slugify","link":"#slugify","children":[]},{"level":3,"title":"excerpt","slug":"excerpt","link":"#excerpt","children":[]},{"level":3,"title":"excerptSeparator","slug":"excerptseparator","link":"#excerptseparator","children":[]},{"level":3,"title":"excerptLength","slug":"excerptlength","link":"#excerptlength","children":[]},{"level":3,"title":"excerptFilter","slug":"excerptfilter","link":"#excerptfilter","children":[]},{"level":3,"title":"isCustomElement","slug":"iscustomelement","link":"#iscustomelement","children":[]},{"level":3,"title":"metaScope","slug":"metascope","link":"#metascope","children":[]},{"level":3,"title":"hotReload","slug":"hotreload","link":"#hotreload","children":[]}]},{"level":2,"title":"博客分类配置","slug":"博客分类配置","link":"#博客分类配置","children":[]},{"level":2,"title":"博客类型配置","slug":"博客类型配置","link":"#博客类型配置","children":[]},{"level":2,"title":"可组合式 API","slug":"可组合式-api","link":"#可组合式-api","children":[]}],"git":{"updatedTime":1707125652000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":1}]},"filePathRelative":"zh/plugins/blog/config.md"}');export{Y as comp,Z as data}; diff --git a/assets/config.html-DcZlIOB_.js b/assets/config.html-DcZlIOB_.js new file mode 100644 index 0000000000..c05fcf8db4 --- /dev/null +++ b/assets/config.html-DcZlIOB_.js @@ -0,0 +1,18 @@ +import{_ as o,r as p,o as i,c,b as e,d as s,a,w as n,e as t}from"./app-DI49vYHA.js";const r={},d=t(`

    插件配置

    hostname

    • 类型:string
    • 必填:是

    部署网站的域名。

    atom

    • 类型:boolean
    • 默认值:false

    是否启用 Atom 格式输出。

    json

    • 类型:boolean
    • 默认值:false

    是否启用 JSON 格式输出。

    rss

    • 类型:boolean
    • 默认值:false

    是否启用 RSS 格式输出。

    image

    • 类型:string

    一个大的图片,用作 feed 展示。

    icon

    • 类型:string

    一个小的图标,显示在订阅列表中。

    count

    • 类型:number
    • 默认值:100

    设置 feed 的最大项目数量。在所有页面排序好后,插件会截取前 count 个项目。

    如果你的站点文章很多,你应该考虑设置这个选项以减少 feed 文件大小。

    preservedElements

    • 类型:(RegExp | string)[] | (tagName:string) => boolean

    应在 Feed 中保留的自定义元素或组件。

    默认情况下,所有未知标签均会被移除。

    filter

    • 类型:(page: Page)=> boolean

    • 默认值:

      ;({ frontmatter, filePathRelative }: Page): boolean =>
      +  !(
      +    frontmatter.home ||
      +    !filePathRelative ||
      +    frontmatter.article === false ||
      +    frontmatter.feed === false
      +  )
      +

    自定义的过滤函数,用于过滤哪些项目在 feed 中显示。

    sorter

    • 类型: (pageA: Page, pageB: Page)=> number

    • 默认值:

      // dateSorter 来源于 @vuepress/helper
      +;(pageA, pageB): number =>
      +  dateSorter(
      +    pageA.data.git?.createdTime
      +      ? new Date(pageA.data.git?.createdTime)
      +      : pageA.frontmatter.date,
      +    pageB.data.git?.createdTime
      +      ? new Date(pageB.data.git?.createdTime)
      +      : pageB.frontmatter.date,
      +  )
      +

    Feed 项目的排序器。

    默认的排序行为是通过 Git 的文件添加日期 (需要 @vuepress/plugin-git)。

    提示

    你应该启用 @vuepress/plugin-git 来获取最新创建的页面作为 feed 项目。否则,feed 项目将按照 VuePress 中页面的默认顺序排序。

    channel

    channel 选项用于配置 Feed 频道。

    `,37),D=t('

    devServer

    • 类型:boolean
    • 默认值:false

    是否在开发服务器中启用

    提示

    由于性能原因,我们不提供热更新。重启开发服务器以同步你的变更。

    devHostname

    • 类型:string
    • 默认值:"http://localhost:${port}"

    开发服务器使用的主机名

    atomOutputFilename

    • 类型:string
    • 默认值:"atom.xml"

    Atom 格式输出路径,相对于输出路径。

    atomXslTemplate

    • 类型:string
    • 默认值:@vuepress/plugin-feed/templates/atom.xsl 的内容

    Atom xsl 模板文件没人陪美国

    atomXslFilename

    • 类型:string
    • 默认值:"atom.xsl"

    Atom xsl 输出路径,相对于输出路径。

    jsonOutputFilename

    • 类型:string
    • 默认值:"feed.json"

    JSON 格式输出路径,相对于输出路径。

    rssOutputFilename

    • 类型:string
    • 默认值:"rss.xml"

    RSS 格式输出路径,相对于输出路径。

    rssXslTemplate

    • 类型:string
    • 默认值:@vuepress/plugin-feed/templates/rss.xsl 的内容

    RSS xsl 模板文件内容。

    rssXslFilename

    • 类型:string
    • 默认值:"rss.xsl"

    RSS xsl 输出路径,相对于输出路径。

    getter

    ',29),u=e("div",{class:"custom-container tip"},[e("p",{class:"custom-container-title"},"此插件内置了生成器,只有当你想完全控制 feed 生成时才需要设置此选项。")],-1),h=e("h2",{id:"locales",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#locales"},[e("span",null,"locales")])],-1),m=e("ul",null,[e("li",null,[s("类型:"),e("code",null,"Record")])],-1),v=e("p",null,"你可以将它用于每个语言环境的特定选项。",-1),g=e("p",null,[s("除 "),e("code",null,"hostname"),s(" 外,上述任何选项均受支持。")],-1);function y(f,C){const l=p("RouteLink");return i(),c("div",null,[d,e("p",null,[s("可用选项详见 "),a(l,{to:"/zh/plugins/feed/channel.html"},{default:n(()=>[s("配置 → 频道设置")]),_:1})]),D,e("p",null,[s("Feed 生成控制器,详见 "),a(l,{to:"/zh/plugins/feed/getter.html"},{default:n(()=>[s("Feed 生成器")]),_:1}),s("。")]),u,h,m,v,g])}const x=o(r,[["render",y],["__file","config.html.vue"]]),F=JSON.parse('{"path":"/zh/plugins/feed/config.html","title":"插件配置","lang":"zh-CN","frontmatter":{},"headers":[{"level":2,"title":"hostname","slug":"hostname","link":"#hostname","children":[]},{"level":2,"title":"atom","slug":"atom","link":"#atom","children":[]},{"level":2,"title":"json","slug":"json","link":"#json","children":[]},{"level":2,"title":"rss","slug":"rss","link":"#rss","children":[]},{"level":2,"title":"image","slug":"image","link":"#image","children":[]},{"level":2,"title":"icon","slug":"icon","link":"#icon","children":[]},{"level":2,"title":"count","slug":"count","link":"#count","children":[]},{"level":2,"title":"preservedElements","slug":"preservedelements","link":"#preservedelements","children":[]},{"level":2,"title":"filter","slug":"filter","link":"#filter","children":[]},{"level":2,"title":"sorter","slug":"sorter","link":"#sorter","children":[]},{"level":2,"title":"channel","slug":"channel","link":"#channel","children":[]},{"level":2,"title":"devServer","slug":"devserver","link":"#devserver","children":[]},{"level":2,"title":"devHostname","slug":"devhostname","link":"#devhostname","children":[]},{"level":2,"title":"atomOutputFilename","slug":"atomoutputfilename","link":"#atomoutputfilename","children":[]},{"level":2,"title":"atomXslTemplate","slug":"atomxsltemplate","link":"#atomxsltemplate","children":[]},{"level":2,"title":"atomXslFilename","slug":"atomxslfilename","link":"#atomxslfilename","children":[]},{"level":2,"title":"jsonOutputFilename","slug":"jsonoutputfilename","link":"#jsonoutputfilename","children":[]},{"level":2,"title":"rssOutputFilename","slug":"rssoutputfilename","link":"#rssoutputfilename","children":[]},{"level":2,"title":"rssXslTemplate","slug":"rssxsltemplate","link":"#rssxsltemplate","children":[]},{"level":2,"title":"rssXslFilename","slug":"rssxslfilename","link":"#rssxslfilename","children":[]},{"level":2,"title":"getter","slug":"getter","link":"#getter","children":[]},{"level":2,"title":"locales","slug":"locales","link":"#locales","children":[]}],"git":{"updatedTime":1706854470000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":3}]},"filePathRelative":"zh/plugins/feed/config.md"}');export{x as comp,F as data}; diff --git a/assets/config.html-DfnnBIqz.js b/assets/config.html-DfnnBIqz.js new file mode 100644 index 0000000000..2ca5b1466a --- /dev/null +++ b/assets/config.html-DfnnBIqz.js @@ -0,0 +1 @@ +import{_ as a,r as s,o as p,c as n,b as e,d as l,a as o,w as r,e as t}from"./app-DI49vYHA.js";const d={},c=t('

    Config

    hostname

    • Type: string

    • Required: Yes

    • Details:

      The domain name where the current site is deployed, the plugin needs this option to work.

    extraUrls

    • Type: string[]

    • Details:

      Extra link to be included.

      If you have some links not including in VuePress Router (normally in public directory or generated by other tools directly), you may need this option.

    • Example: ['/about.html', '/api/']

    excludePaths

    • Type: string[]

    • Default: ['/404.html']

    • Details:

      Urls excluding from sitemap, starting with absolute path.

      By default, all the urls generated by VuePress (excluding 404 page) will be added into sitemap.

    devServer

    • Type: boolean

    • Default: false

    • Details:

      Whether enabled in devServer.

    TIP

    For performance reasons, we do not provide hot reload. Reboot your devServer to sync your changes.

    devHostname

    • Type: string

    • Default: "http://localhost:${port}"

    • Details:

      Hostname to use in devServer

    sitemapFilename

    • Type: string

    • Default value: "sitemap.xml"

    • Details:

      The output filename, relative to output directory.

    sitemapXSLFilename

    • Type: string

    • Default value: "sitemap.xsl"

    • Details:

      Output xsl filename, relative to dest folder.

    sitemapXSLTemplate

    • Type: string

    • Default value: "@vuepress/plugin-sitemap/templates/sitemap.xsl"

    • Details:

      XSL content used as template.

    changefreq

    ',19),h=e("li",null,[e("p",null,[l("Type: "),e("code",null,'"always" | "hourly" | "daily" | "weekly" |"monthly" | "yearly" | "never"')])],-1),u=e("li",null,[e("p",null,[l("Default value: "),e("code",null,'"daily"')])],-1),m=e("p",null,"Details:",-1),f=t('

    priority

    • Type: number

    • Default: 0.5

    • Details:

      Page priority, from 0 to 1.

    modifyTimeGetter

    • Type: (page: Page, app: App) => string

    • Details:

      Last modify time getter. By default, the plugin will use the timestamp generated by git plugin.

    ',4);function g(v,y){const i=s("RouteLink");return p(),n("div",null,[c,e("ul",null,[h,u,e("li",null,[m,e("p",null,[l("Page default update frequency, will be overridden by "),o(i,{to:"/plugins/sitemap/frontmatter.html#sitemap-changefreq"},{default:r(()=>[l("sitemap.changefreq")]),_:1}),l(" in Frontmatter.")])])]),f])}const b=a(d,[["render",g],["__file","config.html.vue"]]),_=JSON.parse('{"path":"/plugins/sitemap/config.html","title":"Config","lang":"en-US","frontmatter":{},"headers":[{"level":2,"title":"hostname","slug":"hostname","link":"#hostname","children":[]},{"level":2,"title":"extraUrls","slug":"extraurls","link":"#extraurls","children":[]},{"level":2,"title":"excludePaths","slug":"excludepaths","link":"#excludepaths","children":[]},{"level":2,"title":"devServer","slug":"devserver","link":"#devserver","children":[]},{"level":2,"title":"devHostname","slug":"devhostname","link":"#devhostname","children":[]},{"level":2,"title":"sitemapFilename","slug":"sitemapfilename","link":"#sitemapfilename","children":[]},{"level":2,"title":"sitemapXSLFilename","slug":"sitemapxslfilename","link":"#sitemapxslfilename","children":[]},{"level":2,"title":"sitemapXSLTemplate","slug":"sitemapxsltemplate","link":"#sitemapxsltemplate","children":[]},{"level":2,"title":"changefreq","slug":"changefreq","link":"#changefreq","children":[]},{"level":2,"title":"priority","slug":"priority","link":"#priority","children":[]},{"level":2,"title":"modifyTimeGetter","slug":"modifytimegetter","link":"#modifytimegetter","children":[]}],"git":{"updatedTime":1706723991000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":3}]},"filePathRelative":"plugins/sitemap/config.md"}');export{b as comp,_ as data}; diff --git a/assets/config.html-FMBVZuNJ.js b/assets/config.html-FMBVZuNJ.js new file mode 100644 index 0000000000..e8a399680f --- /dev/null +++ b/assets/config.html-FMBVZuNJ.js @@ -0,0 +1,143 @@ +import{_ as o,r as t,o as i,c,b as s,a as e,w as a,d as n,e as p}from"./app-DI49vYHA.js";const r={},d=p('

    Config

    Plugin Options

    getInfo

    ',3),u=s("li",null,[s("p",null,[n("Type: "),s("code",null,"(page: Page) => Record")])],-1),y=s("li",null,[s("p",null,"Required: No")],-1),D=s("p",null,"Reference:",-1),g=s("li",null,[s("p",null,"Details:"),s("p",null,"Function getting article info."),s("p",null,"Article info will be injected in route meta so that they will be available later in client composables.")],-1),v=s("h3",{id:"filter",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#filter"},[s("span",null,"filter")])],-1),m=s("li",null,[s("p",null,[n("Type: "),s("code",null,"(page: Page) => boolean")])],-1),h=s("li",null,[s("p",null,[n("Default: "),s("code",null,"(page) => Boolean(page.filePathRelative) && !page.frontmatter.home")])],-1),C=s("p",null,"Reference:",-1),b=s("li",null,[s("p",null,"Details:"),s("p",null,"Page filter, determine whether a page should be included."),s("p",null,"By default, all the pages generated from Markdown files but not homepage will be included as articles.")],-1),f=s("h3",{id:"category",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#category"},[s("span",null,"category")])],-1),E=s("li",null,[n("Type: "),s("code",null,"BlogCategoryOptions[]")],-1),B=s("li",null,"Required: No",-1),A=s("li",null,[n("Details: Blog category config, see "),s("a",{href:"#blog-category-config"},"Blog Category Config")],-1),_=s("h3",{id:"type",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#type"},[s("span",null,"type")])],-1),x=s("li",null,[n("Type: "),s("code",null,"BlogTypeOptions[]")],-1),k=s("li",null,"Required: No",-1),F=s("li",null,[n("Details: Blog type config, see "),s("a",{href:"#blog-type-config"},"Blog Type Config")],-1),w=s("h3",{id:"slugify",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#slugify"},[s("span",null,"slugify")])],-1),T=s("ul",null,[s("li",null,[n("Type: "),s("code",null,"(name: string) => string")]),s("li",null,[n("Default: "),s("code",null,`(name) => name.replace(/ _/g, '-').replace(/[:?*|\\\\/<>]/g, "").toLowerCase()`)]),s("li",null,"Details: Slugify function, used to convert key name which they are register in routes.")],-1),R=s("h3",{id:"excerpt",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#excerpt"},[s("span",null,"excerpt")])],-1),P=s("li",null,[n("Type: "),s("code",null,"boolean")],-1),I=s("li",null,[n("Default: "),s("code",null,"true")],-1),q=s("li",null,"Details: Whether generate excerpt for page.",-1),G=s("h3",{id:"excerptseparator",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#excerptseparator"},[s("span",null,"excerptSeparator")])],-1),L=s("li",null,[n("Type: "),s("code",null,"string")],-1),S=s("li",null,[n("Default: "),s("code",null,"")],-1),N=s("li",null,"Details: Separator used to split excerpt from page content.",-1),O=s("h3",{id:"excerptlength",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#excerptlength"},[s("span",null,"excerptLength")])],-1),z=s("li",null,[s("p",null,[n("Type: "),s("code",null,"number")])],-1),j=s("li",null,[s("p",null,[n("Default: "),s("code",null,"300")])],-1),U=s("p",null,"Reference:",-1),V=s("li",null,[s("p",null,"Details:"),s("p",null,"Length of excerpt when auto generating."),s("div",{class:"custom-container tip"},[s("p",{class:"custom-container-title"},"TIP"),s("p",null,"Excerpt length will be the minimal possible length reaching this value."),s("p",null,[n("You can set it to "),s("code",null,"0"),n(" to disable auto excerpt generation.")])])],-1),W=s("h3",{id:"excerptfilter",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#excerptfilter"},[s("span",null,"excerptFilter")])],-1),M=s("li",null,[s("p",null,[n("Type: "),s("code",null,"(page: Page) => boolean")])],-1),Y=s("li",null,[s("p",null,[n("Default: "),s("code",null,"filter"),n(" option")])],-1),H=s("p",null,"Reference:",-1),J=s("li",null,[s("p",null,"Details:"),s("p",null,"Page filter, determine whether the plugin should generate excerpt for it."),s("div",{class:"custom-container tip"},[s("p",{class:"custom-container-title"},"TIP"),s("p",null,[n("You should use this to skip pages that you don't need to generate excerpt for. E.g.: If users set "),s("code",null,"excerpt"),n(" or "),s("code",null,"description"),n(" in frontmatter, you may want to use them directly.")])])],-1),K=s("h3",{id:"iscustomelement",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#iscustomelement"},[s("span",null,"isCustomElement")])],-1),Q=s("li",null,[s("p",null,[n("Type: "),s("code",null,"(tagName: string) => boolean")])],-1),X=s("li",null,[s("p",null,[n("Default: "),s("code",null,"() => false")])],-1),Z=s("p",null,"Reference:",-1),$=s("li",null,[s("p",null,"Details:"),s("p",null,"Tags which is considered as custom elements."),s("p",null,"This is used to determine whether a tag is a custom element since all unknown tags are removed in excerpt.")],-1),ss=p(`

    metaScope

    • Type: string

    • Default: "_blog"

    • Details:

      Key used when injecting info to route meta.

      TIP

      Setting to an empty key will inject to route meta directly instead of a field.

    hotReload

    • Type: boolean

    • Default: Whether using --debug flag

    • Details:

      Whether enable hotReload in devServer.

      To theme developers

      It's disabled by default because it does have performance impact in sites with a lot of categories and types. And it can slow down hotReload speed when editing Markdown.

      If users are adding or organizing your categories or tags, you may tell them to enable this, for the rest it's better to keep it disabled.

      Also, you can try to detect number of pages in users project and decide whether to enable it.

    Blog Category Config

    Blog category config should be an array, while each item is controlling a "category" rule.

    interface BlogCategoryOptions {
    +  /**
    +   * Unique category name
    +   */
    +  key: string
    +
    +  /**
    +   * Function getting category from page
    +   */
    +  getter: (page: Page) => string[]
    +
    +  /**
    +   * A custom function to sort the pages
    +   */
    +  sorter?: (pageA: Page, pageB: Page) => number
    +
    +  /**
    +   * Path pattern of page to be registered
    +   *
    +   * @description \`:key\` will be replaced by the "slugify" result of the original key
    +   *
    +   * @default \`/:key/\`
    +   */
    +  path?: string
    +
    +  /**
    +   * Page layout name
    +   *
    +   * @default 'Layout'
    +   */
    +  layout?: string
    +
    +  /**
    +   * Frontmatter
    +   */
    +  frontmatter?: (localePath: string) => Record<string, string>
    +
    +  /**
    +   * Item page path pattern or custom function to be registered
    +   *
    +   * @description When filling in a string, \`:key\` and \`:name\` will be replaced by the "slugify" result of the original key and name
    +   *
    +   * @default \`/:key/:name/\`
    +   */
    +  itemPath?: string | ((name: string) => string)
    +
    +  /**
    +   * Item page layout name
    +   *
    +   * @default 'Layout'
    +   */
    +  itemLayout?: string
    +
    +  /**
    +   * Items Frontmatter
    +   */
    +  itemFrontmatter?: (name: string, localePath: string) => Record<string, string>
    +}
    +

    Blog Type Config

    Blog type config should be an array, while each item is controlling a "type" rule.

    interface BlogTypeOptions {
    +  /**
    +   * Unique type name
    +   */
    +  key: string
    +
    +  /**
    +   * A filter function to determine whether a page should be the type
    +   */
    +  filter: (page: Page) => boolean
    +
    +  /**
    +   * A custom function to sort the pages
    +   */
    +  sorter?: (pageA: Page, pageB: Page) => number
    +
    +  /**
    +   * Page path to be registered
    +   *
    +   * @default '/:key/'
    +   */
    +  path?: string
    +
    +  /**
    +   * Layout name
    +   *
    +   * @default 'Layout'
    +   */
    +  layout?: string
    +
    +  /**
    +   * Frontmatter
    +   */
    +  frontmatter?: (localePath: string) => Record<string, string>
    +}
    +

    Composition API

    You can import the following API from @vuepress/plugin-blog/client.

    • Blog category

      const useBlogCategory: <
      +  T extends Record<string, unknown> = Record<string, unknown>,
      +>(
      +  key?: string,
      +) => ComputedRef<BlogCategoryData<T>>
      +

      Argument key should be the category unique key.

      If no key is passed, the plugin will try to use the key in current path.

    • Blog category

      const useBlogType: <
      +  T extends Record<string, unknown> = Record<string, unknown>,
      +>(
      +  key?: string,
      +) => ComputedRef<BlogTypeData<T>>
      +

      Argument key should be the type unique key.

      If no key is passed, the plugin will try to use the key in current path.

    Returning values are:

    interface Article<T extends Record<string, unknown> = Record<string, unknown>> {
    +  /** Article path */
    +  path: string
    +  /** Article info */
    +  info: T
    +}
    +
    +interface BlogCategoryData<
    +  T extends Record<string, unknown> = Record<string, unknown>,
    +> {
    +  /** Category path */
    +  path: string
    +
    +  /**
    +   * Only available when current route matches an item path
    +   */
    +  currentItems?: Article<T>[]
    +
    +  /** Category map */
    +  map: {
    +    /** Unique key under current category */
    +    [key: string]: {
    +      /** Category path of the key */
    +      path: string
    +      /** Category items of the key */
    +      items: Article<T>[]
    +    }
    +  }
    +}
    +
    +interface BlogTypeData<
    +  T extends Record<string, unknown> = Record<string, unknown>,
    +> {
    +  /** Type path */
    +  path: string
    +
    +  /** Items under current type */
    +  items: Article<T>[]
    +}
    +
    `,15);function ns(ls,es){const l=t("RouteLink");return i(),c("div",null,[d,s("ul",null,[u,y,s("li",null,[D,s("ul",null,[s("li",null,[e(l,{to:"/plugins/blog/guide.html#gathering-info"},{default:a(()=>[n("Guide → Gathering Info")]),_:1})])])]),g]),v,s("ul",null,[m,h,s("li",null,[C,s("ul",null,[s("li",null,[e(l,{to:"/plugins/blog/guide.html#collecting-articles"},{default:a(()=>[n("Guide → Collecting Articles")]),_:1})])])]),b]),f,s("ul",null,[E,B,s("li",null,[n("Reference: "),s("ul",null,[s("li",null,[e(l,{to:"/plugins/blog/guide.html#customizing-categories-and-types"},{default:a(()=>[n("Guide → Customizing Categories and Types")]),_:1})])])]),A]),_,s("ul",null,[x,k,s("li",null,[n("Reference: "),s("ul",null,[s("li",null,[e(l,{to:"/plugins/blog/guide.html#customizing-categories-and-types"},{default:a(()=>[n("Guide → Customizing Categories and Types")]),_:1})])])]),F]),w,T,R,s("ul",null,[P,I,s("li",null,[n("Reference: "),s("ul",null,[s("li",null,[e(l,{to:"/plugins/blog/guide.html#generating-excerpt"},{default:a(()=>[n("Guide → Generating Excerpt")]),_:1})])])]),q]),G,s("ul",null,[L,S,s("li",null,[n("Reference: "),s("ul",null,[s("li",null,[e(l,{to:"/plugins/blog/guide.html#generating-excerpt"},{default:a(()=>[n("Guide → Generating Excerpt")]),_:1})])])]),N]),O,s("ul",null,[z,j,s("li",null,[U,s("ul",null,[s("li",null,[e(l,{to:"/plugins/blog/guide.html#generating-excerpt"},{default:a(()=>[n("Guide → Generating Excerpt")]),_:1})])])]),V]),W,s("ul",null,[M,Y,s("li",null,[H,s("ul",null,[s("li",null,[e(l,{to:"/plugins/blog/guide.html#generating-excerpt"},{default:a(()=>[n("Guide → Generating Excerpt")]),_:1})])])]),J]),K,s("ul",null,[Q,X,s("li",null,[Z,s("ul",null,[s("li",null,[e(l,{to:"/plugins/blog/guide.html#generating-excerpt"},{default:a(()=>[n("Guide → Generating Excerpt")]),_:1})])])]),$]),ss])}const ps=o(r,[["render",ns],["__file","config.html.vue"]]),os=JSON.parse('{"path":"/plugins/blog/config.html","title":"Config","lang":"en-US","frontmatter":{},"headers":[{"level":2,"title":"Plugin Options","slug":"plugin-options","link":"#plugin-options","children":[{"level":3,"title":"getInfo","slug":"getinfo","link":"#getinfo","children":[]},{"level":3,"title":"filter","slug":"filter","link":"#filter","children":[]},{"level":3,"title":"category","slug":"category","link":"#category","children":[]},{"level":3,"title":"type","slug":"type","link":"#type","children":[]},{"level":3,"title":"slugify","slug":"slugify","link":"#slugify","children":[]},{"level":3,"title":"excerpt","slug":"excerpt","link":"#excerpt","children":[]},{"level":3,"title":"excerptSeparator","slug":"excerptseparator","link":"#excerptseparator","children":[]},{"level":3,"title":"excerptLength","slug":"excerptlength","link":"#excerptlength","children":[]},{"level":3,"title":"excerptFilter","slug":"excerptfilter","link":"#excerptfilter","children":[]},{"level":3,"title":"isCustomElement","slug":"iscustomelement","link":"#iscustomelement","children":[]},{"level":3,"title":"metaScope","slug":"metascope","link":"#metascope","children":[]},{"level":3,"title":"hotReload","slug":"hotreload","link":"#hotreload","children":[]}]},{"level":2,"title":"Blog Category Config","slug":"blog-category-config","link":"#blog-category-config","children":[]},{"level":2,"title":"Blog Type Config","slug":"blog-type-config","link":"#blog-type-config","children":[]},{"level":2,"title":"Composition API","slug":"composition-api","link":"#composition-api","children":[]}],"git":{"updatedTime":1707125652000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":1}]},"filePathRelative":"plugins/blog/config.md"}');export{ps as comp,os as data}; diff --git a/assets/config.html-ixjaZyxV.js b/assets/config.html-ixjaZyxV.js new file mode 100644 index 0000000000..f48da3d544 --- /dev/null +++ b/assets/config.html-ixjaZyxV.js @@ -0,0 +1,141 @@ +import{_ as c,r as i,o as t,c as r,b as s,d as l,a as n,w as o,e}from"./app-DI49vYHA.js";const d={},u=e('

    配置

    基础配置

    hostname

    • 类型: string

    • 详情:

      部署的域名,例如 https://example.com

    locales

    ',5),D=s("li",null,[s("p",null,[l("类型: "),s("code",null,"{ [path: string]: Partial }")])],-1),v=s("li",null,[s("p",null,[l("默认值: "),s("code",null,"{}")])],-1),h=s("p",null,"详情:",-1),m=s("p",null,"多语言支持的各个语言 locales 。",-1),b=s("p",null,[l("所有在 "),s("a",{href:"#locale-%E9%85%8D%E7%BD%AE"},"Locale 配置"),l(" 章节内的配置项都可以在 locales 中使用。")],-1),y={href:"https://v2.vuepress.vuejs.org/zh/reference/config.html#locales",target:"_blank",rel:"noopener noreferrer"},C=s("code",null,"locales",-1),g=s("p",null,"参考:",-1),_={href:"https://v2.vuepress.vuejs.org/zh/guide/i18n.html",target:"_blank",rel:"noopener noreferrer"},E=e('

    Locale 配置

    该章节内的配置项可以作为一般配置使用,也可以使用在 locales 内。

    colorMode

    ',3),f=s("li",null,[s("p",null,[l("类型: "),s("code",null,"'auto' | 'light' | 'dark'")])],-1),k=s("li",null,[s("p",null,[l("默认值: "),s("code",null,"'auto'")])],-1),x=s("p",null,"详情:",-1),F=s("p",null,"默认颜色模式。",-1),A=s("code",null,"'auto'",-1),L={href:"https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-color-scheme",target:"_blank",rel:"noopener noreferrer"},R=s("li",null,[s("p",null,"参考:"),s("ul",null,[s("li",null,[s("a",{href:"#colormodeswitch"},"默认主题 > 配置 > colorModeSwitch")])])],-1),w=s("h3",{id:"colormodeswitch",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#colormodeswitch"},[s("span",null,"colorModeSwitch")])],-1),S=e("
  • 类型: boolean

  • 默认值: true

  • 详情:

    是否启用切换颜色模式的功能。

    如果设置为 true ,将会在导航栏展示一个切换颜色模式的按钮。

  • ",3),z=s("p",null,"参考:",-1),N=s("li",null,[s("a",{href:"#colormode"},"默认主题 > 配置 > colorMode")],-1),G=e(`

    home

    • 类型: string

    • 默认值: /

    • 详情:

      首页的路径。

      它将被用于:

      • 导航栏中 Logo 的链接
      • 404 页面的 返回首页 链接
    • 类型: false | (NavbarItem | NavbarGroup | string)[]

    • 默认值: []

    • 详情:

      导航栏配置。

      设置为 false 可以禁用导航栏。

      为了配置导航栏元素,你可以将其设置为 导航栏数组 ,其中的每个元素是 NavbarItem 对象、 NavbarGroup 对象、或者字符串:

      • NavbarItem 对象应该有一个 text 字段和一个 link 字段,还有一个可选的 activeMatch 字段。
      • NavbarGroup 对象应该有一个 text 字段和一个 children 字段。 children 字段同样是一个 导航栏数组
      • 字符串应为目标页面文件的路径。它将会被转换为 NavbarItem 对象,将页面标题作为 text ,将页面路由路径作为 link
    • 示例 1:

    export default {
    +  theme: defaultTheme({
    +    navbar: [
    +      // NavbarItem
    +      {
    +        text: 'Foo',
    +        link: '/foo/',
    +      },
    +      // NavbarGroup
    +      {
    +        text: 'Group',
    +        children: ['/group/foo.md', '/group/bar.md'],
    +      },
    +      // 字符串 - 页面文件路径
    +      '/bar/README.md',
    +    ],
    +  }),
    +}
    +
    • 示例 2:
    export default {
    +  theme: defaultTheme({
    +    navbar: [
    +      // 嵌套 Group - 最大深度为 2
    +      {
    +        text: 'Group',
    +        children: [
    +          {
    +            text: 'SubGroup',
    +            children: ['/group/sub/foo.md', '/group/sub/bar.md'],
    +          },
    +        ],
    +      },
    +      // 控制元素何时被激活
    +      {
    +        text: 'Group 2',
    +        children: [
    +          {
    +            text: 'Always active',
    +            link: '/',
    +            // 该元素将一直处于激活状态
    +            activeMatch: '/',
    +          },
    +          {
    +            text: 'Active on /foo/',
    +            link: '/not-foo/',
    +            // 该元素在当前路由路径是 /foo/ 开头时激活
    +            // 支持正则表达式
    +            activeMatch: '^/foo/',
    +          },
    +        ],
    +      },
    +    ],
    +  }),
    +}
    +
    • 类型: null | string

    • 详情:

      Logo 图片的 URL。

      Logo 图片将会显示在导航栏的左端。

      设置为 null 可以禁用 Logo 。

    • 示例:

    export default {
    +  theme: defaultTheme({
    +    // Public 文件路径
    +    logo: '/images/hero.png',
    +    // URL
    +    logo: 'https://vuejs.org/images/logo.png',
    +  }),
    +}
    +
    `,10),M={href:"https://v2.vuepress.vuejs.org/zh/guide/assets.html#public-%E6%96%87%E4%BB%B6",target:"_blank",rel:"noopener noreferrer"},B=e(`

    logoDark

    • 类型: null | string

    • 详情:

      在夜间模式中使用的 Logo 图片的 URL。

      如果你想在夜间模式中使用不同的 Logo 图片,就可以使用该配置项。

      设置为 null 可以在夜间模式下禁用 Logo 。忽略该配置项将会在夜间模式中使用 logo 配置。

    • 参考:

    logoAlt

    • 类型:null | string

    • 详情:

      指定 Logo 图片的替代文字。

      当未指定时,将默认与站点标题相同。

    repo

    • 类型: string

    • 详情:

      项目仓库的 URL。

      它将被用作 仓库链接 的链接。仓库链接 将会显示为导航栏的最后一个元素。

    export default {
    +  theme: defaultTheme({
    +    // 如果你按照 \`organization/repository\` 的格式设置它
    +    // 我们会将它作为一个 GitHub 仓库
    +    repo: 'vuejs/vuepress',
    +    // 你也可以直接将它设置为一个 URL
    +    repo: 'https://gitlab.com/foo/bar',
    +  }),
    +}
    +
    `,8),I=s("li",null,[s("p",null,[l("类型: "),s("code",null,"false | 'auto' | SidebarConfigArray | SidebarConfigObject")])],-1),P=s("li",null,[s("p",null,[l("默认值: "),s("code",null,"'auto'")])],-1),T=s("p",null,"详情:",-1),U=s("p",null,"侧边栏配置。",-1),j=e("

    设置为 false 可以禁用侧边栏。

    如果你设置为 'auto',侧边栏会根据页面标题自动生成。

    为了手动配置侧边栏元素,你可以将其设置为 侧边栏数组 ,其中的每个元素是一个 SidebarItem 对象或者一个字符串:

    • SidebarItem 对象应该有一个 text 字段,有一个可选的 link 字段、一个可选的 children 字段和一个可选的 collapsible 字段。 children 字段同样是一个 侧边栏数组collapsible 字段来控制它是否可折叠。
    • 字符串应为目标页面文件的路径。它将会被转换为 SidebarItem 对象,将页面标题作为 text ,将页面路由路径作为 link ,并根据页面小标题自动生成 children

    如果你想在不同子路径中使用不同的侧边栏,你可以将该配置项设置为 侧边栏对象

    • Key 为路径前缀。
    • Value 为 侧边栏数组"heading" 以自动为相应路径生成基于标题的侧边栏。
    ",6),V=s("li",null,[s("p",null,"示例 1:")],-1),H=e(`
    export default {
    +  theme: defaultTheme({
    +    // 侧边栏数组
    +    // 所有页面会使用相同的侧边栏
    +    sidebar: [
    +      // SidebarItem
    +      {
    +        text: 'Foo',
    +        link: '/foo/',
    +        children: [
    +          // SidebarItem
    +          {
    +            text: 'github',
    +            link: 'https://github.com',
    +            children: [],
    +          },
    +          // 字符串 - 页面文件路径
    +          '/foo/bar.md',
    +        ],
    +      },
    +      // 字符串 - 页面文件路径
    +      '/bar/README.md',
    +    ],
    +  }),
    +}
    +
    • 示例 2:
    export default {
    +  theme: defaultTheme({
    +    // 侧边栏对象
    +    // 不同子路径下的页面会使用不同的侧边栏
    +    sidebar: {
    +      '/guide/': [
    +        {
    +          text: 'Guide',
    +          children: ['/guide/introduction.md', '/guide/getting-started.md'],
    +        },
    +      ],
    +      '/reference/': 'heading',
    +    },
    +  }),
    +}
    +
    • 示例 3:
    export default {
    +  theme: defaultTheme({
    +    // 可折叠的侧边栏
    +    sidebar: {
    +      '/reference/': [
    +        {
    +          text: 'VuePress Reference',
    +          collapsible: true,
    +          children: ['/reference/cli.md', '/reference/config.md'],
    +        },
    +        {
    +          text: 'Bundlers Reference',
    +          collapsible: true,
    +          children: [
    +            '/reference/bundler/vite.md',
    +            '/reference/bundler/webpack.md',
    +          ],
    +        },
    +      ],
    +    },
    +  }),
    +}
    +

    sidebarDepth

    `,6),q=s("li",null,[s("p",null,[l("类型: "),s("code",null,"number")])],-1),O=s("li",null,[s("p",null,[l("默认值: "),s("code",null,"2")])],-1),J=e("

    详情:

    设置根据页面标题自动生成的侧边栏的最大深度。

    • 设为 0 来禁用所有级别的页面标题。
    • 设为 1 来包含 <h2> 标题。
    • 设为 2 来包含 <h2><h3> 标题。
    • ...
    ",3),K={href:"https://v2.vuepress.vuejs.org/zh/config.html#markdown-headers",target:"_blank",rel:"noopener noreferrer"},W=s("p",null,[l("由于 "),s("code",null,"markdown.headers.level"),l(" 的默认值是 "),s("code",null,"[2, 3]"),l(" ,因此 "),s("code",null,"sidebarDepth"),l(" 的默认最大值是 "),s("code",null,"2"),l(" 。")],-1),Q=s("h3",{id:"editlink",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#editlink"},[s("span",null,"editLink")])],-1),X=s("li",null,[s("p",null,[l("类型: "),s("code",null,"boolean")])],-1),Y=s("li",null,[s("p",null,[l("默认值: "),s("code",null,"true")])],-1),Z=s("p",null,"详情:",-1),$=s("p",null,[l("是否启用 "),s("em",null,"编辑此页"),l(" 链接。")],-1),ss=e(`

    editLinkPattern

    • 类型: string

    • 详情:

      编辑此页 链接的 Pattern 。

      它将会用于生成 编辑此页 的链接。

      如果你不设置该选项,则会根据 docsRepo 配置项来推断 Pattern 。但是如果你的文档仓库没有托管在常用的平台上,比如 GitHub 、 GitLab 、 Bitbucket 、 Gitee 等,那么你必须设置该选项才能使 编辑此页 链接正常工作。

    • 用法:

      Pattern描述
      :repo文档仓库 URL ,即 docsRepo
      :branch文档仓库分支 ,即 docsBranch
      :path页面源文件的路径,即 docsDir 拼接上页面文件的相对路径
    • 示例:

    export default {
    +  theme: defaultTheme({
    +    docsRepo: 'https://gitlab.com/owner/name',
    +    docsBranch: 'master',
    +    docsDir: 'docs',
    +    editLinkPattern: ':repo/-/edit/:branch/:path',
    +  }),
    +}
    +

    则会生成类似于 'https://gitlab.com/owner/name/-/edit/master/docs/path/to/file.md' 的链接。

    docsRepo

    • 类型: string

    • 详情:

      文档源文件的仓库 URL 。

      它将会用于生成 编辑此页 的链接。

      如果你不设置该选项,则默认会使用 repo 配置项。但是如果你的文档源文件是在一个不同的仓库内,你就需要设置该配置项了。

    docsBranch

    • 类型: string

    • 默认值: 'main'

    • 详情:

      文档源文件的仓库分支。

      它将会用于生成 编辑此页 的链接。

    docsDir

    • 类型: string

    • 默认值: ''

    • 详情:

      文档源文件存放在仓库中的目录名。

      它将会用于生成 编辑此页 的链接。

    lastUpdated

    `,11),ls=s("li",null,[s("p",null,[l("类型: "),s("code",null,"boolean")])],-1),ns=s("li",null,[s("p",null,[l("默认值: "),s("code",null,"true")])],-1),es=s("p",null,"详情:",-1),as=s("p",null,[l("是否启用 "),s("em",null,"最近更新时间戳"),l(" 。")],-1),os=s("code",null,"false",-1),ps=s("h3",{id:"contributors",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#contributors"},[s("span",null,"contributors")])],-1),is=s("li",null,[s("p",null,[l("类型: "),s("code",null,"boolean")])],-1),cs=s("li",null,[s("p",null,[l("默认值: "),s("code",null,"true")])],-1),ts=s("p",null,"详情:",-1),rs=s("p",null,[l("是否启用 "),s("em",null,"贡献者列表"),l(" 。")],-1),ds=s("code",null,"false",-1);function us(Ds,vs){const p=i("ExternalLinkIcon"),a=i("RouteLink");return t(),r("div",null,[u,s("ul",null,[D,v,s("li",null,[h,m,b,s("p",null,[l("该配置项仅能在默认主题内生效,注意不要和 "),s("a",y,[l("站点配置"),n(p)]),l(" 中的 "),C,l(" 混淆。")])]),s("li",null,[g,s("ul",null,[s("li",null,[s("a",_,[l("指南 > 多语言支持"),n(p)])])])])]),E,s("ul",null,[f,k,s("li",null,[x,F,s("p",null,[l("如果设置为 "),A,l(" ,则会根据 "),s("a",L,[l("prefers-color-scheme"),n(p)]),l(" 自动设置初始颜色模式。")])]),R]),w,s("ul",null,[S,s("li",null,[z,s("ul",null,[N,s("li",null,[n(a,{to:"/zh/themes/default/locale.html#togglecolormode"},{default:o(()=>[l("默认主题 > 语言配置 > toggleColorMode")]),_:1})])])])]),G,s("ul",null,[s("li",null,[l("参考: "),s("ul",null,[s("li",null,[s("a",M,[l("指南 > 静态资源 > Public 文件"),n(p)])])])])]),B,s("ul",null,[I,P,s("li",null,[T,U,s("p",null,[l("你可以通过页面的 "),n(a,{to:"/zh/themes/default/frontmatter.html#sidebar"},{default:o(()=>[l("sidebar")]),_:1}),l(" frontmatter 来覆盖这个全局配置。")]),j]),V]),H,s("ul",null,[q,O,s("li",null,[J,s("p",null,[l("最大值取决于你通过 "),s("a",K,[l("markdown.headers.level"),n(p)]),l(" 提取了哪些级别的标题。")]),W,s("p",null,[l("你可以通过页面的 "),n(a,{to:"/zh/themes/default/frontmatter.html#sidebardepth"},{default:o(()=>[l("sidebarDepth")]),_:1}),l(" frontmatter 来覆盖这个全局配置。")])])]),Q,s("ul",null,[X,Y,s("li",null,[Z,$,s("p",null,[l("你可以通过页面的 "),n(a,{to:"/zh/themes/default/frontmatter.html#editlink"},{default:o(()=>[l("editLink")]),_:1}),l(" frontmatter 来覆盖这个全局配置。")])])]),ss,s("ul",null,[ls,ns,s("li",null,[es,as,s("p",null,[l("你可以通过页面的 "),n(a,{to:"/zh/themes/default/frontmatter.html#lastupdated"},{default:o(()=>[l("lastUpdated")]),_:1}),l(" frontmatter 来覆盖这个全局配置。要注意的是,如果你已经将该选项设为了 "),os,l(" ,那么这个功能会被完全禁用,并且无法在 locales 或页面 frontmatter 中启用。")])])]),ps,s("ul",null,[is,cs,s("li",null,[ts,rs,s("p",null,[l("你可以通过页面的 "),n(a,{to:"/zh/themes/default/frontmatter.html#contributors"},{default:o(()=>[l("contributors")]),_:1}),l(" frontmatter 来覆盖这个全局配置。要注意的是,如果你已经将该选项设为了 "),ds,l(" ,那么这个功能会被完全禁用,并且无法在 locales 或页面 frontmatter 中启用。")])])])])}const ms=c(d,[["render",us],["__file","config.html.vue"]]),bs=JSON.parse('{"path":"/zh/themes/default/config.html","title":"配置","lang":"zh-CN","frontmatter":{},"headers":[{"level":2,"title":"基础配置","slug":"基础配置","link":"#基础配置","children":[{"level":3,"title":"hostname","slug":"hostname","link":"#hostname","children":[]},{"level":3,"title":"locales","slug":"locales","link":"#locales","children":[]}]},{"level":2,"title":"Locale 配置","slug":"locale-配置","link":"#locale-配置","children":[{"level":3,"title":"colorMode","slug":"colormode","link":"#colormode","children":[]},{"level":3,"title":"colorModeSwitch","slug":"colormodeswitch","link":"#colormodeswitch","children":[]},{"level":3,"title":"home","slug":"home","link":"#home","children":[]},{"level":3,"title":"navbar","slug":"navbar","link":"#navbar","children":[]},{"level":3,"title":"logo","slug":"logo","link":"#logo","children":[]},{"level":3,"title":"logoDark","slug":"logodark","link":"#logodark","children":[]},{"level":3,"title":"logoAlt","slug":"logoalt","link":"#logoalt","children":[]},{"level":3,"title":"repo","slug":"repo","link":"#repo","children":[]},{"level":3,"title":"sidebar","slug":"sidebar","link":"#sidebar","children":[]},{"level":3,"title":"sidebarDepth","slug":"sidebardepth","link":"#sidebardepth","children":[]},{"level":3,"title":"editLink","slug":"editlink","link":"#editlink","children":[]},{"level":3,"title":"editLinkPattern","slug":"editlinkpattern","link":"#editlinkpattern","children":[]},{"level":3,"title":"docsRepo","slug":"docsrepo","link":"#docsrepo","children":[]},{"level":3,"title":"docsBranch","slug":"docsbranch","link":"#docsbranch","children":[]},{"level":3,"title":"docsDir","slug":"docsdir","link":"#docsdir","children":[]},{"level":3,"title":"lastUpdated","slug":"lastupdated","link":"#lastupdated","children":[]},{"level":3,"title":"contributors","slug":"contributors","link":"#contributors","children":[]}]}],"git":{"updatedTime":1706734148000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":7}]},"filePathRelative":"zh/themes/default/config.md"}');export{ms as comp,bs as data}; diff --git a/assets/container.html-CcAV80DR.js b/assets/container.html-CcAV80DR.js new file mode 100644 index 0000000000..3dff28b4e1 --- /dev/null +++ b/assets/container.html-CcAV80DR.js @@ -0,0 +1,39 @@ +import{_ as r,r as e,o as c,c as d,a as l,b as s,d as n,w as p,e as o}from"./app-DI49vYHA.js";const D={},u=s("h1",{id:"container",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#container"},[s("span",null,"container")])],-1),h=s("p",null,"为你的 VuePress 站点注册自定义容器。",-1),y={href:"https://github.com/markdown-it/markdown-it-container",target:"_blank",rel:"noopener noreferrer"},v=o(`

    使用方法

    npm i -D @vuepress/plugin-container@next
    +
    import { containerPlugin } from '@vuepress/plugin-container'
    +
    +export default {
    +  plugins: [
    +    containerPlugin({
    +      // 配置项
    +    }),
    +  ],
    +}
    +

    容器语法

    ::: <type> [info]
    +[content]
    +:::
    +
    • type 是必需的,应通过 type 配置项来指定。
    • info 是可选的,其默认值可以通过 localesdefaultInfo 配置项来指定。
    • content 可是任何合法的 Markdown 内容。

    提示

    该插件可以被多次使用,以便支持不同类型的容器。

    配置项

    type

    `,9),m=s("li",null,[s("p",null,[n("类型: "),s("code",null,"string")])],-1),b=s("p",null,"详情:",-1),C=s("p",null,"容器的类型。",-1),E={href:"https://github.com/markdown-it/markdown-it-container#api",target:"_blank",rel:"noopener noreferrer"},f=s("code",null,"name",-1),g=o(`

    locales

    • 类型: Record<string, { defaultInfo: string }>

    • 详情:

      容器在不同 locales 下的默认 info

      如果没有指定该配置项,默认 info 会使用大写的 type

    • 示例:

    export default {
    +  plugins: [
    +    containerPlugin({
    +      type: 'tip',
    +      locales: {
    +        '/': {
    +          defaultInfo: 'TIP',
    +        },
    +        '/zh/': {
    +          defaultInfo: '提示',
    +        },
    +      },
    +    }),
    +  ],
    +}
    +
    `,3),_=o(`

    before

    • 类型: (info: string) => string

    • 默认值:

    (info: string): string =>
    +  \`<div class="custom-container \${type}">\${info ? \`<p class="custom-container-title">\${info}</p>\` : ''}\\n\`
    +
    • 详情:

      一个用于渲染容器起始标签的函数。

      第一个参数是 容器语法info 部分。

      如果你没有设置 after 配置项,则该配置项也不会生效。

    after

    • 类型: (info: string) => string

    • 默认值:

    (): string => '</div>\\n'
    +
    • 详情:

      一个用于渲染容器结束标签的函数。

      第一个参数是 容器语法info 部分。

      如果你没有设置 before 配置项,则该配置项也不会生效。

    render

    • 类型:
    type MarkdownItContainerRenderFunction = (
    +  tokens: Token[],
    +  index: number,
    +  options: any,
    +  env: MarkdownEnv,
    +  self: Renderer,
    +) => string
    +
    `,11),k=s("p",null,"详情:",-1),x={href:"https://github.com/markdown-it/markdown-it-container#api",target:"_blank",rel:"noopener noreferrer"},A=s("code",null,"render",-1),B=s("p",null,[n("该插件使用了一个默认的 "),s("code",null,"render"),n(" 函数。但如果你指定了该配置项,那么默认的 "),s("code",null,"render"),n(" 函数就会被替换掉,此时 "),s("a",{href:"#locales"},"locales"),n(" 、 "),s("a",{href:"#before"},"before"),n(" 和 "),s("a",{href:"#after"},"after"),n(" 配置项都会被忽略。")],-1),w=s("h3",{id:"validate",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#validate"},[s("span",null,"validate")])],-1),F=s("li",null,[s("p",null,[n("类型: "),s("code",null,"(params: string) => boolean")])],-1),I=s("p",null,"详情:",-1),N={href:"https://github.com/markdown-it/markdown-it-container#api",target:"_blank",rel:"noopener noreferrer"},P=s("code",null,"validate",-1),R=s("h3",{id:"marker",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#marker"},[s("span",null,"marker")])],-1),z=s("li",null,[s("p",null,[n("类型: "),s("code",null,"string")])],-1),V=s("p",null,"详情:",-1),q={href:"https://github.com/markdown-it/markdown-it-container#api",target:"_blank",rel:"noopener noreferrer"},L=s("code",null,"marker",-1);function M(T,$){const i=e("NpmBadge"),a=e("ExternalLinkIcon"),t=e("RouteLink");return c(),d("div",null,[u,l(i,{package:"@vuepress/plugin-container"}),h,s("p",null,[n("该插件简化了 "),s("a",y,[n("markdown-it-container"),l(a)]),n(" 的使用方法,但同时也保留了其原本的能力。")]),s("p",null,[n("默认主题的 "),l(t,{to:"/zh/default-theme/markdown.html#%E8%87%AA%E5%AE%9A%E4%B9%89%E5%AE%B9%E5%99%A8"},{default:p(()=>[n("自定义容器")]),_:1}),n(" 就是由该插件支持的。")]),v,s("ul",null,[m,s("li",null,[b,C,s("p",null,[n("它将会被用作 "),s("a",E,[n("markdown-it-container"),l(a)]),n(" 的 "),f,n(" 参数。")])])]),g,s("ul",null,[s("li",null,[n("参考: "),s("ul",null,[s("li",null,[l(t,{to:"/guide/i18n.html"},{default:p(()=>[n("指南 > 多语言支持")]),_:1})])])])]),_,s("ul",null,[s("li",null,[k,s("p",null,[s("a",x,[n("markdown-it-container"),l(a)]),n(" 的 "),A,n(" 配置项。")]),B])]),w,s("ul",null,[F,s("li",null,[I,s("p",null,[s("a",N,[n("markdown-it-container"),l(a)]),n(" 的 "),P,n(" 配置项。")])])]),R,s("ul",null,[z,s("li",null,[V,s("p",null,[s("a",q,[n("markdown-it-container"),l(a)]),n(" 的 "),L,n(" 配置项。")])])])])}const H=r(D,[["render",M],["__file","container.html.vue"]]),J=JSON.parse('{"path":"/zh/plugins/container.html","title":"container","lang":"zh-CN","frontmatter":{},"headers":[{"level":2,"title":"使用方法","slug":"使用方法","link":"#使用方法","children":[]},{"level":2,"title":"容器语法","slug":"容器语法","link":"#容器语法","children":[]},{"level":2,"title":"配置项","slug":"配置项","link":"#配置项","children":[{"level":3,"title":"type","slug":"type","link":"#type","children":[]},{"level":3,"title":"locales","slug":"locales","link":"#locales","children":[]},{"level":3,"title":"before","slug":"before","link":"#before","children":[]},{"level":3,"title":"after","slug":"after","link":"#after","children":[]},{"level":3,"title":"render","slug":"render","link":"#render","children":[]},{"level":3,"title":"validate","slug":"validate","link":"#validate","children":[]},{"level":3,"title":"marker","slug":"marker","link":"#marker","children":[]}]}],"git":{"updatedTime":1706605723000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":1}]},"filePathRelative":"zh/plugins/container.md"}');export{H as comp,J as data}; diff --git a/assets/container.html-Fg-TBurh.js b/assets/container.html-Fg-TBurh.js new file mode 100644 index 0000000000..ee0c243f00 --- /dev/null +++ b/assets/container.html-Fg-TBurh.js @@ -0,0 +1,39 @@ +import{_ as r,r as l,o as c,c as d,a as e,b as n,d as s,w as i,e as o}from"./app-DI49vYHA.js";const u={},D=n("h1",{id:"container",tabindex:"-1"},[n("a",{class:"header-anchor",href:"#container"},[n("span",null,"container")])],-1),h=n("p",null,"Register markdown custom containers in your VuePress site.",-1),y={href:"https://github.com/markdown-it/markdown-it-container",target:"_blank",rel:"noopener noreferrer"},m=o(`

    Usage

    npm i -D @vuepress/plugin-container@next
    +
    import { containerPlugin } from '@vuepress/plugin-container'
    +
    +export default {
    +  plugins: [
    +    containerPlugin({
    +      // options
    +    }),
    +  ],
    +}
    +

    Container Syntax

    ::: <type> [info]
    +[content]
    +:::
    +
    • The type is required and should be specified via type option.
    • The info is optional, and the default value can be specified via defaultInfo in locales option.
    • The content can be any valid markdown content.

    TIP

    This plugin can be used multiple times to support different types of containers.

    Options

    type

    `,9),v=n("li",null,[n("p",null,[s("Type: "),n("code",null,"string")])],-1),f=n("p",null,"Details:",-1),b=n("p",null,"The type of the container.",-1),C=n("code",null,"name",-1),g={href:"https://github.com/markdown-it/markdown-it-container#api",target:"_blank",rel:"noopener noreferrer"},k=o(`

    locales

    • Type: Record<string, { defaultInfo: string }>

    • Details:

      The default info of the container in different locales.

      If this option is not specified, the default info will fallback to the uppercase of the type option.

    • Example:

    export default {
    +  plugins: [
    +    containerPlugin({
    +      type: 'tip',
    +      locales: {
    +        '/': {
    +          defaultInfo: 'TIP',
    +        },
    +        '/zh/': {
    +          defaultInfo: '提示',
    +        },
    +      },
    +    }),
    +  ],
    +}
    +
    `,3),_=o(`

    before

    • Type: (info: string) => string

    • Default:

    (info: string): string =>
    +  \`<div class="custom-container \${type}">\${info ? \`<p class="custom-container-title">\${info}</p>\` : ''}\\n\`
    +
    • Details:

      A function to render the starting tag of the container.

      The first param is the info part of container syntax.

      This option will not take effect if you don't specify the after option.

    after

    • Type: (info: string) => string

    • Default:

    (): string => '</div>\\n'
    +
    • Details:

      A function to render the ending tag of the container.

      The first param is the info part of container syntax.

      This option will not take effect if you don't specify the before option.

    render

    • Type:
    type MarkdownItContainerRenderFunction = (
    +  tokens: Token[],
    +  index: number,
    +  options: any,
    +  env: MarkdownEnv,
    +  self: Renderer,
    +) => string
    +
    `,11),E=n("p",null,"Details:",-1),x=n("code",null,"render",-1),w={href:"https://github.com/markdown-it/markdown-it-container#api",target:"_blank",rel:"noopener noreferrer"},T=n("p",null,[s("This plugin uses a default "),n("code",null,"render"),s(" function. If you specify this option, the default "),n("code",null,"render"),s(" function will be replaced, and the "),n("a",{href:"#locales"},"locales"),s(", "),n("a",{href:"#before"},"before"),s(" and "),n("a",{href:"#after"},"after"),s(" options will be ignored.")],-1),F=n("h3",{id:"validate",tabindex:"-1"},[n("a",{class:"header-anchor",href:"#validate"},[n("span",null,"validate")])],-1),B=n("li",null,[n("p",null,[s("Type: "),n("code",null,"(params: string) => boolean")])],-1),I=n("p",null,"Details:",-1),A=n("code",null,"validate",-1),N={href:"https://github.com/markdown-it/markdown-it-container#api",target:"_blank",rel:"noopener noreferrer"},P=n("h3",{id:"marker",tabindex:"-1"},[n("a",{class:"header-anchor",href:"#marker"},[n("span",null,"marker")])],-1),R=n("li",null,[n("p",null,[s("Type: "),n("code",null,"string")])],-1),q=n("p",null,"Details:",-1),S=n("code",null,"marker",-1),V={href:"https://github.com/markdown-it/markdown-it-container#api",target:"_blank",rel:"noopener noreferrer"};function L(M,O){const p=l("NpmBadge"),a=l("ExternalLinkIcon"),t=l("RouteLink");return c(),d("div",null,[D,e(p,{package:"@vuepress/plugin-container"}),h,n("p",null,[s("This plugin simplifies the use of "),n("a",y,[s("markdown-it-container"),e(a)]),s(", but also retains its original capabilities.")]),n("p",null,[s("The "),e(t,{to:"/default-theme/markdown.html#custom-containers"},{default:i(()=>[s("Custom Containers")]),_:1}),s(" of default theme is powered by this plugin.")]),m,n("ul",null,[v,n("li",null,[f,b,n("p",null,[s("It will be used as the "),C,s(" param of "),n("a",g,[s("markdown-it-container"),e(a)]),s(".")])])]),k,n("ul",null,[n("li",null,[s("Also see: "),n("ul",null,[n("li",null,[e(t,{to:"/guide/i18n.html"},{default:i(()=>[s("Guide > I18n")]),_:1})])])])]),_,n("ul",null,[n("li",null,[E,n("p",null,[s("The "),x,s(" option of "),n("a",w,[s("markdown-it-container"),e(a)]),s(".")]),T])]),F,n("ul",null,[B,n("li",null,[I,n("p",null,[s("The "),A,s(" option of "),n("a",N,[s("markdown-it-container"),e(a)]),s(".")])])]),P,n("ul",null,[R,n("li",null,[q,n("p",null,[s("The "),S,s(" option of "),n("a",V,[s("markdown-it-container"),e(a)]),s(".")])])])])}const $=r(u,[["render",L],["__file","container.html.vue"]]),z=JSON.parse('{"path":"/plugins/container.html","title":"container","lang":"en-US","frontmatter":{},"headers":[{"level":2,"title":"Usage","slug":"usage","link":"#usage","children":[]},{"level":2,"title":"Container Syntax","slug":"container-syntax","link":"#container-syntax","children":[]},{"level":2,"title":"Options","slug":"options","link":"#options","children":[{"level":3,"title":"type","slug":"type","link":"#type","children":[]},{"level":3,"title":"locales","slug":"locales","link":"#locales","children":[]},{"level":3,"title":"before","slug":"before","link":"#before","children":[]},{"level":3,"title":"after","slug":"after","link":"#after","children":[]},{"level":3,"title":"render","slug":"render","link":"#render","children":[]},{"level":3,"title":"validate","slug":"validate","link":"#validate","children":[]},{"level":3,"title":"marker","slug":"marker","link":"#marker","children":[]}]}],"git":{"updatedTime":1706605723000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":1}]},"filePathRelative":"plugins/container.md"}');export{$ as comp,z as data}; diff --git a/assets/copy-code.html-DVC_b5GJ.js b/assets/copy-code.html-DVC_b5GJ.js new file mode 100644 index 0000000000..1cf2515341 --- /dev/null +++ b/assets/copy-code.html-DVC_b5GJ.js @@ -0,0 +1,61 @@ +import{_ as l,r as a,o as e,c as o,a as i,b as s,e as p}from"./app-DI49vYHA.js";const c={},t=s("h1",{id:"copy-code",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#copy-code"},[s("span",null,"copy-code")])],-1),r=p(`

    This plugin will automatically add a copy button to the top right corner of each code block on PC devices.

    The default selector matches @vuepress/theme-default, so you might need to change it when integrating your own theme.

    Usage

    npm i -D @vuepress/plugin-copy-code@next
    +
    import { copyCodePlugin } from '@vuepress/plugin-copy-code'
    +
    +export default {
    +  plugins: [
    +    copyCodePlugin({
    +      // options
    +    }),
    +  ],
    +}
    +

    Options

    selector

    • Type: string | string[]

    • Default: '.theme-default-content div[class*="language-"] pre'

    • Details:

      Code block selector

    showInMobile

    • Type: boolean

    • Default: false

    • Details:

      Whether to display copy button on the mobile device

    duration

    • Type: number

    • Default: 2000

    • Details:

      Hint display time, setting it to 0 will disable the hint.

    delay

    • Type: number

    • Default: 800

    • Details:

      The delay of registering copy code buttons, in ms.

      If the theme you are using has a switching animation, it is recommended to configure this option to Switch animation duration + 200.

    locales

    • Type: CopyCodePluginLocaleConfig

      interface CopyCodePluginLocaleData {
      +  /**
      +   * Copy text
      +   */
      +  copy: string
      +
      +  /**
      +   * Copied text
      +   */
      +  copied: string
      +}
      +
      +interface CopyCodePluginLocaleConfig {
      +  [localePath: string]: CopyCodePluginLocaleData
      +}
      +
    • Required: No

    • Details:

      Locales config for copy code plugin.

    • Example:

      import { copyCodePlugin } from '@vuepress/plugin-copy-code'
      +
      +export default {
      +  locales: {
      +    '/': {
      +      // this is a supported language
      +      lang: 'en-US',
      +    },
      +    '/xx/': {
      +      // the plugin does not support this language
      +      lang: 'mm-NN',
      +    },
      +  },
      +
      +  plugins: [
      +    copyCodePlugin({
      +      locales: {
      +        '/': {
      +          // Override copy button label text
      +          copy: 'Copy Codes from code block',
      +        },
      +
      +        '/xx/': {
      +          // Complete locale config for \`mm-NN\` language here
      +        },
      +      },
      +    }),
      +  ],
      +}
      +
    Built-in Supported Languages
    • Simplified Chinese (zh-CN)
    • Traditional Chinese (zh-TW)
    • English (United States) (en-US)
    • German (de-DE)
    • German (Australia) (de-AT)
    • Russian (ru-RU)
    • Ukrainian (uk-UA)
    • Vietnamese (vi-VN)
    • Portuguese (Brazil) (pt-BR)
    • Polish (pl-PL)
    • French (fr-FR)
    • Spanish (es-ES)
    • Slovak (sk-SK)
    • Japanese (ja-JP)
    • Turkish (tr-TR)
    • Korean (ko-KR)
    • Finnish (fi-FI)
    • Indonesian (id-ID)
    • Dutch (nl-NL)

    Styles

    You can customize the icon of the copy button via CSS variables:

    :root {
    +  --code-copy-icon: url("copy-button.svg");
    +  --code-copied-icon: url("copied-button.svg");
    +  --copy-code-color: #9e9e9e;
    +  --copy-code-hover: rgb(0 0 0 / 50%);
    +}
    +
    `,20);function d(D,u){const n=a("NpmBadge");return e(),o("div",null,[t,i(n,{package:"@vuepress/plugin-copy-code"}),r])}const v=l(c,[["render",d],["__file","copy-code.html.vue"]]),m=JSON.parse('{"path":"/plugins/copy-code.html","title":"copy-code","lang":"en-US","frontmatter":{},"headers":[{"level":2,"title":"Usage","slug":"usage","link":"#usage","children":[]},{"level":2,"title":"Options","slug":"options","link":"#options","children":[{"level":3,"title":"selector","slug":"selector","link":"#selector","children":[]},{"level":3,"title":"showInMobile","slug":"showinmobile","link":"#showinmobile","children":[]},{"level":3,"title":"duration","slug":"duration","link":"#duration","children":[]},{"level":3,"title":"delay","slug":"delay","link":"#delay","children":[]},{"level":3,"title":"locales","slug":"locales","link":"#locales","children":[]}]},{"level":2,"title":"Styles","slug":"styles","link":"#styles","children":[]}],"git":{"updatedTime":1708274662000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":4}]},"filePathRelative":"plugins/copy-code.md"}');export{v as comp,m as data}; diff --git a/assets/copy-code.html-Dl2NXwK3.js b/assets/copy-code.html-Dl2NXwK3.js new file mode 100644 index 0000000000..8cbf4366da --- /dev/null +++ b/assets/copy-code.html-Dl2NXwK3.js @@ -0,0 +1,61 @@ +import{_ as l,r as a,o as e,c as p,a as o,b as s,e as i}from"./app-DI49vYHA.js";const c={},r=s("h1",{id:"copy-code",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#copy-code"},[s("span",null,"copy-code")])],-1),t=i(`

    此插件会自动在 PC 设备上为每个代码块右上角添加复制按钮。

    默认选择器匹配 @vuepress/theme-default,所以在你自己的主题中集成时可能需要调整它。

    使用

    npm i -D @vuepress/plugin-copy-code@next
    +
    import { copyCodePlugin } from '@vuepress/plugin-copy-code'
    +
    +export default {
    +  plugins: [
    +    copyCodePlugin({
    +      // options
    +    }),
    +  ],
    +}
    +

    选项

    selector

    • 类型:string | string[]

    • 默认值:'.theme-default-content div[class*="language-"] pre'

    • 详情:

      代码块选择器

    showInMobile

    • 类型:boolean

    • 默认值:false

    • 详情:

      是否展示在移动端

    duration

    • 类型:number

    • 默认值:2000

    • 详情:

      提示消息显示时间,设置为 0 会禁用提示。

    delay

    • 类型:number

    • 默认值:800

    • 详情:

      注册复制按钮的延时,单位 ms。

      如果你使用的主题有切换动画,建议配置此选项为 切换动画时长 + 200

    locales

    • 类型:CopyCodePluginLocaleConfig

      interface CopyCodePluginLocaleData {
      +  /**
      +   * 复制文字
      +   */
      +  copy: string
      +
      +  /**
      +   * 已复制文字
      +   */
      +  copied: string
      +}
      +
      +interface CopyCodePluginLocaleConfig {
      +  [localePath: string]: CopyCodePluginLocaleData
      +}
      +
    • 必填:否

    • 详情:

      复制按钮插件的国际化配置。

    • 示例:

      import { copyCodePlugin } from '@vuepress/plugin-copy-code'
      +
      +export default {
      +  locales: {
      +    '/': {
      +      // 这是一个支持的语言
      +      lang: 'zh-CN',
      +    },
      +    '/xx/': {
      +      // 这是一个没有收到插件支持的语言
      +      lang: 'mm-NN',
      +    },
      +  },
      +
      +  plugins: [
      +    copyCodePlugin({
      +      locales: {
      +        '/': {
      +          // 覆盖复制按钮标签文字
      +          copy: '复制此段代码',
      +        },
      +
      +        '/xx/': {
      +          // 在这里完整设置 \`mm-NN\` 的多语言配置
      +        },
      +      },
      +    }),
      +  ],
      +}
      +
    内置支持语言
    • 简体中文 (zh-CN)
    • 繁体中文 (zh-TW)
    • 英文(美国) (en-US)
    • 德语 (de-DE)
    • 德语(澳大利亚) (de-AT)
    • 俄语 (ru-RU)
    • 乌克兰语 (uk-UA)
    • 越南语 (vi-VN)
    • 葡萄牙语(巴西) (pt-BR)
    • 波兰语 (pl-PL)
    • 法语 (fr-FR)
    • 西班牙语 (es-ES)
    • 斯洛伐克 (sk-SK)
    • 日语 (ja-JP)
    • 土耳其语 (tr-TR)
    • 韩语 (ko-KR)
    • 芬兰语 (fi-FI)
    • 印尼语 (id-ID)
    • 荷兰语 (nl-NL)

    样式

    你可以通过 CSS 变量来自定义复制按钮的样式:

    :root {
    +  --code-copy-icon: url("copy-button.svg");
    +  --code-copied-icon: url("copied-button.svg");
    +  --copy-code-color: #9e9e9e;
    +  --copy-code-hover: rgb(0 0 0 / 50%);
    +}
    +
    `,20);function d(D,u){const n=a("NpmBadge");return e(),p("div",null,[r,o(n,{package:"@vuepress/plugin-copy-code"}),t])}const v=l(c,[["render",d],["__file","copy-code.html.vue"]]),m=JSON.parse('{"path":"/zh/plugins/copy-code.html","title":"copy-code","lang":"zh-CN","frontmatter":{},"headers":[{"level":2,"title":"使用","slug":"使用","link":"#使用","children":[]},{"level":2,"title":"选项","slug":"选项","link":"#选项","children":[{"level":3,"title":"selector","slug":"selector","link":"#selector","children":[]},{"level":3,"title":"showInMobile","slug":"showinmobile","link":"#showinmobile","children":[]},{"level":3,"title":"duration","slug":"duration","link":"#duration","children":[]},{"level":3,"title":"delay","slug":"delay","link":"#delay","children":[]},{"level":3,"title":"locales","slug":"locales","link":"#locales","children":[]}]},{"level":2,"title":"样式","slug":"样式","link":"#样式","children":[]}],"git":{"updatedTime":1708274662000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":6}]},"filePathRelative":"zh/plugins/copy-code.md"}');export{v as comp,m as data}; diff --git a/assets/copyright.html-Cjv27u5V.js b/assets/copyright.html-Cjv27u5V.js new file mode 100644 index 0000000000..60a83e2403 --- /dev/null +++ b/assets/copyright.html-Cjv27u5V.js @@ -0,0 +1,66 @@ +import{_ as n,r as e,o as a,c as i,a as o,b as s,e as c}from"./app-DI49vYHA.js";const p={},t=s("h1",{id:"copyright",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#copyright"},[s("span",null,"copyright")])],-1),r=c(`

    此插件可以在访问者从你的站点复制内容时,自动追加版权信息,也可以禁止站点的复制或者选择。

    使用

    npm i -D @vuepress/plugin-copyright@next
    +
    import { copyrightPlugin } from '@vuepress/plugin-copyright'
    +
    +export default {
    +  plugins: [
    +    copyrightPlugin({
    +      // options
    +    }),
    +  ],
    +}
    +

    启用版权信息

    此插件默认全局禁用。你可以:

    • 在特定页面的 frontmatter 中设置 copy: true 手动开启。
    • 在插件选项中设置 global: true 让其全局生效,并在页面的 frontmatter 中设置 copy: false 禁用它。

    处于不打扰用户的考虑,默认配置下仅当复制长度超过 100 时才会追加版权信息。如果你希望改变这个触发值,你可以插件选项中设置 triggerLength,或在页面 frontmatter 单独设置 copy.triggerLength

    你可以通过插件的 authorlicense 选项设置全局作者和协议信息。

    如果文档的不同部分拥有不同的作者和协议,你可以通过 authorGetterlicenseGetter 传入一个使用当前页面对象作为参数的函数 (page: Page) => string 并通过它返回相应信息。

    插件会默认通过模板从作者、协议和页面链接生成版权信息,并在复制时追加。如果你认为这不够灵活,你可以设置 copyrightGetter 返回一个完全由你自定义的版权信息,或返回 null 以使用默认模板。

    禁用复制和选择

    如果你希望禁止用户复制较长内容,你可以在插件选项中设置 maxLength 控制这个临界值,或在页面 frontmatter 单独设置 copy.maxLength

    • 如果你不希望用户复制你的整个站点或特定页面文字,你可以在插件选项中设置 disableCopy 或在页面 frontmatter 中设置 copy.disableCopy 来禁用复制,后者具有更高优先级。
    • 如果你不希望用户选择你的整个站点或特定页面文字,你可以在插件选项中设置 disableSelection 或在页面 frontmatter 中设置 copy.disableSelection 来禁用文字选择。此选项具有更高优先级

    选项

    author

    • 类型:string
    • 详情:默认作者信息

    license

    • 类型:string
    • 详情:默认协议信息

    authorGetter

    • 类型:(page: Page) => string | null
    • 详情:作者信息获取器

    licenseGetter

    • 类型:(page: Page) => string | null
    • 详情:协议信息获取器

    copyrightGetter

    • 类型:(page: Page) => string | null
    • 详情:协议信息获取器

    triggerLength

    • 类型:number
    • 默认值:100
    • 详情:触发附加版权的最小内容长度

    maxLength

    • 类型:number
    • 默认值:0
    • 详情:允许复制的最大内容长度,0 意味着无限制。

    global

    • 类型:boolean
    • 默认值:false
    • 详情:是否全局启用

    disableCopy

    • 类型:boolean
    • 默认值:false
    • 详情:禁用复制

    disableSelection

    • 类型:boolean
    • 默认值:false
    • 详情:禁用选择

    canonical

    • 类型:string

    • 详情:

      首选部署位置。

    例子

    如果你在 https://myblog.comhttps://blog.com/username/ 下部署相同的内容,你可能希望选择一个站点作为首选链接。

    • 如果你倾向于使用第一个,你应该将 canonical 设置为 https://myblog.com
    • 如果你倾向于使用第二个,你应该将 canonical 设置为 https://blog.com/username/

    这样,在另一个站点触发的版权信息也会指向你的首选站点。

    locales

    • 类型:CopyrightPluginLocaleConfig

      interface CopyrightPluginLocaleData {
      +  /**
      +   * 作者文字
      +   *
      +   * @description \`:author\` 将会被作者替换
      +   */
      +  author: string
      +
      +  /**
      +   * 协议文字
      +   *
      +   * @description \`:license\` 会被当前协议替换
      +   */
      +  license: string
      +
      +  /**
      +   * 链接文字
      +   *
      +   * @description \`:link\` 会替换为当前页面链接
      +   */
      +  link: string
      +}
      +
      +interface CopyrightPluginLocaleConfig {
      +  [localePath: string]: CopyrightPluginLocaleData
      +}
      +
    • 详情:版权插件的国际化配置。

    • 示例:

      import { copyrightPlugin } from '@vuepress/plugin-copyright'
      +
      +export default {
      +  locales: {
      +    '/': {
      +      // this is a supported language
      +      lang: 'en-US',
      +    },
      +    '/xx/': {
      +      // the plugin does not support this language
      +      lang: 'mm-NN',
      +    },
      +  },
      +
      +  plugins: [
      +    copyrightPlugin({
      +      locales: {
      +        '/': {
      +          // Override link text
      +          link: 'Original posted at :link',
      +        },
      +
      +        '/xx/': {
      +          // Complete locale config for \`mm-NN\` language here
      +        },
      +      },
      +    }),
      +  ],
      +}
      +
    内置支持语言
    • 简体中文 (zh-CN)
    • 繁体中文 (zh-TW)
    • 英文(美国) (en-US)
    • 德语 (de-DE)
    • 德语(澳大利亚) (de-AT)
    • 俄语 (ru-RU)
    • 乌克兰语 (uk-UA)
    • 越南语 (vi-VN)
    • 葡萄牙语(巴西) (pt-BR)
    • 波兰语 (pl-PL)
    • 法语 (fr-FR)
    • 西班牙语 (es-ES)
    • 斯洛伐克 (sk-SK)
    • 日语 (ja-JP)
    • 土耳其语 (tr-TR)
    • 韩语 (ko-KR)
    • 芬兰语 (fi-FI)
    • 印尼语 (id-ID)
    • 荷兰语 (nl-NL)

    Frontmatter

    copy.triggerLength

    • 类型:number
    • 默认值:100
    • 详情: 触发附加版权的最小内容长度

    copy.maxLength

    • 类型:number
    • 默认值:0
    • 详情: 允许复制的最大内容长度,0 意味着无限制。

    copy.disableCopy

    • 类型:boolean
    • 默认值:false
    • 详情:禁用复制

    copy.disableSelection

    • 类型:boolean
    • 默认值:false
    • 详情:禁用选择
    `,50);function d(g,h){const l=e("NpmBadge");return a(),i("div",null,[t,o(l,{package:"@vuepress/plugin-copyright"}),r])}const y=n(p,[["render",d],["__file","copyright.html.vue"]]),v=JSON.parse('{"path":"/zh/plugins/copyright.html","title":"copyright","lang":"zh-CN","frontmatter":{},"headers":[{"level":2,"title":"使用","slug":"使用","link":"#使用","children":[{"level":3,"title":"启用版权信息","slug":"启用版权信息","link":"#启用版权信息","children":[]},{"level":3,"title":"禁用复制和选择","slug":"禁用复制和选择","link":"#禁用复制和选择","children":[]}]},{"level":2,"title":"选项","slug":"选项","link":"#选项","children":[{"level":3,"title":"author","slug":"author","link":"#author","children":[]},{"level":3,"title":"license","slug":"license","link":"#license","children":[]},{"level":3,"title":"authorGetter","slug":"authorgetter","link":"#authorgetter","children":[]},{"level":3,"title":"licenseGetter","slug":"licensegetter","link":"#licensegetter","children":[]},{"level":3,"title":"copyrightGetter","slug":"copyrightgetter","link":"#copyrightgetter","children":[]},{"level":3,"title":"triggerLength","slug":"triggerlength","link":"#triggerlength","children":[]},{"level":3,"title":"maxLength","slug":"maxlength","link":"#maxlength","children":[]},{"level":3,"title":"global","slug":"global","link":"#global","children":[]},{"level":3,"title":"disableCopy","slug":"disablecopy","link":"#disablecopy","children":[]},{"level":3,"title":"disableSelection","slug":"disableselection","link":"#disableselection","children":[]},{"level":3,"title":"canonical","slug":"canonical","link":"#canonical","children":[]},{"level":3,"title":"locales","slug":"locales","link":"#locales","children":[]}]},{"level":2,"title":"Frontmatter","slug":"frontmatter","link":"#frontmatter","children":[{"level":3,"title":"copy.triggerLength","slug":"copy-triggerlength","link":"#copy-triggerlength","children":[]},{"level":3,"title":"copy.maxLength","slug":"copy-maxlength","link":"#copy-maxlength","children":[]},{"level":3,"title":"copy.disableCopy","slug":"copy-disablecopy","link":"#copy-disablecopy","children":[]},{"level":3,"title":"copy.disableSelection","slug":"copy-disableselection","link":"#copy-disableselection","children":[]}]}],"git":{"updatedTime":1708274662000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":3}]},"filePathRelative":"zh/plugins/copyright.md"}');export{y as comp,v as data}; diff --git a/assets/copyright.html-xIj0WcRd.js b/assets/copyright.html-xIj0WcRd.js new file mode 100644 index 0000000000..05b9982a14 --- /dev/null +++ b/assets/copyright.html-xIj0WcRd.js @@ -0,0 +1,66 @@ +import{_ as n,r as l,o as a,c as i,a as o,b as e,e as t}from"./app-DI49vYHA.js";const c={},p=e("h1",{id:"copyright",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#copyright"},[e("span",null,"copyright")])],-1),r=t(`

    This plugin can automatically append copyright information when visitors copy content from your site, and can also prohibit site copying or selection.

    Usage

    npm i -D @vuepress/plugin-copyright@next
    +
    import { copyrightPlugin } from '@vuepress/plugin-copyright'
    +
    +export default {
    +  plugins: [
    +    copyrightPlugin({
    +      // options
    +    }),
    +  ],
    +}
    +

    This plugin is disabled globally by default, you can:

    • Manually enable it by setting copy: true in page frontmatter
    • Set global: true in plugin options to enable it globally, and set copy: false in page frontmatter to disable it.

    To avoid disturbing visitors, copyright information will be appended only when the copied content length is greater than 100. Set triggerLength in plugin options if you want to change this threshold, or via copy.triggerLength in page frontmatter.

    You can set default author and license information via author and license in plugin options.

    If your site have different authors and license in different pages, you can set authorGetter and licenseGetter with function (page: Page) => string that takes the current page object as parameter and returns the corresponding information.

    The plugin will generate copyright information from author, license, and page link via template by default, and append it when copying. If you think that this is not flexible enough, you can set copyrightGetter option to return a completely customized information with Page object or return null to use the default template.

    Disable Copy and Selection

    If you want to prevent users copying long content, you can set maxLength in plugin options to customize this limit, or via copy.maxLength in page frontmatter.

    • If you don't want users to copy your entire site or specific page text, you can set disableCopy in plugin options or copy.disableCopy in page frontmatter, the latter has higher priority.
    • If you don't want users to select your entire site or specific page text, you can set disableSelection in plugin options or copy.disableSelection in page frontmatter. This option has higher priority.

    Options

    author

    • Type: string
    • Details: Default author Information

    license

    • Type: string
    • Details: Default license Information

    authorGetter

    • Type: (page: Page) => string | null
    • Details: Author getter

    licenseGetter

    • Type: (page: Page) => string | null
    • Details: License getter

    copyrightGetter

    • Type: (page: Page) => string | null
    • Details: Copyright getter

    triggerLength

    • Type: number
    • Default: 100
    • Details: Min content length triggering copyright append

    maxLength

    • Type: number
    • Default: 0
    • Details: Max content length which allows to copy, 0 means no limit.

    global

    • Type: boolean
    • Default: false
    • Details: Whether enable globally.

    disableCopy

    • Type: boolean
    • Default: false
    • Details: Disable copy

    disableSelection

    • Type: boolean
    • Default: false
    • Details: Disable selection

    canonical

    • Type: string
    • Details: Canonical deploy location.

    Example

    If you are deploying same content under https://myblog.com and https://blog.com/username/, you may want to prefer one site as reference link.

    • If you prefer the first one, you should set canonical to https://myblog.com
    • If you prefer the second one, you should set canonical to https://blog.com/username/

    So copyright message triggered on another site also points to your preferred site.

    locales

    • Type: CopyrightPluginLocaleConfig

      interface CopyrightPluginLocaleData {
      +  /**
      +   * Author text
      +   *
      +   * @description \`:author\` will be replaced by author
      +   */
      +  author: string
      +
      +  /**
      +   * License text
      +   *
      +   * @description \`:license\` will be replaced by current license
      +   */
      +  license: string
      +
      +  /**
      +   * Link text
      +   *
      +   * @description \`:link\` will be replaced by current page link
      +   */
      +  link: string
      +}
      +
      +interface CopyrightPluginLocaleConfig {
      +  [localePath: string]: CopyrightPluginLocaleData
      +}
      +
    • Details: Locale config for copyright plugin.

    • Example:

      import { copyrightPlugin } from '@vuepress/plugin-copyright'
      +
      +export default {
      +  locales: {
      +    '/': {
      +      // this is a supported language
      +      lang: 'en-US',
      +    },
      +    '/xx/': {
      +      // the plugin does not support this language
      +      lang: 'mm-NN',
      +    },
      +  },
      +
      +  plugins: [
      +    copyrightPlugin({
      +      locales: {
      +        '/': {
      +          // Override link text
      +          link: 'Original posted at :link',
      +        },
      +
      +        '/xx/': {
      +          // Complete locale config for \`mm-NN\` language here
      +        },
      +      },
      +    }),
      +  ],
      +}
      +
    Built-in Supported Languages
    • Simplified Chinese (zh-CN)
    • Traditional Chinese (zh-TW)
    • English (United States) (en-US)
    • German (de-DE)
    • German (Australia) (de-AT)
    • Russian (ru-RU)
    • Ukrainian (uk-UA)
    • Vietnamese (vi-VN)
    • Portuguese (Brazil) (pt-BR)
    • Polish (pl-PL)
    • French (fr-FR)
    • Spanish (es-ES)
    • Slovak (sk-SK)
    • Japanese (ja-JP)
    • Turkish (tr-TR)
    • Korean (ko-KR)
    • Finnish (fi-FI)
    • Indonesian (id-ID)
    • Dutch (nl-NL)

    Frontmatter

    copy.triggerLength

    • Type: number
    • Default: 100
    • Details: Min content length triggering copyright append

    copy.maxLength

    • Type: number
    • Default: 0
    • Details: Max content length which allows to copy, 0 means no limit.

    copy.disableCopy

    • Type: boolean
    • Default: false
    • Details: Disable copy

    copy.disableSelection

    • Type: boolean
    • Default: false
    • Details: Disable selection
    `,50);function d(g,h){const s=l("NpmBadge");return a(),i("div",null,[p,o(s,{package:"@vuepress/plugin-copyright"}),r])}const y=n(c,[["render",d],["__file","copyright.html.vue"]]),D=JSON.parse('{"path":"/plugins/copyright.html","title":"copyright","lang":"en-US","frontmatter":{},"headers":[{"level":2,"title":"Usage","slug":"usage","link":"#usage","children":[{"level":3,"title":"Enabling Copyright","slug":"enabling-copyright","link":"#enabling-copyright","children":[]},{"level":3,"title":"Disable Copy and Selection","slug":"disable-copy-and-selection","link":"#disable-copy-and-selection","children":[]}]},{"level":2,"title":"Options","slug":"options","link":"#options","children":[{"level":3,"title":"author","slug":"author","link":"#author","children":[]},{"level":3,"title":"license","slug":"license","link":"#license","children":[]},{"level":3,"title":"authorGetter","slug":"authorgetter","link":"#authorgetter","children":[]},{"level":3,"title":"licenseGetter","slug":"licensegetter","link":"#licensegetter","children":[]},{"level":3,"title":"copyrightGetter","slug":"copyrightgetter","link":"#copyrightgetter","children":[]},{"level":3,"title":"triggerLength","slug":"triggerlength","link":"#triggerlength","children":[]},{"level":3,"title":"maxLength","slug":"maxlength","link":"#maxlength","children":[]},{"level":3,"title":"global","slug":"global","link":"#global","children":[]},{"level":3,"title":"disableCopy","slug":"disablecopy","link":"#disablecopy","children":[]},{"level":3,"title":"disableSelection","slug":"disableselection","link":"#disableselection","children":[]},{"level":3,"title":"canonical","slug":"canonical","link":"#canonical","children":[]},{"level":3,"title":"locales","slug":"locales","link":"#locales","children":[]}]},{"level":2,"title":"Frontmatter","slug":"frontmatter","link":"#frontmatter","children":[{"level":3,"title":"copy.triggerLength","slug":"copy-triggerlength","link":"#copy-triggerlength","children":[]},{"level":3,"title":"copy.maxLength","slug":"copy-maxlength","link":"#copy-maxlength","children":[]},{"level":3,"title":"copy.disableCopy","slug":"copy-disablecopy","link":"#copy-disablecopy","children":[]},{"level":3,"title":"copy.disableSelection","slug":"copy-disableselection","link":"#copy-disableselection","children":[]}]}],"git":{"updatedTime":1708274662000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":2}]},"filePathRelative":"plugins/copyright.md"}');export{y as comp,D as data}; diff --git a/assets/docsearch.html-B3tKWbdo.js b/assets/docsearch.html-B3tKWbdo.js new file mode 100644 index 0000000000..a0ed795cd0 --- /dev/null +++ b/assets/docsearch.html-B3tKWbdo.js @@ -0,0 +1,205 @@ +import{_ as i,r,o as t,c as d,a as l,b as s,d as n,w as p,e}from"./app-DI49vYHA.js";const D={},y=s("h1",{id:"docsearch",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#docsearch"},[s("span",null,"docsearch")])],-1),u={href:"https://docsearch.algolia.com/",target:"_blank",rel:"noopener noreferrer"},h=e(`

    TIP

    Default theme will add DocSearch to the navbar once you configure this plugin correctly.

    This plugin may not be used directly in other themes, so you'd better refer to the documentation of your theme for more details.

    Usage

    npm i -D @vuepress/plugin-docsearch@next
    +
    import { docsearchPlugin } from '@vuepress/plugin-docsearch'
    +
    +export default {
    +  plugins: [
    +    docsearchPlugin({
    +      // options
    +    }),
    +  ],
    +}
    +

    Get Search Index

    `,5),v={href:"https://docsearch.algolia.com/apply/",target:"_blank",rel:"noopener noreferrer"},C=s("a",{href:"#apikey"},"apiKey",-1),b=s("a",{href:"#indexname"},"indexName",-1),m={href:"https://docsearch.algolia.com/docs/run-your-own/",target:"_blank",rel:"noopener noreferrer"},E=s("a",{href:"#appId"},"appId",-1),g=s("a",{href:"#apikey"},"apiKey",-1),_=s("a",{href:"#indexname"},"indexName",-1),A=e(`
    Official crawler config
    new Crawler({
    +  appId: 'YOUR_APP_ID',
    +  apiKey: 'YOUR_API_KEY',
    +  rateLimit: 8,
    +  startUrls: [
    +    // These are urls which algolia start to craw
    +    // If your site is divided in to mutiple parts,
    +    // you may want to set mutiple entry links
    +    'https://YOUR_WEBSITE_URL/',
    +  ],
    +  sitemaps: [
    +    // if you are using sitemap plugins (e.g.: @vuepress-plugin/sitemap), you may provide one
    +    'https://YOUR_WEBSITE_URL/sitemap.xml',
    +  ],
    +  ignoreCanonicalTo: false,
    +  exclusionPatterns: [
    +    // You can use this to stop algolia crawing some paths
    +  ],
    +  discoveryPatterns: [
    +    // These are urls which algolia looking for,
    +    'https://YOUR_WEBSITE_URL/**',
    +  ],
    +  // Crawler schedule, set it according to your docs update frequency
    +  schedule: 'at 02:00 every 1 day',
    +  actions: [
    +    // you may have mutiple actions, especially when you are deploying mutiple docs under one domain
    +    {
    +      // name the index with name you like
    +      indexName: 'YOUR_INDEX_NAME',
    +      // paths where the index take effect
    +      pathsToMatch: ['https://YOUR_WEBSITE_URL/**'],
    +      // controls how algolia extracts records from your site
    +      recordExtractor: ({ $, helpers }) => {
    +        // options for @vuepress/theme-default
    +        return helpers.docsearch({
    +          recordProps: {
    +            lvl0: {
    +              selectors: '.sidebar-heading.active',
    +              defaultValue: 'Documentation',
    +            },
    +            lvl1: '.theme-default-content h1',
    +            lvl2: '.theme-default-content h2',
    +            lvl3: '.theme-default-content h3',
    +            lvl4: '.theme-default-content h4',
    +            lvl5: '.theme-default-content h5',
    +            lvl6: '.theme-default-content h6',
    +            content: '.theme-default-content p, .theme-default-content li',
    +          },
    +          indexHeadings: true,
    +        })
    +      },
    +    },
    +  ],
    +  initialIndexSettings: {
    +    // controls how index are initialized
    +    // only has effects before index are initialize
    +    // you may need to delete your index and recraw after modification
    +    YOUR_INDEX_NAME: {
    +      attributesForFaceting: ['type', 'lang'],
    +      attributesToRetrieve: ['hierarchy', 'content', 'anchor', 'url'],
    +      attributesToHighlight: ['hierarchy', 'hierarchy_camel', 'content'],
    +      attributesToSnippet: ['content:10'],
    +      camelCaseAttributes: ['hierarchy', 'hierarchy_radio', 'content'],
    +      searchableAttributes: [
    +        'unordered(hierarchy_radio_camel.lvl0)',
    +        'unordered(hierarchy_radio.lvl0)',
    +        'unordered(hierarchy_radio_camel.lvl1)',
    +        'unordered(hierarchy_radio.lvl1)',
    +        'unordered(hierarchy_radio_camel.lvl2)',
    +        'unordered(hierarchy_radio.lvl2)',
    +        'unordered(hierarchy_radio_camel.lvl3)',
    +        'unordered(hierarchy_radio.lvl3)',
    +        'unordered(hierarchy_radio_camel.lvl4)',
    +        'unordered(hierarchy_radio.lvl4)',
    +        'unordered(hierarchy_radio_camel.lvl5)',
    +        'unordered(hierarchy_radio.lvl5)',
    +        'unordered(hierarchy_radio_camel.lvl6)',
    +        'unordered(hierarchy_radio.lvl6)',
    +        'unordered(hierarchy_camel.lvl0)',
    +        'unordered(hierarchy.lvl0)',
    +        'unordered(hierarchy_camel.lvl1)',
    +        'unordered(hierarchy.lvl1)',
    +        'unordered(hierarchy_camel.lvl2)',
    +        'unordered(hierarchy.lvl2)',
    +        'unordered(hierarchy_camel.lvl3)',
    +        'unordered(hierarchy.lvl3)',
    +        'unordered(hierarchy_camel.lvl4)',
    +        'unordered(hierarchy.lvl4)',
    +        'unordered(hierarchy_camel.lvl5)',
    +        'unordered(hierarchy.lvl5)',
    +        'unordered(hierarchy_camel.lvl6)',
    +        'unordered(hierarchy.lvl6)',
    +        'content',
    +      ],
    +      distinct: true,
    +      attributeForDistinct: 'url',
    +      customRanking: [
    +        'desc(weight.pageRank)',
    +        'desc(weight.level)',
    +        'asc(weight.position)',
    +      ],
    +      ranking: [
    +        'words',
    +        'filters',
    +        'typo',
    +        'attribute',
    +        'proximity',
    +        'exact',
    +        'custom',
    +      ],
    +      highlightPreTag: '<span class="algolia-docsearch-suggestion--highlight">',
    +      highlightPostTag: '</span>',
    +      minWordSizefor1Typo: 3,
    +      minWordSizefor2Typos: 7,
    +      allowTyposOnNumericTokens: false,
    +      minProximity: 1,
    +      ignorePlurals: true,
    +      advancedSyntax: true,
    +      attributeCriteriaComputedByMinProximity: true,
    +      removeWordsIfNoResults: 'allOptional',
    +    },
    +  },
    +})
    +


































     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     








     
































































    The above recordProps is the configuration used for the default theme. You can modify them according to the theme you are using.

    Notice that the initialIndexSettings.YOUR_INDEX_NAME.attributesForFaceting fields must include 'lang' to make this plugin work properly.

    `,1),f={class:"custom-container tip"},x=s("p",{class:"custom-container-title"},"TIP",-1),B={href:"https://crawler.algolia.com/admin/crawlers/",target:"_blank",rel:"noopener noreferrer"},F=s("h2",{id:"options",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#options"},[s("span",null,"Options")])],-1),k=s("h3",{id:"apikey",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#apikey"},[s("span",null,"apiKey")])],-1),w=s("li",null,[s("p",null,[n("Type: "),s("code",null,"string")])],-1),S=s("li",null,[s("p",null,[n("Required: "),s("code",null,"true")])],-1),T=s("li",null,[s("p",null,"Details:"),s("p",null,[n("The "),s("code",null,"apiKey"),n(" that you received from the DocSearch team, or generated by yourself.")])],-1),I=s("p",null,"Also see:",-1),P={href:"https://docsearch.algolia.com/docs/api#apikey",target:"_blank",rel:"noopener noreferrer"},N=s("h3",{id:"indexname",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#indexname"},[s("span",null,"indexName")])],-1),O=s("li",null,[s("p",null,[n("Type: "),s("code",null,"string")])],-1),R=s("li",null,[s("p",null,[n("Required: "),s("code",null,"true")])],-1),U=s("li",null,[s("p",null,"Details:"),s("p",null,[n("The "),s("code",null,"indexName"),n(" that you received from the DocSearch team, or generated by yourself.")])],-1),Y=s("p",null,"Also see:",-1),z={href:"https://docsearch.algolia.com/docs/api#indexname",target:"_blank",rel:"noopener noreferrer"},j=s("h3",{id:"appid",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#appid"},[s("span",null,"appId")])],-1),q=s("li",null,[s("p",null,[n("Type: "),s("code",null,"string")])],-1),K=s("li",null,[s("p",null,[n("Required: "),s("code",null,"true")])],-1),L=s("li",null,[s("p",null,"Details:"),s("p",null,"It defines your own application ID.")],-1),W=s("p",null,"Also see:",-1),M={href:"https://docsearch.algolia.com/docs/api#appid",target:"_blank",rel:"noopener noreferrer"},V=s("h3",{id:"searchparameters",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#searchparameters"},[s("span",null,"searchParameters")])],-1),H=s("li",null,[s("p",null,[n("Type: "),s("code",null,"SearchParameters")])],-1),X=s("li",null,[s("p",null,"Details:"),s("p",null,"Parameters of Algolia Search API.")],-1),G=s("p",null,"Also see:",-1),Q={href:"https://docsearch.algolia.com/docs/api/#searchparameters",target:"_blank",rel:"noopener noreferrer"},J={href:"https://www.algolia.com/doc/api-reference/search-api-parameters/",target:"_blank",rel:"noopener noreferrer"},$=s("h3",{id:"placeholder",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#placeholder"},[s("span",null,"placeholder")])],-1),Z=s("li",null,[s("p",null,[n("Type: "),s("code",null,"string")])],-1),ss=s("li",null,[s("p",null,[n("Default: "),s("code",null,"'Search docs'")])],-1),ns=s("li",null,[s("p",null,"Details:"),s("p",null,"The placeholder attribute of the search input.")],-1),ls=s("p",null,"Also see:",-1),as={href:"https://docsearch.algolia.com/docs/api/#placeholder",target:"_blank",rel:"noopener noreferrer"},es=s("h3",{id:"disableuserpersonalization",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#disableuserpersonalization"},[s("span",null,"disableUserPersonalization")])],-1),os=s("li",null,[s("p",null,[n("Type: "),s("code",null,"boolean")])],-1),ps=s("li",null,[s("p",null,[n("Default: "),s("code",null,"false")])],-1),rs=s("li",null,[s("p",null,"Details:"),s("p",null,"Whether to disable all personalized features: recent searches, favorite searches, etc.")],-1),cs=s("p",null,"Also see:",-1),is={href:"https://docsearch.algolia.com/docs/api/#disableuserpersonalization",target:"_blank",rel:"noopener noreferrer"},ts=s("h3",{id:"initialquery",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#initialquery"},[s("span",null,"initialQuery")])],-1),ds=s("li",null,[s("p",null,[n("Type: "),s("code",null,"string")])],-1),Ds=s("li",null,[s("p",null,"Details:"),s("p",null,"The initial query when the modal opens.")],-1),ys=s("p",null,"Also see:",-1),us={href:"https://docsearch.algolia.com/docs/api/#initialquery",target:"_blank",rel:"noopener noreferrer"},hs=s("h3",{id:"translations",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#translations"},[s("span",null,"translations")])],-1),vs=s("li",null,[s("p",null,[n("Type: "),s("code",null,"Partial")])],-1),Cs=s("li",null,[s("p",null,"Details:"),s("p",null,"Allow replacing the default text in the DocSearch button or modal.")],-1),bs=s("p",null,"Also see:",-1),ms={href:"https://docsearch.algolia.com/docs/api/#translations",target:"_blank",rel:"noopener noreferrer"},Es=e(`

    locales

    • Type: Record<string, DocsearchPluginOptions>

    • Details:

      Options of this plugin in different locales.

      All other options of this plugin are acceptable in locale config.

    • Example:

    export default {
    +  plugins: [
    +    docsearchPlugin({
    +      appId: '<APP_ID>',
    +      apiKey: '<API_KEY>',
    +      indexName: '<INDEX_NAME>',
    +      locales: {
    +        '/': {
    +          placeholder: 'Search Documentation',
    +          translations: {
    +            button: {
    +              buttonText: 'Search Documentation',
    +            },
    +          },
    +        },
    +        '/zh/': {
    +          placeholder: '搜索文档',
    +          translations: {
    +            button: {
    +              buttonText: '搜索文档',
    +            },
    +          },
    +        },
    +      },
    +    }),
    +  ],
    +}
    +
    `,3),gs=s("h3",{id:"indexbase",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#indexbase"},[s("span",null,"indexBase")])],-1),_s=s("li",null,[s("p",null,[n("Type: "),s("code",null,"string")])],-1),As=s("p",null,"Details:",-1),fs=s("p",null,"The base path of the search index.",-1),xs=s("p",null,[n("If you are deploying your site to multiple domains, you don't need to submit all of them to DocSearch and generate search index separately. You could choose one of the domains as the "),s("em",null,"index domain"),n(", and only submit the "),s("em",null,"index domain"),n(" to Docsearch for crawling search index. Then, you could reuse the search index across all deployments.")],-1),Bs=s("em",null,"index domain",-1),Fs=e('

    injectStyles

    • Type: boolean

    • Default: true

    • Details:

      Whether to inject the default styles of DocSearch or not.

      If you think the default styles of DocSearch is not compatible with your site, you can try to override the default styles, or set this option to false to totally exclude the default styles.

      When this option is disabled, you need to import your own styles for DocSearch. Also notice that all styles customization in Styles section would be unavailable.

    Styles

    ',3),ks={href:"https://docsearch.algolia.com/docs/styling",target:"_blank",rel:"noopener noreferrer"},ws=e(`
    :root {
    +  --docsearch-primary-color: rgb(84, 104, 255);
    +  --docsearch-text-color: rgb(28, 30, 33);
    +  --docsearch-spacing: 12px;
    +  --docsearch-icon-stroke-width: 1.4;
    +  --docsearch-highlight-color: var(--docsearch-primary-color);
    +  --docsearch-muted-color: rgb(150, 159, 175);
    +  --docsearch-container-background: rgba(101, 108, 133, 0.8);
    +  --docsearch-logo-color: rgba(84, 104, 255);
    +
    +  /* modal */
    +  --docsearch-modal-width: 560px;
    +  --docsearch-modal-height: 600px;
    +  --docsearch-modal-background: rgb(245, 246, 247);
    +  --docsearch-modal-shadow: inset 1px 1px 0 0 rgba(255, 255, 255, 0.5), 0 3px
    +      8px 0 rgba(85, 90, 100, 1);
    +
    +  /* searchbox */
    +  --docsearch-searchbox-height: 56px;
    +  --docsearch-searchbox-background: rgb(235, 237, 240);
    +  --docsearch-searchbox-focus-background: #fff;
    +  --docsearch-searchbox-shadow: inset 0 0 0 2px var(--docsearch-primary-color);
    +
    +  /* hit */
    +  --docsearch-hit-height: 56px;
    +  --docsearch-hit-color: rgb(68, 73, 80);
    +  --docsearch-hit-active-color: #fff;
    +  --docsearch-hit-background: #fff;
    +  --docsearch-hit-shadow: 0 1px 3px 0 rgb(212, 217, 225);
    +
    +  /* key */
    +  --docsearch-key-gradient: linear-gradient(
    +    -225deg,
    +    rgb(213, 219, 228) 0%,
    +    rgb(248, 248, 248) 100%
    +  );
    +  --docsearch-key-shadow: inset 0 -2px 0 0 rgb(205, 205, 230), inset 0 0 1px 1px
    +      #fff, 0 1px 2px 1px rgba(30, 35, 90, 0.4);
    +
    +  /* footer */
    +  --docsearch-footer-height: 44px;
    +  --docsearch-footer-background: #fff;
    +  --docsearch-footer-shadow: 0 -1px 0 0 rgb(224, 227, 232), 0 -3px 6px 0 rgba(69, 98, 155, 0.12);
    +}
    +

    Components

    Docsearch

    • Details:

      This plugin will register a <Docsearch /> component globally, and you can use it without any props.

      Put this component to where you want to place the docsearch button. For example, default theme puts this component to the end of the navbar.

    TIP

    This component is mainly used for theme development. You don't need to use it directly in most cases.

    `,5);function Ss(Ts,Is){const c=r("NpmBadge"),a=r("ExternalLinkIcon"),o=r("RouteLink");return t(),d("div",null,[y,l(c,{package:"@vuepress/plugin-docsearch"}),s("p",null,[n("Integrate "),s("a",u,[n("Algolia DocSearch"),l(a)]),n(" into VuePress, which can provide search to your documentation site.")]),h,s("p",null,[n("You need to "),s("a",v,[n("submit the URL of your site"),l(a)]),n(" to join the DocSearch program. The DocSearch team will send "),C,n(" and "),b,n(" to your email once the index is generated. Then you can configure this plugin to enable DocSearch in VuePress.")]),s("p",null,[n("Alternatively, you can "),s("a",m,[n("run your own crawler"),l(a)]),n(" to generate the index, and then use your own "),E,n(", "),g,n(" and "),_,n(" to configure this plugin.")]),A,s("div",f,[x,s("p",null,[n("If you are not using default theme, or you meet any problems when using docsearch, you can also check the above example crawler config, and ahead to "),s("a",B,[n("Algolia Crawler"),l(a)]),n(", and edit your config with 'Editor' panel in project sidebar.")])]),F,k,s("ul",null,[w,S,T,s("li",null,[I,s("ul",null,[s("li",null,[s("a",P,[n("DocSearch > Options > apiKey"),l(a)])])])])]),N,s("ul",null,[O,R,U,s("li",null,[Y,s("ul",null,[s("li",null,[s("a",z,[n("DocSearch > Options > indexName"),l(a)])])])])]),j,s("ul",null,[q,K,L,s("li",null,[W,s("ul",null,[s("li",null,[s("a",M,[n("DocSearch > Options > appId"),l(a)])])])])]),V,s("ul",null,[H,X,s("li",null,[G,s("ul",null,[s("li",null,[s("a",Q,[n("DocSearch > Options > searchParameters"),l(a)])]),s("li",null,[s("a",J,[n("Algolia > Search API Parameters"),l(a)])])])])]),$,s("ul",null,[Z,ss,ns,s("li",null,[ls,s("ul",null,[s("li",null,[s("a",as,[n("DocSearch > Options > placeholder"),l(a)])])])])]),es,s("ul",null,[os,ps,rs,s("li",null,[cs,s("ul",null,[s("li",null,[s("a",is,[n("DocSearch > Options > disableUserPersonalization"),l(a)])])])])]),ts,s("ul",null,[ds,Ds,s("li",null,[ys,s("ul",null,[s("li",null,[s("a",us,[n("DocSearch > Options > initialQuery"),l(a)])])])])]),hs,s("ul",null,[vs,Cs,s("li",null,[bs,s("ul",null,[s("li",null,[s("a",ms,[n("DocSearch > Options > translations"),l(a)])])])])]),Es,s("ul",null,[s("li",null,[n("Also see: "),s("ul",null,[s("li",null,[l(o,{to:"/guide/i18n.html"},{default:p(()=>[n("Guide > I18n")]),_:1})])])])]),gs,s("ul",null,[_s,s("li",null,[s("p",null,[n("Default: "),l(o,{to:"/config.html#base"},{default:p(()=>[n("base")]),_:1})])]),s("li",null,[As,fs,xs,s("p",null,[n("However, if the "),l(o,{to:"/config.html#base"},{default:p(()=>[n("base")]),_:1}),n(" of your deployments are different for different domains, you need to set the option to the "),l(o,{to:"/config.html#base"},{default:p(()=>[n("base")]),_:1}),n(" of your "),Bs,n(", so that other deployments could reuse the search index correctly.")])])]),Fs,s("p",null,[n("You can customize styles via CSS variables that provided by "),s("a",ks,[n("@docsearch/css"),l(a)]),n(":")]),ws])}const Ns=i(D,[["render",Ss],["__file","docsearch.html.vue"]]),Os=JSON.parse('{"path":"/plugins/docsearch.html","title":"docsearch","lang":"en-US","frontmatter":{},"headers":[{"level":2,"title":"Usage","slug":"usage","link":"#usage","children":[]},{"level":2,"title":"Get Search Index","slug":"get-search-index","link":"#get-search-index","children":[]},{"level":2,"title":"Options","slug":"options","link":"#options","children":[{"level":3,"title":"apiKey","slug":"apikey","link":"#apikey","children":[]},{"level":3,"title":"indexName","slug":"indexname","link":"#indexname","children":[]},{"level":3,"title":"appId","slug":"appid","link":"#appid","children":[]},{"level":3,"title":"searchParameters","slug":"searchparameters","link":"#searchparameters","children":[]},{"level":3,"title":"placeholder","slug":"placeholder","link":"#placeholder","children":[]},{"level":3,"title":"disableUserPersonalization","slug":"disableuserpersonalization","link":"#disableuserpersonalization","children":[]},{"level":3,"title":"initialQuery","slug":"initialquery","link":"#initialquery","children":[]},{"level":3,"title":"translations","slug":"translations","link":"#translations","children":[]},{"level":3,"title":"locales","slug":"locales","link":"#locales","children":[]},{"level":3,"title":"indexBase","slug":"indexbase","link":"#indexbase","children":[]},{"level":3,"title":"injectStyles","slug":"injectstyles","link":"#injectstyles","children":[]}]},{"level":2,"title":"Styles","slug":"styles","link":"#styles","children":[]},{"level":2,"title":"Components","slug":"components","link":"#components","children":[{"level":3,"title":"Docsearch","slug":"docsearch-1","link":"#docsearch-1","children":[]}]}],"git":{"updatedTime":1706627619000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":2}]},"filePathRelative":"plugins/docsearch.md"}');export{Ns as comp,Os as data}; diff --git a/assets/docsearch.html-D42wByNJ.js b/assets/docsearch.html-D42wByNJ.js new file mode 100644 index 0000000000..70d909d9b3 --- /dev/null +++ b/assets/docsearch.html-D42wByNJ.js @@ -0,0 +1,203 @@ +import{_ as i,r as c,o as t,c as D,a as l,b as s,d as n,w as o,e}from"./app-DI49vYHA.js";const d={},y=s("h1",{id:"docsearch",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#docsearch"},[s("span",null,"docsearch")])],-1),u={href:"https://docsearch.algolia.com/",target:"_blank",rel:"noopener noreferrer"},h=e(`

    提示

    当你正确配置该插件后,默认主题会把 DocSearch 按钮添加到导航栏。

    该插件不一定能在其他主题中直接使用,因此你应参考主题本身的文档来获取更多信息。

    使用方法

    npm i -D @vuepress/plugin-docsearch@next
    +
    import { docsearchPlugin } from '@vuepress/plugin-docsearch'
    +
    +export default {
    +  plugins: [
    +    docsearchPlugin({
    +      // 配置项
    +    }),
    +  ],
    +}
    +

    获取搜索索引

    `,5),v={href:"https://docsearch.algolia.com/apply/",target:"_blank",rel:"noopener noreferrer"},C=s("a",{href:"#apikey"},"apiKey",-1),b=s("a",{href:"#indexname"},"indexName",-1),m={href:"https://docsearch.algolia.com/docs/run-your-own/",target:"_blank",rel:"noopener noreferrer"},E=s("a",{href:"#appId"},"appId",-1),_=s("a",{href:"#apikey"},"apiKey",-1),g=s("a",{href:"#indexname"},"indexName",-1),A=e(`
    官方爬虫配置示例
    new Crawler({
    +  appId: 'YOUR_APP_ID',
    +  apiKey: 'YOUR_API_KEY',
    +  rateLimit: 8,
    +  startUrls: [
    +    // 这是 Algolia 开始抓取网站的初始地址
    +    // 如果你的网站被分为数个独立部分,你可能需要在此设置多个入口链接
    +    'https://YOUR_WEBSITE_URL/',
    +  ],
    +  sitemaps: [
    +    // 如果你在使用 Sitemap 插件 (如: @vuepress-plugin/sitemap),你可以提供 Sitemap 链接
    +    'https://YOUR_WEBSITE_URL/sitemap.xml',
    +  ],
    +  ignoreCanonicalTo: false,
    +  exclusionPatterns: [
    +    // 你可以通过它阻止 Algolia 抓取某些 URL
    +  ],
    +  discoveryPatterns: [
    +    // 这是 Algolia 抓取 URL 的范围
    +    'https://YOUR_WEBSITE_URL/**',
    +  ],
    +  // 爬虫执行的计划时间,可根据文档更新频率设置
    +  schedule: 'at 02:00 every 1 day',
    +  actions: [
    +    // 你可以拥有多个 action,特别是你在一个域名下部署多个文档时
    +    {
    +      // 使用适当的名称为索引命名
    +      indexName: 'YOUR_INDEX_NAME',
    +      // 索引生效的路径
    +      pathsToMatch: ['https://YOUR_WEBSITE_URL/**'],
    +      // 控制 Algolia 如何抓取你的站点
    +      recordExtractor: ({ $, helpers }) => {
    +        // @vuepress/theme-default 的选项
    +        return helpers.docsearch({
    +          recordProps: {
    +            lvl0: {
    +              selectors: '.sidebar-heading.active',
    +              defaultValue: 'Documentation',
    +            },
    +            lvl1: '.theme-default-content h1',
    +            lvl2: '.theme-default-content h2',
    +            lvl3: '.theme-default-content h3',
    +            lvl4: '.theme-default-content h4',
    +            lvl5: '.theme-default-content h5',
    +            lvl6: '.theme-default-content h6',
    +            content: '.theme-default-content p, .theme-default-content li',
    +          },
    +          indexHeadings: true,
    +        })
    +      },
    +    },
    +  ],
    +  initialIndexSettings: {
    +    // 控制索引如何被初始化,这仅当索引尚未生成时有效
    +    // 你可能需要在修改后手动删除并重新生成新的索引
    +    YOUR_INDEX_NAME: {
    +      attributesForFaceting: ['type', 'lang'],
    +      attributesToRetrieve: ['hierarchy', 'content', 'anchor', 'url'],
    +      attributesToHighlight: ['hierarchy', 'hierarchy_camel', 'content'],
    +      attributesToSnippet: ['content:10'],
    +      camelCaseAttributes: ['hierarchy', 'hierarchy_radio', 'content'],
    +      searchableAttributes: [
    +        'unordered(hierarchy_radio_camel.lvl0)',
    +        'unordered(hierarchy_radio.lvl0)',
    +        'unordered(hierarchy_radio_camel.lvl1)',
    +        'unordered(hierarchy_radio.lvl1)',
    +        'unordered(hierarchy_radio_camel.lvl2)',
    +        'unordered(hierarchy_radio.lvl2)',
    +        'unordered(hierarchy_radio_camel.lvl3)',
    +        'unordered(hierarchy_radio.lvl3)',
    +        'unordered(hierarchy_radio_camel.lvl4)',
    +        'unordered(hierarchy_radio.lvl4)',
    +        'unordered(hierarchy_radio_camel.lvl5)',
    +        'unordered(hierarchy_radio.lvl5)',
    +        'unordered(hierarchy_radio_camel.lvl6)',
    +        'unordered(hierarchy_radio.lvl6)',
    +        'unordered(hierarchy_camel.lvl0)',
    +        'unordered(hierarchy.lvl0)',
    +        'unordered(hierarchy_camel.lvl1)',
    +        'unordered(hierarchy.lvl1)',
    +        'unordered(hierarchy_camel.lvl2)',
    +        'unordered(hierarchy.lvl2)',
    +        'unordered(hierarchy_camel.lvl3)',
    +        'unordered(hierarchy.lvl3)',
    +        'unordered(hierarchy_camel.lvl4)',
    +        'unordered(hierarchy.lvl4)',
    +        'unordered(hierarchy_camel.lvl5)',
    +        'unordered(hierarchy.lvl5)',
    +        'unordered(hierarchy_camel.lvl6)',
    +        'unordered(hierarchy.lvl6)',
    +        'content',
    +      ],
    +      distinct: true,
    +      attributeForDistinct: 'url',
    +      customRanking: [
    +        'desc(weight.pageRank)',
    +        'desc(weight.level)',
    +        'asc(weight.position)',
    +      ],
    +      ranking: [
    +        'words',
    +        'filters',
    +        'typo',
    +        'attribute',
    +        'proximity',
    +        'exact',
    +        'custom',
    +      ],
    +      highlightPreTag: '<span class="algolia-docsearch-suggestion--highlight">',
    +      highlightPostTag: '</span>',
    +      minWordSizefor1Typo: 3,
    +      minWordSizefor2Typos: 7,
    +      allowTyposOnNumericTokens: false,
    +      minProximity: 1,
    +      ignorePlurals: true,
    +      advancedSyntax: true,
    +      attributeCriteriaComputedByMinProximity: true,
    +      removeWordsIfNoResults: 'allOptional',
    +    },
    +  },
    +})
    +

































     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     







     
































































    上述 recordProps 是用于默认主题的配置,你可以根据你使用的主题来修改它们。

    注意 initialIndexSettings.YOUR_INDEX_NAME.attributesForFaceting 字段必须包含 'lang',否则该插件将无法正常工作。

    `,1),B={class:"custom-container tip"},f=s("p",{class:"custom-container-title"},"提示",-1),F={href:"https://crawler.algolia.com/admin/crawlers/",target:"_blank",rel:"noopener noreferrer"},x=s("h2",{id:"配置项",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#配置项"},[s("span",null,"配置项")])],-1),k=s("h3",{id:"apikey",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#apikey"},[s("span",null,"apiKey")])],-1),S=s("li",null,[s("p",null,[n("类型: "),s("code",null,"string")])],-1),P=s("li",null,[s("p",null,[n("是否必需: "),s("code",null,"true")])],-1),I=s("li",null,[s("p",null,"详情:"),s("p",null,[n("从 DocSearch 团队收到的 "),s("code",null,"apiKey"),n(" ,或者由你自己生成。")])],-1),N=s("p",null,"参考:",-1),R={href:"https://docsearch.algolia.com/docs/api#apikey",target:"_blank",rel:"noopener noreferrer"},w=s("h3",{id:"indexname",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#indexname"},[s("span",null,"indexName")])],-1),O=s("li",null,[s("p",null,[n("类型: "),s("code",null,"string")])],-1),T=s("li",null,[s("p",null,[n("是否必需: "),s("code",null,"true")])],-1),U=s("li",null,[s("p",null,"详情:"),s("p",null,[n("从 DocSearch 团队收到的 "),s("code",null,"indexName"),n(" ,或者由你自己生成。")])],-1),z=s("p",null,"参考:",-1),L={href:"https://docsearch.algolia.com/docs/api#indexname",target:"_blank",rel:"noopener noreferrer"},Y=s("h3",{id:"appid",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#appid"},[s("span",null,"appId")])],-1),K=s("li",null,[s("p",null,[n("类型: "),s("code",null,"string")])],-1),j=s("li",null,[s("p",null,[n("是否必需: "),s("code",null,"true")])],-1),q=s("li",null,[s("p",null,"详情:"),s("p",null,"用于设置你的 Application ID。")],-1),M=s("p",null,"参考:",-1),V={href:"https://docsearch.algolia.com/docs/api#appid",target:"_blank",rel:"noopener noreferrer"},W=s("h3",{id:"searchparameters",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#searchparameters"},[s("span",null,"searchParameters")])],-1),X=s("li",null,[s("p",null,[n("类型: "),s("code",null,"SearchParameters")])],-1),H=s("li",null,[s("p",null,"详情:"),s("p",null,"Algolia 搜索 API 参数。")],-1),Q=s("p",null,"参考:",-1),J={href:"https://docsearch.algolia.com/docs/api/#searchparameters",target:"_blank",rel:"noopener noreferrer"},$={href:"https://www.algolia.com/doc/api-reference/search-api-parameters/",target:"_blank",rel:"noopener noreferrer"},G=s("h3",{id:"placeholder",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#placeholder"},[s("span",null,"placeholder")])],-1),Z=s("li",null,[s("p",null,[n("类型: "),s("code",null,"string")])],-1),ss=s("li",null,[s("p",null,[n("默认值: "),s("code",null,"'Search docs'")])],-1),ns=s("li",null,[s("p",null,"详情:"),s("p",null,"搜索输入框的 placeholder 属性。")],-1),ls=s("p",null,"参考:",-1),as={href:"https://docsearch.algolia.com/docs/api#placeholder",target:"_blank",rel:"noopener noreferrer"},es=s("h3",{id:"disableuserpersonalization",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#disableuserpersonalization"},[s("span",null,"disableUserPersonalization")])],-1),ps=s("li",null,[s("p",null,[n("类型: "),s("code",null,"boolean")])],-1),os=s("li",null,[s("p",null,[n("默认值: "),s("code",null,"false")])],-1),cs=s("li",null,[s("p",null,"详情:"),s("p",null,"是否禁用所有的个性化功能:最近的搜索、收藏的搜索结果等。")],-1),rs=s("p",null,"参考:",-1),is={href:"https://docsearch.algolia.com/docs/api#disableuserpersonalization",target:"_blank",rel:"noopener noreferrer"},ts=s("h3",{id:"initialquery",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#initialquery"},[s("span",null,"initialQuery")])],-1),Ds=s("li",null,[s("p",null,[n("类型: "),s("code",null,"string")])],-1),ds=s("li",null,[s("p",null,"详情:"),s("p",null,"打开弹窗时的初始请求。")],-1),ys=s("p",null,"参考:",-1),us={href:"https://docsearch.algolia.com/docs/api#initialquery",target:"_blank",rel:"noopener noreferrer"},hs=s("h3",{id:"translations",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#translations"},[s("span",null,"translations")])],-1),vs=s("li",null,[s("p",null,[n("类型: "),s("code",null,"Partial")])],-1),Cs=s("li",null,[s("p",null,"详情:"),s("p",null,"允许替换 DocSearch 按钮和弹窗内的默认文字。")],-1),bs=s("p",null,"参考:",-1),ms={href:"https://docsearch.algolia.com/docs/api/#translations",target:"_blank",rel:"noopener noreferrer"},Es=e(`

    locales

    • 类型: Record<string, DocsearchPluginOptions>

    • 详情:

      在不同 locales 下对该插件进行不同的配置。

      该插件的所有其他选项都可以在 locale 中进行配置。

    • 示例:

    export default {
    +  plugins: [
    +    docsearchPlugin({
    +      appId: '<APP_ID>',
    +      apiKey: '<API_KEY>',
    +      indexName: '<INDEX_NAME>',
    +      locales: {
    +        '/': {
    +          placeholder: 'Search Documentation',
    +          translations: {
    +            button: {
    +              buttonText: 'Search Documentation',
    +            },
    +          },
    +        },
    +        '/zh/': {
    +          placeholder: '搜索文档',
    +          translations: {
    +            button: {
    +              buttonText: '搜索文档',
    +            },
    +          },
    +        },
    +      },
    +    }),
    +  ],
    +}
    +
    `,3),_s=s("h3",{id:"indexbase",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#indexbase"},[s("span",null,"indexBase")])],-1),gs=s("li",null,[s("p",null,[n("类型: "),s("code",null,"string")])],-1),As=s("p",null,"详情:",-1),Bs=s("p",null,"搜索索引基础路径。",-1),fs=s("p",null,[n("如果你需要把你的站点部署到不同的域名上,你不需要把它们全都提交到 Docsearch 上来分别生成搜索索引。你可以选择其中一个域名作为 "),s("em",null,"索引域名"),n(" ,并且仅将 "),s("em",null,"索引域名"),n(" 提交到 DocSearch 上来爬去搜索索引。然后,你就可以在不同的部署域名下复用索引。")],-1),Fs=s("em",null,"索引域名",-1),xs=e('

    injectStyles

    • 类型: boolean

    • 默认值: true

    • 详情:

      是否注入 DocSearch 的默认样式。

      如果你认为 DocSearch 的默认样式和你的站点不兼容,你可以尝试覆盖默认样式,或者将该选项设置为 false 来完全移除默认样式。

      当该选项被禁用时,你需要为 DocSearch 引入你自己的样式。同时要注意,你也无法再使用 样式 章节中提到的样式自定义能力。

    样式

    ',3),ks={href:"https://docsearch.algolia.com/docs/styling",target:"_blank",rel:"noopener noreferrer"},Ss=e(`
    :root {
    +  --docsearch-primary-color: rgb(84, 104, 255);
    +  --docsearch-text-color: rgb(28, 30, 33);
    +  --docsearch-spacing: 12px;
    +  --docsearch-icon-stroke-width: 1.4;
    +  --docsearch-highlight-color: var(--docsearch-primary-color);
    +  --docsearch-muted-color: rgb(150, 159, 175);
    +  --docsearch-container-background: rgba(101, 108, 133, 0.8);
    +  --docsearch-logo-color: rgba(84, 104, 255);
    +
    +  /* modal */
    +  --docsearch-modal-width: 560px;
    +  --docsearch-modal-height: 600px;
    +  --docsearch-modal-background: rgb(245, 246, 247);
    +  --docsearch-modal-shadow: inset 1px 1px 0 0 rgba(255, 255, 255, 0.5), 0 3px
    +      8px 0 rgba(85, 90, 100, 1);
    +
    +  /* searchbox */
    +  --docsearch-searchbox-height: 56px;
    +  --docsearch-searchbox-background: rgb(235, 237, 240);
    +  --docsearch-searchbox-focus-background: #fff;
    +  --docsearch-searchbox-shadow: inset 0 0 0 2px var(--docsearch-primary-color);
    +
    +  /* hit */
    +  --docsearch-hit-height: 56px;
    +  --docsearch-hit-color: rgb(68, 73, 80);
    +  --docsearch-hit-active-color: #fff;
    +  --docsearch-hit-background: #fff;
    +  --docsearch-hit-shadow: 0 1px 3px 0 rgb(212, 217, 225);
    +
    +  /* key */
    +  --docsearch-key-gradient: linear-gradient(
    +    -225deg,
    +    rgb(213, 219, 228) 0%,
    +    rgb(248, 248, 248) 100%
    +  );
    +  --docsearch-key-shadow: inset 0 -2px 0 0 rgb(205, 205, 230), inset 0 0 1px 1px
    +      #fff, 0 1px 2px 1px rgba(30, 35, 90, 0.4);
    +
    +  /* footer */
    +  --docsearch-footer-height: 44px;
    +  --docsearch-footer-background: #fff;
    +  --docsearch-footer-shadow: 0 -1px 0 0 rgb(224, 227, 232), 0 -3px 6px 0 rgba(69, 98, 155, 0.12);
    +}
    +

    组件

    Docsearch

    • 详情:

      该插件会全局注册一个 <Docsearch /> 组件,你可以不传入任何 Props 来使用它。

      将该组件放置在你想要显示 docsearch 按钮的地方。例如,默认主题将这个组件放在了导航栏的末尾。

    提示

    该组件主要用于主题开发。在大多数情况下你不需要直接使用该组件。

    `,5);function Ps(Is,Ns){const r=c("NpmBadge"),a=c("ExternalLinkIcon"),p=c("RouteLink");return t(),D("div",null,[y,l(r,{package:"@vuepress/plugin-docsearch"}),s("p",null,[n("将 "),s("a",u,[n("Algolia DocSearch"),l(a)]),n(" 集成到 VuePress 中,为你的文档网站提供搜索功能。")]),h,s("p",null,[n("你需要 "),s("a",v,[n("提交你的网站 URL"),l(a)]),n(" 来加入 DocSearch 项目。当你的索引成功创建后, DocSearch 团队会将 "),C,n(" 和 "),b,n(" 发送到你的邮箱。接下来,你就可以配置该插件,在 VuePress 中启用 DocSearch 了。")]),s("p",null,[n("或者,你也可以 "),s("a",m,[n("运行你自己的爬虫"),l(a)]),n(" 来创建索引,然后使用你自己的 "),E,n(", "),_,n(" 和 "),g,n(" 来配置该插件。")]),A,s("div",B,[f,s("p",null,[n("如果你使用的不是默认主题,或者在使用 Docsearch 的时候遇到了任何问题,你也可以检查上述的爬虫配置示例,然后前往 "),s("a",F,[n("Algolia Crawler"),l(a)]),n(" 仓库,在你项目侧边栏中的 Editor 页面中修改你的配置。")])]),x,k,s("ul",null,[S,P,I,s("li",null,[N,s("ul",null,[s("li",null,[s("a",R,[n("DocSearch > Options > apiKey"),l(a)])])])])]),w,s("ul",null,[O,T,U,s("li",null,[z,s("ul",null,[s("li",null,[s("a",L,[n("DocSearch > Options > indexName"),l(a)])])])])]),Y,s("ul",null,[K,j,q,s("li",null,[M,s("ul",null,[s("li",null,[s("a",V,[n("DocSearch > Options > appId"),l(a)])])])])]),W,s("ul",null,[X,H,s("li",null,[Q,s("ul",null,[s("li",null,[s("a",J,[n("DocSearch > Options > searchParameters"),l(a)])]),s("li",null,[s("a",$,[n("Algolia > Search API Parameters"),l(a)])])])])]),G,s("ul",null,[Z,ss,ns,s("li",null,[ls,s("ul",null,[s("li",null,[s("a",as,[n("DocSearch > Options > placeholder"),l(a)])])])])]),es,s("ul",null,[ps,os,cs,s("li",null,[rs,s("ul",null,[s("li",null,[s("a",is,[n("DocSearch > Options > disableUserPersonalization"),l(a)])])])])]),ts,s("ul",null,[Ds,ds,s("li",null,[ys,s("ul",null,[s("li",null,[s("a",us,[n("DocSearch > Options > initialQuery"),l(a)])])])])]),hs,s("ul",null,[vs,Cs,s("li",null,[bs,s("ul",null,[s("li",null,[s("a",ms,[n("DocSearch > Options > translations"),l(a)])])])])]),Es,s("ul",null,[s("li",null,[n("参考: "),s("ul",null,[s("li",null,[l(p,{to:"/guide/i18n.html"},{default:o(()=>[n("指南 > 多语言支持")]),_:1})])])])]),_s,s("ul",null,[gs,s("li",null,[s("p",null,[n("默认值: "),l(p,{to:"/zh/config.html#base"},{default:o(()=>[n("base")]),_:1})])]),s("li",null,[As,Bs,fs,s("p",null,[n("如果你不同部署域名下的 "),l(p,{to:"/zh/config.html#base"},{default:o(()=>[n("base")]),_:1}),n(" 是不一样的,你就需要将这个配置设置成 "),Fs,n(" 的 "),l(p,{to:"/zh/config.html#base"},{default:o(()=>[n("base")]),_:1}),n(" ,这样其他的部署域名就可以正确复用索引了。")])])]),xs,s("p",null,[n("你可以通过 "),s("a",ks,[n("@docsearch/css"),l(a)]),n(" 提供的 CSS 变量来自定义样式:")]),Ss])}const ws=i(d,[["render",Ps],["__file","docsearch.html.vue"]]),Os=JSON.parse('{"path":"/zh/plugins/docsearch.html","title":"docsearch","lang":"zh-CN","frontmatter":{},"headers":[{"level":2,"title":"使用方法","slug":"使用方法","link":"#使用方法","children":[]},{"level":2,"title":"获取搜索索引","slug":"获取搜索索引","link":"#获取搜索索引","children":[]},{"level":2,"title":"配置项","slug":"配置项","link":"#配置项","children":[{"level":3,"title":"apiKey","slug":"apikey","link":"#apikey","children":[]},{"level":3,"title":"indexName","slug":"indexname","link":"#indexname","children":[]},{"level":3,"title":"appId","slug":"appid","link":"#appid","children":[]},{"level":3,"title":"searchParameters","slug":"searchparameters","link":"#searchparameters","children":[]},{"level":3,"title":"placeholder","slug":"placeholder","link":"#placeholder","children":[]},{"level":3,"title":"disableUserPersonalization","slug":"disableuserpersonalization","link":"#disableuserpersonalization","children":[]},{"level":3,"title":"initialQuery","slug":"initialquery","link":"#initialquery","children":[]},{"level":3,"title":"translations","slug":"translations","link":"#translations","children":[]},{"level":3,"title":"locales","slug":"locales","link":"#locales","children":[]},{"level":3,"title":"indexBase","slug":"indexbase","link":"#indexbase","children":[]},{"level":3,"title":"injectStyles","slug":"injectstyles","link":"#injectstyles","children":[]}]},{"level":2,"title":"样式","slug":"样式","link":"#样式","children":[]},{"level":2,"title":"组件","slug":"组件","link":"#组件","children":[{"level":3,"title":"Docsearch","slug":"docsearch-1","link":"#docsearch-1","children":[]}]}],"git":{"updatedTime":1706627619000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":2}]},"filePathRelative":"zh/plugins/docsearch.md"}');export{ws as comp,Os as data}; diff --git a/assets/extending-a-theme-01-Cqco1E4_.js b/assets/extending-a-theme-01-Cqco1E4_.js new file mode 100644 index 0000000000..0372478e41 --- /dev/null +++ b/assets/extending-a-theme-01-Cqco1E4_.js @@ -0,0 +1 @@ +const e="/ecosystem/images/cookbook/extending-a-theme-01.png";export{e as _}; diff --git a/assets/extending.html-B-2YhQDL.js b/assets/extending.html-B-2YhQDL.js new file mode 100644 index 0000000000..f7ff5555ec --- /dev/null +++ b/assets/extending.html-B-2YhQDL.js @@ -0,0 +1,66 @@ +import{_ as o}from"./extending-a-theme-01-Cqco1E4_.js";import{_ as p,r as t,o as c,c as i,b as n,d as s,a,e as l}from"./app-DI49vYHA.js";const r={},d=l(`

    Extending

    VuePress default theme is widely used by users, so it is designed to be extendable, allowing users to make their own customization with ease.

    Layout Slots

    Default theme's Layout provides some slots:

    • navbar
    • navbar-before
    • navbar-after
    • sidebar
    • sidebar-top
    • sidebar-bottom
    • page
    • page-top
    • page-bottom
    • page-content-top
    • page-content-bottom

    With the help of them, you can add or replace content easily. Here comes an example to introduce how to extend default theme with layout slots.

    Firstly, create a client config file .vuepress/client.ts:

    import { defineClientConfig } from 'vuepress/client'
    +import Layout from './layouts/Layout.vue'
    +
    +export default defineClientConfig({
    +  layouts: {
    +    Layout,
    +  },
    +})
    +

    Next, create the .vuepress/layouts/Layout.vue, and make use of the slots that provided by the Layout of default theme:

    <script setup>
    +import ParentLayout from '@vuepress/theme-default/layouts/Layout.vue'
    +</script>
    +
    +<template>
    +  <ParentLayout>
    +    <template #page-bottom>
    +      <div class="my-footer">This is my custom page footer</div>
    +    </template>
    +  </ParentLayout>
    +</template>
    +
    +<style lang="css">
    +.my-footer {
    +  text-align: center;
    +}
    +</style>
    +

    Then the default Layout layout has been overridden by your own local layout, which will add a custom footer to every normal pages in default theme (excluding homepage):

    extending-a-theme

    Components Replacement

    The layout slots are useful, but sometimes you might find it's not flexible enough. Default theme also provides the ability to replace a single component.

    ',14),D={href:"https://v2.vuepress.vuejs.org/plugin-api.html#alias",target:"_blank",rel:"noopener noreferrer"},y={href:"https://github.com/vuepress/ecosystem/tree/main/themes/theme-default/src/client/components",target:"_blank",rel:"noopener noreferrer"},m=n("code",null,"@theme",-1),u=n("code",null,"HomeFooter.vue",-1),v=n("code",null,"@theme/HomeFooter.vue",-1),C=l(`

    Then, if you want to replace the HomeFooter.vue component, just override the alias in your config file .vuepress/config.ts:

    import { defaultTheme } from '@vuepress/theme-default'
    +import { getDirname, path } from 'vuepress/utils'
    +import { defineUserConfig } from 'vuepress'
    +
    +const __dirname = getDirname(import.meta.url)
    +
    +export default defineUserConfig({
    +  theme: defaultTheme(),
    +  alias: {
    +    '@theme/HomeFooter.vue': path.resolve(
    +      __dirname,
    +      './components/MyHomeFooter.vue',
    +    ),
    +  },
    +})
    +

    Developing a Child Theme

    Instead of extending the default theme directly in .vuepress/config.ts and .vuepress/client.ts, you can also develop your own theme extending the default theme:

    import { defaultTheme, type DefaultThemeOptions } from '@vuepress/theme-default'
    +import type { Theme } from 'vuepress/core'
    +import { getDirname, path } from 'vuepress/utils'
    +
    +const __dirname = getDirname(import.meta.url)
    +
    +export const childTheme = (options: DefaultThemeOptions): Theme => {
    +  return {
    +    name: 'vuepress-theme-child',
    +    extends: defaultTheme(options),
    +
    +    // override layouts in child theme's client config file
    +    // notice that you would build ts to js before publishing to npm,
    +    // so this should be the path to the js file
    +    clientConfigFile: path.resolve(__dirname, './client.js'),
    +
    +    // override component alias
    +    alias: {
    +      '@theme/HomeFooter.vue': path.resolve(
    +        __dirname,
    +        './components/MyHomeFooter.vue',
    +      ),
    +    },
    +  }
    +}
    +
    `,5);function h(b,g){const e=t("ExternalLinkIcon");return c(),i("div",null,[d,n("p",null,[s("Default theme has registered "),n("a",D,[s("alias"),a(e)]),s(" for every "),n("a",y,[s("non-global components"),a(e)]),s(" with a "),m,s(" prefix. For example, the alias of "),u,s(" is "),v,s(".")]),C])}const F=p(r,[["render",h],["__file","extending.html.vue"]]),_=JSON.parse('{"path":"/themes/default/extending.html","title":"Extending","lang":"en-US","frontmatter":{},"headers":[{"level":2,"title":"Layout Slots","slug":"layout-slots","link":"#layout-slots","children":[]},{"level":2,"title":"Components Replacement","slug":"components-replacement","link":"#components-replacement","children":[]},{"level":2,"title":"Developing a Child Theme","slug":"developing-a-child-theme","link":"#developing-a-child-theme","children":[]}],"git":{"updatedTime":1706625181000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":2}]},"filePathRelative":"themes/default/extending.md"}');export{F as comp,_ as data}; diff --git a/assets/extending.html-C_vYwTyq.js b/assets/extending.html-C_vYwTyq.js new file mode 100644 index 0000000000..6258b216d1 --- /dev/null +++ b/assets/extending.html-C_vYwTyq.js @@ -0,0 +1,65 @@ +import{_ as p}from"./extending-a-theme-01-Cqco1E4_.js";import{_ as o,r as t,o as c,c as r,b as n,d as s,a as l,e}from"./app-DI49vYHA.js";const i={},D=e(`

    继承

    VuePress 默认主题有着大量的用户,因此我们对它进行了一些便于继承的设计,以便用户轻松进行定制化。

    布局插槽

    默认主题的 Layout 布局提供了一些插槽:

    • navbar
    • navbar-before
    • navbar-after
    • sidebar
    • sidebar-top
    • sidebar-bottom
    • page
    • page-top
    • page-bottom
    • page-content-top
    • page-content-bottom

    在它们的帮助下,你可以很容易地添加或替换内容。下面通过一个示例来介绍一下如何使用布局插槽来继承默认主题。

    首先,创建一个客户端配置文件 .vuepress/client.ts

    import { defineClientConfig } from 'vuepress/client'
    +import Layout from './layouts/Layout.vue'
    +
    +export default defineClientConfig({
    +  layouts: {
    +    Layout,
    +  },
    +})
    +

    接下来,创建 .vuepress/layouts/Layout.vue ,并使用由默认主题的 Layout 布局提供的插槽:

    <script setup>
    +import ParentLayout from '@vuepress/theme-default/layouts/Layout.vue'
    +</script>
    +
    +<template>
    +  <ParentLayout>
    +    <template #page-bottom>
    +      <div class="my-footer">This is my custom page footer</div>
    +    </template>
    +  </ParentLayout>
    +</template>
    +
    +<style lang="css">
    +.my-footer {
    +  text-align: center;
    +}
    +</style>
    +

    此时默认的 Layout 布局已经被你的本地布局覆盖,将会在除了首页外的所有页面添加一个自定义的页脚:

    extending-a-theme

    组件替换

    布局插槽十分实用,但有时候你可能会觉得它不够灵活。默认主题同样提供了替换单个组件的能力。

    ',14),d={href:"https://github.com/vuepress/ecosystem/tree/main/themes/theme-default/src/client/components",target:"_blank",rel:"noopener noreferrer"},y=n("code",null,"@theme",-1),C={href:"https://v2.vuepress.vuejs.org/zh/reference/plugin-api.html#alias",target:"_blank",rel:"noopener noreferrer"},v=n("code",null,"HomeFooter.vue",-1),m=n("code",null,"@theme/HomeFooter.vue",-1),u=e(`

    接下来,如果你想要替换 HomeFooter.vue 组件,只需要在配置文件 .vuepress/config.ts 中覆盖这个别名即可:

    import { defaultTheme } from '@vuepress/theme-default'
    +import { getDirname, path } from 'vuepress/utils'
    +import { defineUserConfig } from 'vuepress'
    +
    +const __dirname = getDirname(import.meta.url)
    +
    +export default defineUserConfig({
    +  theme: defaultTheme(),
    +  alias: {
    +    '@theme/HomeFooter.vue': path.resolve(
    +      __dirname,
    +      './components/MyHomeFooter.vue',
    +    ),
    +  },
    +})
    +

    开发一个子主题

    除了在 .vuepress/config.ts.vuepress/client.ts 中直接扩展默认主题以外,你可以通过继承默认主题来开发一个你自己的主题:

    import { defaultTheme, type DefaultThemeOptions } from '@vuepress/theme-default'
    +import type { Theme } from 'vuepress/core'
    +import { getDirname, path } from 'vuepress/utils'
    +
    +const __dirname = getDirname(import.meta.url)
    +
    +export const childTheme = (options: DefaultThemeOptions): Theme => {
    +  return {
    +    name: 'vuepress-theme-child',
    +    extends: defaultTheme(options),
    +
    +    // 在子主题的客户端配置文件中覆盖布局
    +    // 注意,你在发布到 NPM 之前会将 TS 构建为 JS ,因此这里需要设置为 JS 文件的路径
    +    clientConfigFile: path.resolve(__dirname, './client.js'),
    +
    +    // 覆盖组件别名
    +    alias: {
    +      '@theme/HomeFooter.vue': path.resolve(
    +        __dirname,
    +        './components/MyHomeFooter.vue',
    +      ),
    +    },
    +  }
    +}
    +
    `,5);function b(h,E){const a=t("ExternalLinkIcon");return c(),r("div",null,[D,n("p",null,[s("默认主题将所有 "),n("a",d,[s("非全局的组件"),l(a)]),s(" 都注册了一个带 "),y,s(" 前缀的 "),n("a",C,[s("alias"),l(a)]),s(" 。例如,"),v,s(" 的别名是 "),m,s(" 。")]),u])}const F=o(i,[["render",b],["__file","extending.html.vue"]]),_=JSON.parse('{"path":"/zh/themes/default/extending.html","title":"继承","lang":"zh-CN","frontmatter":{},"headers":[{"level":2,"title":"布局插槽","slug":"布局插槽","link":"#布局插槽","children":[]},{"level":2,"title":"组件替换","slug":"组件替换","link":"#组件替换","children":[]},{"level":2,"title":"开发一个子主题","slug":"开发一个子主题","link":"#开发一个子主题","children":[]}],"git":{"updatedTime":1706625181000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":2}]},"filePathRelative":"zh/themes/default/extending.md"}');export{F as comp,_ as data}; diff --git a/assets/external-link-icon.html-CkGGjwAB.js b/assets/external-link-icon.html-CkGGjwAB.js new file mode 100644 index 0000000000..637727428c --- /dev/null +++ b/assets/external-link-icon.html-CkGGjwAB.js @@ -0,0 +1,28 @@ +import{_ as c,r as s,o as t,c as r,a as l,b as n,d as a,w as d,e}from"./app-DI49vYHA.js";const D={},u=n("h1",{id:"external-link-icon",tabindex:"-1"},[n("a",{class:"header-anchor",href:"#external-link-icon"},[n("span",null,"external-link-icon")])],-1),v=e(`

    该插件已经集成到默认主题中。

    使用方法

    npm i -D @vuepress/plugin-external-link-icon@next
    +
    import { externalLinkIconPlugin } from '@vuepress/plugin-external-link-icon'
    +
    +export default {
    +  plugins: [
    +    externalLinkIconPlugin({
    +      // 配置项
    +    }),
    +  ],
    +}
    +

    选项

    locales

    • 类型: Record<string, { openInNewWindow: string }>

    • 详情:

      外部链接图标在不同 locales 下的 A11y 文字。

      如果没有指定该配置项,它会降级使用默认文字。

    • 示例:

    export default {
    +  plugins: [
    +    externalLinkIconPlugin({
    +      locales: {
    +        '/': {
    +          openInNewWindow: 'open in new window',
    +        },
    +        '/zh/': {
    +          openInNewWindow: '在新窗口打开',
    +        },
    +      },
    +    }),
    +  ],
    +}
    +
    `,8),h=e(`

    Frontmatter

    externalLinkIcon

    • 类型: boolean

    • 详情:

      是否在当前页面的外部链接的后面添加外部链接图标。

    样式

    你可以通过 CSS 变量来自定义外部链接图标的样式:

    :root {
    +  --external-link-icon-color: #aaa;
    +}
    +

    组件

    ExternalLinkIcon

    • 详情:

      该插件会全局注册一个 <ExternalLinkIcon /> 组件,你可以不传入任何 Props 来使用它。

    提示

    该组件主要用于主题开发。在大多数情况下你不需要直接使用该组件。

    `,10);function m(y,k){const i=s("NpmBadge"),o=s("ExternalLinkIcon"),p=s("RouteLink");return t(),r("div",null,[u,l(i,{package:"@vuepress/plugin-external-link-icon"}),n("p",null,[a("该插件会为你 Markdown 内容中的外部链接添加一个图标,即 "),l(o)]),v,n("ul",null,[n("li",null,[a("参考: "),n("ul",null,[n("li",null,[l(p,{to:"/guide/i18n.html"},{default:d(()=>[a("指南 > 多语言支持")]),_:1})])])])]),h])}const x=c(D,[["render",m],["__file","external-link-icon.html.vue"]]),C=JSON.parse('{"path":"/zh/plugins/external-link-icon.html","title":"external-link-icon","lang":"zh-CN","frontmatter":{},"headers":[{"level":2,"title":"使用方法","slug":"使用方法","link":"#使用方法","children":[]},{"level":2,"title":"选项","slug":"选项","link":"#选项","children":[{"level":3,"title":"locales","slug":"locales","link":"#locales","children":[]}]},{"level":2,"title":"Frontmatter","slug":"frontmatter","link":"#frontmatter","children":[{"level":3,"title":"externalLinkIcon","slug":"externallinkicon","link":"#externallinkicon","children":[]}]},{"level":2,"title":"样式","slug":"样式","link":"#样式","children":[]},{"level":2,"title":"组件","slug":"组件","link":"#组件","children":[{"level":3,"title":"ExternalLinkIcon","slug":"externallinkicon-1","link":"#externallinkicon-1","children":[]}]}],"git":{"updatedTime":1707223547000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":2}]},"filePathRelative":"zh/plugins/external-link-icon.md"}');export{x as comp,C as data}; diff --git a/assets/external-link-icon.html-oHMFewKb.js b/assets/external-link-icon.html-oHMFewKb.js new file mode 100644 index 0000000000..4f3eeea155 --- /dev/null +++ b/assets/external-link-icon.html-oHMFewKb.js @@ -0,0 +1,28 @@ +import{_ as p,r as s,o as c,c as r,a as e,b as n,d as l,w as d,e as a}from"./app-DI49vYHA.js";const u={},D=n("h1",{id:"external-link-icon",tabindex:"-1"},[n("a",{class:"header-anchor",href:"#external-link-icon"},[n("span",null,"external-link-icon")])],-1),h=a(`

    This plugin has been integrated into the default theme.

    Usage

    npm i -D @vuepress/plugin-external-link-icon@next
    +
    import { externalLinkIconPlugin } from '@vuepress/plugin-external-link-icon'
    +
    +export default {
    +  plugins: [
    +    externalLinkIconPlugin({
    +      // options
    +    }),
    +  ],
    +}
    +

    Options

    locales

    • Type: Record<string, { openInNewWindow: string }>

    • Details:

      The a11y text of the external link icon in different locales.

      If this option is not specified, it will fallback to default text.

    • Example:

    export default {
    +  plugins: [
    +    externalLinkIconPlugin({
    +      locales: {
    +        '/': {
    +          openInNewWindow: 'open in new window',
    +        },
    +        '/zh/': {
    +          openInNewWindow: '在新窗口打开',
    +        },
    +      },
    +    }),
    +  ],
    +}
    +
    `,8),m=a(`

    Frontmatter

    externalLinkIcon

    • Type: boolean

    • Details:

      Whether to append an external link icon to external links in current page.

    Styles

    You can customize the style of the external link icon via CSS variables:

    :root {
    +  --external-link-icon-color: #aaa;
    +}
    +

    Components

    ExternalLinkIcon

    • Details:

      This plugin will register a <ExternalLinkIcon /> component globally, and you can use it without any props.

    TIP

    This component is mainly used for theme development. You don't need to use it directly in most cases.

    `,10);function v(y,k){const i=s("NpmBadge"),o=s("ExternalLinkIcon"),t=s("RouteLink");return c(),r("div",null,[D,e(i,{package:"@vuepress/plugin-external-link-icon"}),n("p",null,[l("This plugin will add an icon to the external link in your markdown content, i.e. "),e(o)]),h,n("ul",null,[n("li",null,[l("Also see: "),n("ul",null,[n("li",null,[e(t,{to:"/guide/i18n.html"},{default:d(()=>[l("Guide > I18n")]),_:1})])])])]),m])}const b=p(u,[["render",v],["__file","external-link-icon.html.vue"]]),g=JSON.parse('{"path":"/plugins/external-link-icon.html","title":"external-link-icon","lang":"en-US","frontmatter":{},"headers":[{"level":2,"title":"Usage","slug":"usage","link":"#usage","children":[]},{"level":2,"title":"Options","slug":"options","link":"#options","children":[{"level":3,"title":"locales","slug":"locales","link":"#locales","children":[]}]},{"level":2,"title":"Frontmatter","slug":"frontmatter","link":"#frontmatter","children":[{"level":3,"title":"externalLinkIcon","slug":"externallinkicon","link":"#externallinkicon","children":[]}]},{"level":2,"title":"Styles","slug":"styles","link":"#styles","children":[]},{"level":2,"title":"Components","slug":"components","link":"#components","children":[{"level":3,"title":"ExternalLinkIcon","slug":"externallinkicon-1","link":"#externallinkicon-1","children":[]}]}],"git":{"updatedTime":1706605723000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":1}]},"filePathRelative":"plugins/external-link-icon.md"}');export{b as comp,g as data}; diff --git a/assets/frontmatter.html-BzPSllnx.js b/assets/frontmatter.html-BzPSllnx.js new file mode 100644 index 0000000000..c561f48da7 --- /dev/null +++ b/assets/frontmatter.html-BzPSllnx.js @@ -0,0 +1,51 @@ +import{_ as e,o as s,c as n,e as a}from"./app-DI49vYHA.js";const l={},i=a(`

    Frontmatter Config

    You can control each feed item generation by setting page frontmatter.

    Additions and Removals

    By default, all articles are added to the feed stream. Set feed: false in frontmatter to remove a page from feed.

    Frontmatter Information

    title

    • Type: string

    Automatically generated by VuePress, defaults to the h1 content of the page

    description

    • Type: string

    Description of the page

    date

    • Type: Date

    Date when the page was published

    article

    • Type: boolean

    Whether the page is an article

    If this is set to false, the page will not be included in the final feed.

    • Type: string

    Page copyright information

    cover / image / banner

    • Type: string

    Image used as page cover , should be full link or absolute link.

    Frontmatter Options

    feed.title

    • Type: string

    The title of the feed item

    feed.description

    • Type: string

    Description of the feed item

    feed.content

    • Type: string

    The content of the feed item

    feed.author

    • Type: FeedAuthor[] | FeedAuthor

    The author of the feed item

    FeedAuthor format
    interface FeedAuthor {
    +  /**
    +   * Author name
    +   */
    +  name?: string
    +
    +  /**
    +   * Author email
    +   */
    +  email?: string
    +
    +  /**
    +   * Author site
    +   *
    +   * @description json format only
    +   */
    +  url?: string
    +
    +  /**
    +   * Author avatar
    +   *
    +   * @description json format only
    +   */
    +  avatar?: string
    +}
    +

    feed.contributor

    • Type: FeedContributor[] | FeedContributor

    Contributors to feed item

    FeedContributor format
    interface FeedContributor {
    +  /**
    +   * Author name
    +   */
    +  name?: string
    +
    +  /**
    +   * Author email
    +   */
    +  email?: string
    +
    +  /**
    +   * Author site
    +   *
    +   * @description json format only
    +   */
    +  url?: string
    +
    +  /**
    +   * Author avatar
    +   *
    +   * @description json format only
    +   */
    +  avatar?: string
    +}
    +

    feed.guid

    • Type: string

    The identifier of feed item, used to identify the feed item.

    You should ensure every feed has a unique guid.

    `,46),t=[i];function o(r,p){return s(),n("div",null,t)}const d=e(l,[["render",o],["__file","frontmatter.html.vue"]]),u=JSON.parse('{"path":"/plugins/feed/frontmatter.html","title":"Frontmatter Config","lang":"en-US","frontmatter":{},"headers":[{"level":2,"title":"Additions and Removals","slug":"additions-and-removals","link":"#additions-and-removals","children":[]},{"level":2,"title":"Frontmatter Information","slug":"frontmatter-information","link":"#frontmatter-information","children":[{"level":3,"title":"title","slug":"title","link":"#title","children":[]},{"level":3,"title":"description","slug":"description","link":"#description","children":[]},{"level":3,"title":"date","slug":"date","link":"#date","children":[]},{"level":3,"title":"article","slug":"article","link":"#article","children":[]},{"level":3,"title":"copyright","slug":"copyright","link":"#copyright","children":[]},{"level":3,"title":"cover / image / banner","slug":"cover-image-banner","link":"#cover-image-banner","children":[]}]},{"level":2,"title":"Frontmatter Options","slug":"frontmatter-options","link":"#frontmatter-options","children":[{"level":3,"title":"feed.title","slug":"feed-title","link":"#feed-title","children":[]},{"level":3,"title":"feed.description","slug":"feed-description","link":"#feed-description","children":[]},{"level":3,"title":"feed.content","slug":"feed-content","link":"#feed-content","children":[]},{"level":3,"title":"feed.author","slug":"feed-author","link":"#feed-author","children":[]},{"level":3,"title":"feed.contributor","slug":"feed-contributor","link":"#feed-contributor","children":[]},{"level":3,"title":"feed.guid","slug":"feed-guid","link":"#feed-guid","children":[]}]}],"git":{"updatedTime":1706762763000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":2}]},"filePathRelative":"plugins/feed/frontmatter.md"}');export{d as comp,u as data}; diff --git a/assets/frontmatter.html-CS29ziHh.js b/assets/frontmatter.html-CS29ziHh.js new file mode 100644 index 0000000000..ea61d0a2d2 --- /dev/null +++ b/assets/frontmatter.html-CS29ziHh.js @@ -0,0 +1,60 @@ +import{_ as r,r as o,o as c,c as d,a as s,b as e,d as l,w as a,e as t}from"./app-DI49vYHA.js";const u={},h=e("h1",{id:"frontmatter",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#frontmatter"},[e("span",null,"Frontmatter")])],-1),m=e("h2",{id:"all-pages",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#all-pages"},[e("span",null,"All Pages")])],-1),D=e("p",null,"Frontmatter in this section will take effect in all types of pages.",-1),g=e("h3",{id:"externallinkicon",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#externallinkicon"},[e("span",null,"externalLinkIcon")])],-1),v=e("li",null,[e("p",null,[l("Type: "),e("code",null,"boolean")])],-1),f=e("p",null,"Details:",-1),b=e("p",null,"Also see:",-1),y=e("h3",{id:"navbar",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#navbar"},[e("span",null,"navbar")])],-1),_=e("li",null,[e("p",null,[l("Type: "),e("code",null,"boolean")])],-1),k=e("li",null,[e("p",null,"Details:"),e("p",null,"Show navbar on this page or not."),e("p",null,"If you disable navbar in theme config, this frontmatter will not take effect.")],-1),x=e("p",null,"Also see:",-1),C=t(`

    pageClass

    • Type: string

    • Details:

      Add extra class name to this page.

    • Example:

    ---
    +pageClass: custom-page-class
    +---
    +

    Then you can customize styles of this page in .vuepress/styles/index.scss file:

    .theme-container.custom-page-class {
    +  /* page styles */
    +}
    +
    `,5),E=t(`

    Home Page

    Frontmatter in this section will only take effect in home pages.

    home

    • Type: boolean

    • Details:

      Specify whether the page is homepage or a normal page.

      If you don't set this frontmatter or set it to false, the page would be a normal page.

    • Example:

    ---
    +home: true
    +---
    +

    heroImage

    • Type: string

    • Details:

      Specify the url of the hero image.

    • Example:

    ---
    +# public file path
    +heroImage: /images/hero.png
    +# url
    +heroImage: https://vuejs.org/images/logo.png
    +---
    +
    `,8),T={href:"https://v2.vuepress.vuejs.org/guide/assets.html#public-files",target:"_blank",rel:"noopener noreferrer"},A=e("h3",{id:"heroimagedark",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#heroimagedark"},[e("span",null,"heroImageDark")])],-1),w=e("li",null,[e("p",null,[l("Type: "),e("code",null,"string")])],-1),S=e("li",null,[e("p",null,"Details:"),e("p",null,"Specify the url of hero image to be used in dark mode."),e("p",null,"You can make use of this option if you want to use different heroImage config in dark mode.")],-1),L=e("p",null,"Also see:",-1),I=e("li",null,[e("a",{href:"#heroimage"},"Default Theme > Frontmatter > heroImage")],-1),N=t('

    heroAlt

    • Type: string

    • Details:

      Specify the alt attribute of the hero image.

      This will fallback to the heroText.

    heroHeight

    ',3),P=e("li",null,[e("p",null,[l("Type: "),e("code",null,"number")])],-1),F=e("li",null,[e("p",null,[l("Default: "),e("code",null,"280")])],-1),H=e("p",null,"Details:",-1),j=e("p",null,[l("Specify the "),e("code",null,"height"),l(" attribute of the hero "),e("code",null,""),l(" tag.")],-1),B=e("p",null,"You may need to reduce this value if the height of your hero image is less than the default value.",-1),V={href:"https://web.dev/cls/",target:"_blank",rel:"noopener noreferrer"},M=e("h3",{id:"herotext",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#herotext"},[e("span",null,"heroText")])],-1),G=e("li",null,[e("p",null,[l("Type: "),e("code",null,"string | null")])],-1),R=e("p",null,"Details:",-1),U=e("p",null,"Specify the the hero text.",-1),O={href:"https://v2.vuepress.vuejs.org/reference/config.html#title",target:"_blank",rel:"noopener noreferrer"},Y=e("p",null,[l("Set to "),e("code",null,"null"),l(" to disable hero text.")],-1),z=e("h3",{id:"tagline",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#tagline"},[e("span",null,"tagline")])],-1),J=e("li",null,[e("p",null,[l("Type: "),e("code",null,"string | null")])],-1),q=e("p",null,"Details:",-1),K=e("p",null,"Specify the the tagline.",-1),Q={href:"https://v2.vuepress.vuejs.org/reference/config.html#description",target:"_blank",rel:"noopener noreferrer"},W=e("p",null,[l("Set to "),e("code",null,"null"),l(" to disable tagline.")],-1),X=t(`

    actions

    • Type:
    Array<{
    +  text: string
    +  link: string
    +  type?: 'primary' | 'secondary'
    +}>
    +
    • Details:

      Configuration of the action buttons.

    • Example:

    ---
    +actions:
    +  - text: Get Started
    +    link: /guide/getting-started.html
    +    type: primary
    +  - text: Introduction
    +    link: /guide/introduction.html
    +    type: secondary
    +---
    +

    features

    • Type:
    Array<{
    +  title: string
    +  details: string
    +}>
    +
    • Details:

      Configuration of the features list.

    • Example:

    ---
    +features:
    +  - title: Simplicity First
    +    details: Minimal setup with markdown-centered project structure helps you focus on writing.
    +  - title: Vue-Powered
    +    details: Enjoy the dev experience of Vue, use Vue components in markdown, and develop custom themes with Vue.
    +  - title: Performant
    +    details: VuePress generates pre-rendered static HTML for each page, and runs as an SPA once a page is loaded.
    +---
    +
    • Type: string

    • Details:

      Specify the content of the footer.

    footerHtml

    • Type: boolean

    • Details:

      Allow HTML in footer or not.

      If you set it to true, the footer will be treated as HTML code.

    Normal Page

    Frontmatter in this section will only take effect in normal pages.

    `,17),Z=e("li",null,[e("p",null,[l("Type: "),e("code",null,"boolean")])],-1),$=e("li",null,[e("p",null,"Details:"),e("p",null,[l("Enable the "),e("em",null,"edit this page"),l(" link in this page or not.")])],-1),ee=e("p",null,"Also see:",-1),le=e("h3",{id:"editlinkpattern",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#editlinkpattern"},[e("span",null,"editLinkPattern")])],-1),se=e("li",null,[e("p",null,[l("Type: "),e("code",null,"string")])],-1),ne=e("li",null,[e("p",null,"Details:"),e("p",null,[l("Specify the pattern of the "),e("em",null,"edit this page"),l(" link of this page.")])],-1),ae=e("p",null,"Also see:",-1),te=e("h3",{id:"lastupdated",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#lastupdated"},[e("span",null,"lastUpdated")])],-1),ie=e("li",null,[e("p",null,[l("Type: "),e("code",null,"boolean")])],-1),oe=e("li",null,[e("p",null,"Details:"),e("p",null,[l("Enable the "),e("em",null,"last updated timestamp"),l(" in this page or not.")])],-1),pe=e("p",null,"Also see:",-1),re=e("h3",{id:"contributors",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#contributors"},[e("span",null,"contributors")])],-1),ce=e("li",null,[e("p",null,[l("Type: "),e("code",null,"boolean")])],-1),de=e("li",null,[e("p",null,"Details:"),e("p",null,[l("Enable the "),e("em",null,"contributors list"),l(" in this page or not.")])],-1),ue=e("p",null,"Also see:",-1),he=e("h3",{id:"sidebar",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#sidebar"},[e("span",null,"sidebar")])],-1),me=e("li",null,[e("p",null,[l("Type: "),e("code",null,"false | 'auto' | SidebarConfigArray | SidebarConfigObject")])],-1),De=e("li",null,[e("p",null,"Details:"),e("p",null,"Configure the sidebar of this page.")],-1),ge=e("p",null,"Also see:",-1),ve=e("h3",{id:"sidebardepth",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#sidebardepth"},[e("span",null,"sidebarDepth")])],-1),fe=e("li",null,[e("p",null,[l("Type: "),e("code",null,"number")])],-1),be=e("li",null,[e("p",null,"Details:"),e("p",null,"Configure the sidebar depth of this page.")],-1),ye=e("p",null,"Also see:",-1),_e=t(`

    prev

    • Type: NavLink | string

    • Details:

      Specify the link of the previous page.

      If you don't set this frontmatter, the link will be inferred from the sidebar config.

      To configure the prev link manually, you can set this frontmatter to a NavLink object or a string:

      • A NavLink object should have a text field and a link field.
      • A string should be the path to the target page file. It will be converted to a NavLink object, whose text is the page title, and link is the page route path.
    • Example:

    ---
    +# NavLink
    +prev:
    +  text: Get Started
    +  link: /guide/getting-started.html
    +
    +# NavLink - external url
    +prev:
    +  text: GitHub
    +  link: https://github.com
    +
    +# string - page file path
    +prev: /guide/getting-started.md
    +
    +# string - page file relative path
    +prev: ../../guide/getting-started.md
    +---
    +

    next

    • Type: NavLink | string

    • Details:

      Specify the link of the next page.

      If you don't set this frontmatter, the link will be inferred from the sidebar config.

      The type is the same as prev frontmatter.

    `,5);function ke(xe,Ce){const p=o("NpmBadge"),n=o("RouteLink"),i=o("ExternalLinkIcon");return c(),d("div",null,[h,s(p,{package:"@vuepress/theme-default"}),m,D,g,e("ul",null,[v,e("li",null,[f,e("p",null,[l("Provided by "),s(n,{to:"/plugins/external-link-icon.html#externallinkicon"},{default:a(()=>[l("@vuepress/plugin-external-link-icon")]),_:1}),l(".")])]),e("li",null,[b,e("ul",null,[e("li",null,[s(n,{to:"/themes/default/config.html#themeplugins-externallinkicon"},{default:a(()=>[l("Default Theme > Config Reference > themePlugins.externalLinkIcon")]),_:1})])])])]),y,e("ul",null,[_,k,e("li",null,[x,e("ul",null,[e("li",null,[s(n,{to:"/themes/default/config.html#navbar"},{default:a(()=>[l("Default Theme > Config > navbar")]),_:1})])])])]),C,e("ul",null,[e("li",null,[l("Also see: "),e("ul",null,[e("li",null,[s(n,{to:"/themes/default/styles.html#style-file"},{default:a(()=>[l("Default Theme > Styles > Style File")]),_:1})])])])]),E,e("ul",null,[e("li",null,[l("Also see: "),e("ul",null,[e("li",null,[e("a",T,[l("Guide > Assets > Public Files"),s(i)])])])])]),A,e("ul",null,[w,S,e("li",null,[L,e("ul",null,[I,e("li",null,[s(n,{to:"/themes/default/config.html#colormode"},{default:a(()=>[l("Default Theme > Config > colorMode")]),_:1})])])])]),N,e("ul",null,[P,F,e("li",null,[H,j,B,e("p",null,[l("Notice that the height is also constrained by CSS. This attribute is to reduce "),e("a",V,[l("Cumulative Layout Shift (CLS)"),s(i)]),l(" that caused by the loading of the hero image.")])])]),M,e("ul",null,[G,e("li",null,[R,U,e("p",null,[l("This will fallback to the site "),e("a",O,[l("title"),s(i)]),l(".")]),Y])]),z,e("ul",null,[J,e("li",null,[q,K,e("p",null,[l("This will fallback to the site "),e("a",Q,[l("description"),s(i)]),l(".")]),W])]),X,e("ul",null,[Z,$,e("li",null,[ee,e("ul",null,[e("li",null,[s(n,{to:"/themes/default/config.html#editlink"},{default:a(()=>[l("Default Theme > Config > editLink")]),_:1})])])])]),le,e("ul",null,[se,ne,e("li",null,[ae,e("ul",null,[e("li",null,[s(n,{to:"/themes/default/config.html#editlinkpattern"},{default:a(()=>[l("Default Theme > Config > editLinkPattern")]),_:1})])])])]),te,e("ul",null,[ie,oe,e("li",null,[pe,e("ul",null,[e("li",null,[s(n,{to:"/themes/default/config.html#lastupdated"},{default:a(()=>[l("Default Theme > Config > lastUpdated")]),_:1})])])])]),re,e("ul",null,[ce,de,e("li",null,[ue,e("ul",null,[e("li",null,[s(n,{to:"/themes/default/config.html#contributors"},{default:a(()=>[l("Default Theme > Config > contributors")]),_:1})])])])]),he,e("ul",null,[me,De,e("li",null,[ge,e("ul",null,[e("li",null,[s(n,{to:"/themes/default/config.html#sidebar"},{default:a(()=>[l("Default Theme > Config > sidebar")]),_:1})])])])]),ve,e("ul",null,[fe,be,e("li",null,[ye,e("ul",null,[e("li",null,[s(n,{to:"/themes/default/config.html#sidebardepth"},{default:a(()=>[l("Default Theme > Config > sidebarDepth")]),_:1})])])])]),_e])}const Te=r(u,[["render",ke],["__file","frontmatter.html.vue"]]),Ae=JSON.parse('{"path":"/themes/default/frontmatter.html","title":"Frontmatter","lang":"en-US","frontmatter":{},"headers":[{"level":2,"title":"All Pages","slug":"all-pages","link":"#all-pages","children":[{"level":3,"title":"externalLinkIcon","slug":"externallinkicon","link":"#externallinkicon","children":[]},{"level":3,"title":"navbar","slug":"navbar","link":"#navbar","children":[]},{"level":3,"title":"pageClass","slug":"pageclass","link":"#pageclass","children":[]}]},{"level":2,"title":"Home Page","slug":"home-page","link":"#home-page","children":[{"level":3,"title":"home","slug":"home","link":"#home","children":[]},{"level":3,"title":"heroImage","slug":"heroimage","link":"#heroimage","children":[]},{"level":3,"title":"heroImageDark","slug":"heroimagedark","link":"#heroimagedark","children":[]},{"level":3,"title":"heroAlt","slug":"heroalt","link":"#heroalt","children":[]},{"level":3,"title":"heroHeight","slug":"heroheight","link":"#heroheight","children":[]},{"level":3,"title":"heroText","slug":"herotext","link":"#herotext","children":[]},{"level":3,"title":"tagline","slug":"tagline","link":"#tagline","children":[]},{"level":3,"title":"actions","slug":"actions","link":"#actions","children":[]},{"level":3,"title":"features","slug":"features","link":"#features","children":[]},{"level":3,"title":"footer","slug":"footer","link":"#footer","children":[]},{"level":3,"title":"footerHtml","slug":"footerhtml","link":"#footerhtml","children":[]}]},{"level":2,"title":"Normal Page","slug":"normal-page","link":"#normal-page","children":[{"level":3,"title":"editLink","slug":"editlink","link":"#editlink","children":[]},{"level":3,"title":"editLinkPattern","slug":"editlinkpattern","link":"#editlinkpattern","children":[]},{"level":3,"title":"lastUpdated","slug":"lastupdated","link":"#lastupdated","children":[]},{"level":3,"title":"contributors","slug":"contributors","link":"#contributors","children":[]},{"level":3,"title":"sidebar","slug":"sidebar","link":"#sidebar","children":[]},{"level":3,"title":"sidebarDepth","slug":"sidebardepth","link":"#sidebardepth","children":[]},{"level":3,"title":"prev","slug":"prev","link":"#prev","children":[]},{"level":3,"title":"next","slug":"next","link":"#next","children":[]}]}],"git":{"updatedTime":1706625181000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":2}]},"filePathRelative":"themes/default/frontmatter.md"}');export{Te as comp,Ae as data}; diff --git a/assets/frontmatter.html-CZ3v91x9.js b/assets/frontmatter.html-CZ3v91x9.js new file mode 100644 index 0000000000..9bf8ff6923 --- /dev/null +++ b/assets/frontmatter.html-CZ3v91x9.js @@ -0,0 +1,60 @@ +import{_ as r,r as o,o as c,c as d,a as s,b as l,d as n,w as a,e as i}from"./app-DI49vYHA.js";const u={},h=l("h1",{id:"frontmatter",tabindex:"-1"},[l("a",{class:"header-anchor",href:"#frontmatter"},[l("span",null,"Frontmatter")])],-1),D=l("h2",{id:"所有页面",tabindex:"-1"},[l("a",{class:"header-anchor",href:"#所有页面"},[l("span",null,"所有页面")])],-1),v=l("p",null,"本章节中的 Frontmatter 会在所有类型的页面中生效。",-1),m=l("h3",{id:"externallinkicon",tabindex:"-1"},[l("a",{class:"header-anchor",href:"#externallinkicon"},[l("span",null,"externalLinkIcon")])],-1),b=l("li",null,[l("p",null,[n("类型: "),l("code",null,"boolean")])],-1),_=l("p",null,"详情:",-1),g=l("p",null,"参考:",-1),y=l("h3",{id:"navbar",tabindex:"-1"},[l("a",{class:"header-anchor",href:"#navbar"},[l("span",null,"navbar")])],-1),k=l("li",null,[l("p",null,[n("类型: "),l("code",null,"boolean")])],-1),f=l("li",null,[l("p",null,"详情:"),l("p",null,"是否在当前页面展示导航栏。"),l("p",null,"如果你在主题配置中禁用了导航栏,那么该 Frontmatter 将不会生效。")],-1),x=l("p",null,"参考:",-1),C=i(`

    pageClass

    • 类型: string

    • 详情:

      为当前页面添加额外的类名。

    • 示例:

    ---
    +pageClass: custom-page-class
    +---
    +

    然后你可以在 .vuepress/styles/index.scss 文件中为这个页面添加自定义样式:

    .theme-container.custom-page-class {
    +  /* 页面样式 */
    +}
    +
    `,5),E=i(`

    首页

    本章节中的 Frontmatter 只会在首页中生效。

    home

    • 类型: boolean

    • 详情:

      设定该页面是首页还是普通页面。

      如果你不设置该 Frontmatter 或将其设为 false ,则该页面会是一个 普通页面

    • 示例:

      ---
      +home: true
      +---
      +

    heroImage

    `,5),L=i(`
  • 类型: string

  • 详情:

    首页图片的 URL 。

  • 示例:

    ---
    +# Public 文件路径
    +heroImage: /images/hero.png
    +# URL
    +heroImage: https://vuejs.org/images/logo.png
    +---
    +
  • `,3),A=l("p",null,"参考:",-1),z={href:"https://v2.vuepress.vuejs.org/zh/guide/assets.html#public-%E6%96%87%E4%BB%B6",target:"_blank",rel:"noopener noreferrer"},B=l("h3",{id:"heroimagedark",tabindex:"-1"},[l("a",{class:"header-anchor",href:"#heroimagedark"},[l("span",null,"heroImageDark")])],-1),F=l("li",null,[l("p",null,[n("类型: "),l("code",null,"string")])],-1),N=l("li",null,[l("p",null,"详情:"),l("p",null,"在夜间模式中使用的首页图片的 URL 。"),l("p",null,"如果你想在夜间模式中使用不同的首页图片,就可以使用该配置项。")],-1),I=l("p",null,"参考:",-1),w=l("li",null,[l("a",{href:"#heroimage"},"默认主题 > Frontmatter > heroImage")],-1),P=i('

    heroAlt

    • 类型: string

    • 详情:

      首页图片的 alt 属性。

      如果不设置,则默认使用 heroText

    heroHeight

    ',3),S=l("li",null,[l("p",null,[n("类型: "),l("code",null,"number")])],-1),H=l("li",null,[l("p",null,[n("默认值: "),l("code",null,"280")])],-1),V=l("p",null,"详情:",-1),T=l("p",null,[n("首页图片 "),l("code",null,""),n(" 标签的 "),l("code",null,"height"),n(" 属性。")],-1),M=l("p",null,"当你的首页图片高度小于默认值时,你可能需要减小该属性。",-1),R={href:"https://web.dev/cls/",target:"_blank",rel:"noopener noreferrer"},U=l("h3",{id:"herotext",tabindex:"-1"},[l("a",{class:"header-anchor",href:"#herotext"},[l("span",null,"heroText")])],-1),j=l("li",null,[l("p",null,[n("类型: "),l("code",null,"string | null")])],-1),G=l("p",null,"详情:",-1),O=l("p",null,"首页的大标题。",-1),J={href:"https://v2.vuepress.vuejs.org/zh/reference/config.html#title",target:"_blank",rel:"noopener noreferrer"},q=l("p",null,[n("设置为 "),l("code",null,"null"),n(" 来禁用首页大标题。")],-1),K=l("h3",{id:"tagline",tabindex:"-1"},[l("a",{class:"header-anchor",href:"#tagline"},[l("span",null,"tagline")])],-1),Q=l("li",null,[l("p",null,[n("类型: "),l("code",null,"string | null")])],-1),W=l("p",null,"详情:",-1),X=l("p",null,"首页的标语。",-1),Y={href:"https://v2.vuepress.vuejs.org/zh/reference/config.html#description",target:"_blank",rel:"noopener noreferrer"},Z=l("p",null,[n("设置为 "),l("code",null,"null"),n(" 来禁用首页标语。")],-1),$=i(`

    actions

    • 类型:
    Array<{
    +  text: string
    +  link: string
    +  type?: 'primary' | 'secondary'
    +}>
    +
    • 详情:

      配置首页按钮。

    • 示例:

    ---
    +actions:
    +  - text: 快速上手
    +    link: /zh/guide/getting-started.html
    +    type: primary
    +  - text: 项目简介
    +    link: /zh/guide/introduction.html
    +    type: secondary
    +---
    +

    features

    • 类型:
    Array<{
    +  title: string
    +  details: string
    +}>
    +
    • 详情:

      配置首页特性列表。

    • 示例:

    ---
    +features:
    +  - title: 简洁至上
    +    details: 以 Markdown 为中心的项目结构,以最少的配置帮助你专注于写作。
    +  - title: Vue 驱动
    +    details: 享受 Vue 的开发体验,可以在 Markdown 中使用 Vue 组件,又可以使用 Vue 来开发自定义主题。
    +  - title: 高性能
    +    details: VuePress 会为每个页面预渲染生成静态的 HTML,同时,每个页面被加载的时候,将作为 SPA 运行。
    +---
    +
    • 类型: string

    • 详情:

      首页的页脚。

    footerHtml

    • 类型: boolean

    • 详情:

      是否允许页脚中使用 HTML 。

      如果设置为 true ,那么 footer 会被作为 HTML 代码处理。

    普通页面

    本章节中的 Frontmatter 只会在普通页面中生效。

    `,17),ll=l("li",null,[l("p",null,[n("类型: "),l("code",null,"boolean")])],-1),nl=l("li",null,[l("p",null,"详情:"),l("p",null,[n("是否在本页面中启用 "),l("em",null,"编辑此页"),n(" 链接。")])],-1),sl=l("p",null,"参考:",-1),el=l("h3",{id:"editlinkpattern",tabindex:"-1"},[l("a",{class:"header-anchor",href:"#editlinkpattern"},[l("span",null,"editLinkPattern")])],-1),al=l("li",null,[l("p",null,[n("类型: "),l("code",null,"string")])],-1),il=l("li",null,[l("p",null,"详情:"),l("p",null,[n("本页面中 "),l("em",null,"编辑此页"),n(" 链接的 Pattern 。")])],-1),tl=l("p",null,"参考:",-1),ol=l("h3",{id:"lastupdated",tabindex:"-1"},[l("a",{class:"header-anchor",href:"#lastupdated"},[l("span",null,"lastUpdated")])],-1),pl=l("li",null,[l("p",null,[n("类型: "),l("code",null,"boolean")])],-1),rl=l("li",null,[l("p",null,"详情:"),l("p",null,[n("是否在本页面中启用 "),l("em",null,"最近更新时间戳"),n(" 。")])],-1),cl=l("p",null,"参考:",-1),dl=l("h3",{id:"contributors",tabindex:"-1"},[l("a",{class:"header-anchor",href:"#contributors"},[l("span",null,"contributors")])],-1),ul=l("li",null,[l("p",null,[n("类型: "),l("code",null,"boolean")])],-1),hl=l("li",null,[l("p",null,"详情:"),l("p",null,[n("是否在本页面中启用 "),l("em",null,"贡献者列表"),n(" 。")])],-1),Dl=l("p",null,"参考:",-1),vl=l("h3",{id:"sidebar",tabindex:"-1"},[l("a",{class:"header-anchor",href:"#sidebar"},[l("span",null,"sidebar")])],-1),ml=l("li",null,[l("p",null,[n("类型: "),l("code",null,"false | 'auto' | SidebarConfigArray | SidebarConfigObject")])],-1),bl=l("li",null,[l("p",null,"详情:"),l("p",null,"配置本页面的侧边栏。")],-1),_l=l("p",null,"参考:",-1),gl=l("h3",{id:"sidebardepth",tabindex:"-1"},[l("a",{class:"header-anchor",href:"#sidebardepth"},[l("span",null,"sidebarDepth")])],-1),yl=l("li",null,[l("p",null,[n("类型: "),l("code",null,"number")])],-1),kl=l("li",null,[l("p",null,"详情:"),l("p",null,"配置本页面的侧边栏深度。")],-1),fl=l("p",null,"参考:",-1),xl=i(`

    prev

    • 类型: NavLink | string

    • 详情:

      上一个页面的链接。

      如果你不设置该 Frontmatter ,该链接会自动根据侧边栏配置进行推断。

      为了手动配置上一页面的链接,你可以将其设置为一个 NavLink 对象或者一个字符串:

      • NavLink 对象应该有一个 text 字段和一个 link 字段。
      • 字符串应为目标页面文件的路径。它将会被转换为 NavLink 对象,将页面标题作为 text ,将页面路由路径作为 link
    • 示例:

    ---
    +# NavLink
    +prev:
    +  text: Get Started
    +  link: /guide/getting-started.html
    +
    +# NavLink - 外部 URL
    +prev:
    +  text: GitHub
    +  link: https://github.com
    +
    +# 字符串 - 页面文件路径
    +prev: /guide/getting-started.md
    +
    +# 字符串 - 页面文件相对路径
    +prev: ../../guide/getting-started.md
    +---
    +

    next

    • 类型: NavLink | string

    • 详情:

      下一个页面的链接。

      如果你不设置该 Frontmatter ,该链接会自动根据侧边栏配置进行推断。

      类型和 prev Frontmatter 相同。

    `,5);function Cl(El,Ll){const p=o("NpmBadge"),e=o("RouteLink"),t=o("ExternalLinkIcon");return c(),d("div",null,[h,s(p,{package:"@vuepress/theme-default"}),D,v,m,l("ul",null,[b,l("li",null,[_,l("p",null,[n("由"),s(e,{to:"/zh/plugins/external-link-icon.html#externallinkicon"},{default:a(()=>[n("@vuepress/plugin-external-link-icon")]),_:1}),n(" 提供。")])]),l("li",null,[g,l("ul",null,[l("li",null,[s(e,{to:"/zh/themes/default/config.html#themeplugins-externallinkicon"},{default:a(()=>[n("默认主题 > 配置 > themePlugins.externalLinkIcon")]),_:1})])])])]),y,l("ul",null,[k,f,l("li",null,[x,l("ul",null,[l("li",null,[s(e,{to:"/zh/themes/default/config.html#navbar"},{default:a(()=>[n("默认主题 > 配置 > navbar")]),_:1})])])])]),C,l("ul",null,[l("li",null,[n("参考: "),l("ul",null,[l("li",null,[s(e,{to:"/zh/themes/default/styles.html#style-%E6%96%87%E4%BB%B6"},{default:a(()=>[n("默认主题 > 样式 > Style 文件")]),_:1})])])])]),E,l("ul",null,[L,l("li",null,[A,l("ul",null,[l("li",null,[l("a",z,[n("指南 > 静态资源 > Public 文件"),s(t)])])])])]),B,l("ul",null,[F,N,l("li",null,[I,l("ul",null,[w,l("li",null,[s(e,{to:"/zh/themes/default/config.html#colormode"},{default:a(()=>[n("默认主题 > 配置 > colorMode")]),_:1})])])])]),P,l("ul",null,[S,H,l("li",null,[V,T,M,l("p",null,[n("需要注意的是,首页图片的高度同样受到了 CSS 的约束。设置这个属性主要是为了减少由加载首页图片引起的 "),l("a",R,[n("累积布局偏移 (CLS)"),s(t)]),n(" 。")])])]),U,l("ul",null,[j,l("li",null,[G,O,l("p",null,[n("如果不设置,则默认使用站点 "),l("a",J,[n("title"),s(t)]),n(" 。")]),q])]),K,l("ul",null,[Q,l("li",null,[W,X,l("p",null,[n("如果不设置,则默认使用站点 "),l("a",Y,[n("description"),s(t)]),n(" 。")]),Z])]),$,l("ul",null,[ll,nl,l("li",null,[sl,l("ul",null,[l("li",null,[s(e,{to:"/zh/themes/default/config.html#editlink"},{default:a(()=>[n("默认主题 > 配置 > editLink")]),_:1})])])])]),el,l("ul",null,[al,il,l("li",null,[tl,l("ul",null,[l("li",null,[s(e,{to:"/zh/themes/default/config.html#editlinkpattern"},{default:a(()=>[n("默认主题 > 配置 > editLinkPattern")]),_:1})])])])]),ol,l("ul",null,[pl,rl,l("li",null,[cl,l("ul",null,[l("li",null,[s(e,{to:"/zh/themes/default/config.html#lastupdated"},{default:a(()=>[n("默认主题 > 配置 > lastUpdated")]),_:1})])])])]),dl,l("ul",null,[ul,hl,l("li",null,[Dl,l("ul",null,[l("li",null,[s(e,{to:"/zh/themes/default/config.html#contributors"},{default:a(()=>[n("默认主题 > 配置 > contributors")]),_:1})])])])]),vl,l("ul",null,[ml,bl,l("li",null,[_l,l("ul",null,[l("li",null,[s(e,{to:"/zh/themes/default/config.html#sidebar"},{default:a(()=>[n("默认主题 > 配置 > sidebar")]),_:1})])])])]),gl,l("ul",null,[yl,kl,l("li",null,[fl,l("ul",null,[l("li",null,[s(e,{to:"/zh/themes/default/config.html#sidebardepth"},{default:a(()=>[n("默认主题 > 配置 > sidebarDepth")]),_:1})])])])]),xl])}const zl=r(u,[["render",Cl],["__file","frontmatter.html.vue"]]),Bl=JSON.parse('{"path":"/zh/themes/default/frontmatter.html","title":"Frontmatter","lang":"zh-CN","frontmatter":{},"headers":[{"level":2,"title":"所有页面","slug":"所有页面","link":"#所有页面","children":[{"level":3,"title":"externalLinkIcon","slug":"externallinkicon","link":"#externallinkicon","children":[]},{"level":3,"title":"navbar","slug":"navbar","link":"#navbar","children":[]},{"level":3,"title":"pageClass","slug":"pageclass","link":"#pageclass","children":[]}]},{"level":2,"title":"首页","slug":"首页","link":"#首页","children":[{"level":3,"title":"home","slug":"home","link":"#home","children":[]},{"level":3,"title":"heroImage","slug":"heroimage","link":"#heroimage","children":[]},{"level":3,"title":"heroImageDark","slug":"heroimagedark","link":"#heroimagedark","children":[]},{"level":3,"title":"heroAlt","slug":"heroalt","link":"#heroalt","children":[]},{"level":3,"title":"heroHeight","slug":"heroheight","link":"#heroheight","children":[]},{"level":3,"title":"heroText","slug":"herotext","link":"#herotext","children":[]},{"level":3,"title":"tagline","slug":"tagline","link":"#tagline","children":[]},{"level":3,"title":"actions","slug":"actions","link":"#actions","children":[]},{"level":3,"title":"features","slug":"features","link":"#features","children":[]},{"level":3,"title":"footer","slug":"footer","link":"#footer","children":[]},{"level":3,"title":"footerHtml","slug":"footerhtml","link":"#footerhtml","children":[]}]},{"level":2,"title":"普通页面","slug":"普通页面","link":"#普通页面","children":[{"level":3,"title":"editLink","slug":"editlink","link":"#editlink","children":[]},{"level":3,"title":"editLinkPattern","slug":"editlinkpattern","link":"#editlinkpattern","children":[]},{"level":3,"title":"lastUpdated","slug":"lastupdated","link":"#lastupdated","children":[]},{"level":3,"title":"contributors","slug":"contributors","link":"#contributors","children":[]},{"level":3,"title":"sidebar","slug":"sidebar","link":"#sidebar","children":[]},{"level":3,"title":"sidebarDepth","slug":"sidebardepth","link":"#sidebardepth","children":[]},{"level":3,"title":"prev","slug":"prev","link":"#prev","children":[]},{"level":3,"title":"next","slug":"next","link":"#next","children":[]}]}],"git":{"updatedTime":1706625181000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":2}]},"filePathRelative":"zh/themes/default/frontmatter.md"}');export{zl as comp,Bl as data}; diff --git a/assets/frontmatter.html-CjXlpGGX.js b/assets/frontmatter.html-CjXlpGGX.js new file mode 100644 index 0000000000..e672685dea --- /dev/null +++ b/assets/frontmatter.html-CjXlpGGX.js @@ -0,0 +1 @@ +import{_ as l,r as n,o,c as r,b as e,d as t,a as s,w as p,e as a}from"./app-DI49vYHA.js";const c={},m=a('

    Frontmatter

    sitemap

    • 类型:SitemapFrontmatterOptions | false

    • 详情:

      false 表示将页面排除在 sitemap 之外。

    sitemap.changefreq

    ',4),h=e("li",null,[e("p",null,[t("类型:"),e("code",null,'"always" | "hourly" | "daily" | "weekly" | "monthly" | "yearly" | "never"')])],-1),d=e("li",null,[e("p",null,[t("默认值:"),e("code",null,'"daily"')])],-1),u=e("p",null,"详情:",-1),f=a('

    sitemap.priority

    • 类型:number

    • 默认值:0.5

    • 详情:

      页面优先级,范围 01

    ',2);function _(g,y){const i=n("RouteLink");return o(),r("div",null,[m,e("ul",null,[h,d,e("li",null,[u,e("p",null,[t("页面默认更新频率。它会覆盖插件选项中的 "),s(i,{to:"/zh/plugins/sitemap/config.html#changefreq"},{default:p(()=>[t("changefreq")]),_:1}),t(" 选项。")])])]),f])}const x=l(c,[["render",_],["__file","frontmatter.html.vue"]]),q=JSON.parse('{"path":"/zh/plugins/sitemap/frontmatter.html","title":"Frontmatter","lang":"zh-CN","frontmatter":{},"headers":[{"level":2,"title":"sitemap","slug":"sitemap","link":"#sitemap","children":[{"level":3,"title":"sitemap.changefreq","slug":"sitemap-changefreq","link":"#sitemap-changefreq","children":[]},{"level":3,"title":"sitemap.priority","slug":"sitemap-priority","link":"#sitemap-priority","children":[]}]}],"git":{"updatedTime":1706625181000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":2}]},"filePathRelative":"zh/plugins/sitemap/frontmatter.md"}');export{x as comp,q as data}; diff --git a/assets/frontmatter.html-Dm0TiI4m.js b/assets/frontmatter.html-Dm0TiI4m.js new file mode 100644 index 0000000000..ab3b2b3110 --- /dev/null +++ b/assets/frontmatter.html-Dm0TiI4m.js @@ -0,0 +1 @@ +import{_ as l,r as n,o as r,c as o,b as e,d as t,a as s,w as p,e as a}from"./app-DI49vYHA.js";const c={},m=a('

    Frontmatter

    sitemap

    • Type: SitemapFrontmatterOptions | false

    • Details:

      false means exclude the page from sitemap.

    sitemap.changefreq

    ',4),d=e("li",null,[e("p",null,[t("Type: "),e("code",null,'"always" | "hourly" | "daily" | "weekly" | "monthly" | "yearly" | "never"')])],-1),h=e("li",null,[e("p",null,[t("Default: "),e("code",null,'"daily"')])],-1),u=e("p",null,"Details:",-1),f=a('

    sitemap.priority

    • Type: number

    • Default: 0.5

    • Details:

      Page priority, range from 0 to 1.

    ',2);function _(g,y){const i=n("RouteLink");return r(),o("div",null,[m,e("ul",null,[d,h,e("li",null,[u,e("p",null,[t("Page default update frequency. This will override "),s(i,{to:"/plugins/sitemap/config.html#changefreq"},{default:p(()=>[t("changefreq")]),_:1}),t(" in Plugin Options.")])])]),f])}const k=l(c,[["render",_],["__file","frontmatter.html.vue"]]),q=JSON.parse('{"path":"/plugins/sitemap/frontmatter.html","title":"Frontmatter","lang":"en-US","frontmatter":{},"headers":[{"level":2,"title":"sitemap","slug":"sitemap","link":"#sitemap","children":[{"level":3,"title":"sitemap.changefreq","slug":"sitemap-changefreq","link":"#sitemap-changefreq","children":[]},{"level":3,"title":"sitemap.priority","slug":"sitemap-priority","link":"#sitemap-priority","children":[]}]}],"git":{"updatedTime":1706762763000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":3}]},"filePathRelative":"plugins/sitemap/frontmatter.md"}');export{k as comp,q as data}; diff --git a/assets/frontmatter.html-JmG15wiP.js b/assets/frontmatter.html-JmG15wiP.js new file mode 100644 index 0000000000..454ed3e79c --- /dev/null +++ b/assets/frontmatter.html-JmG15wiP.js @@ -0,0 +1,51 @@ +import{_ as s,o as n,c as e,e as a}from"./app-DI49vYHA.js";const l={},i=a(`

    Frontmatter 配置

    你可以通过配置每个页面的 Frontmatter,来对每个 Feed 项目生成进行单独的控制。

    添加与移除

    默认情况下,所有文章均会被添加至 feed 流。如果你想在 feed 中移除特定页面,你可以在 frontmatter 中设置 feed: false

    读取的 Frontmatter 信息

    title

    • 类型:string

    由 VuePress 自动生成,默认为页面的 h1 内容

    description

    • 类型:string

    页面描述

    date

    • 类型:Date

    页面的发布日期

    article

    • 类型:boolean

    该页面是否是文章

    如果此项设置为 false,则该页不会包含在最终的 feed 中。

    • 类型:string

    页面版权信息

    cover / image / banner

    • 类型:string

    页面的封面/分享图,需为完整链接或绝对链接。

    Frontmatter 选项

    feed.title

    • 类型:string

    Feed 项目的标题

    feed.description

    • 类型:string

    Feed 项目的描述

    feed.content

    • 类型:string

    Feed 项目的内容

    feed.author

    • 类型:FeedAuthor[] | FeedAuthor

    Feed 项目的作者

    FeedAuthor 格式
    interface FeedAuthor {
    +  /**
    +   * 作者名字
    +   */
    +  name?: string
    +
    +  /**
    +   * 作者邮件
    +   */
    +  email?: string
    +
    +  /**
    +   * 作者网站
    +   *
    +   * @description json format only
    +   */
    +  url?: string
    +
    +  /**
    +   * 作者头像
    +   *
    +   * @description json format only
    +   */
    +  avatar?: string
    +}
    +

    feed.contributor

    • 类型:FeedContributor[] | FeedContributor

    Feed 项目的贡献者

    FeedContributor 格式
    interface FeedContributor {
    +  /**
    +   * 作者名字
    +   */
    +  name?: string
    +
    +  /**
    +   * 作者邮件
    +   */
    +  email?: string
    +
    +  /**
    +   * 作者网站
    +   *
    +   * @description json format only
    +   */
    +  url?: string
    +
    +  /**
    +   * 作者头像
    +   *
    +   * @description json format only
    +   */
    +  avatar?: string
    +}
    +

    feed.guid

    • 类型:string

    Feed 项目的标识符,用于标识 Feed 项目。

    你应该确保每个 Feed 项目有全局唯一的 guid。

    `,46),t=[i];function r(p,c){return n(),e("div",null,t)}const d=s(l,[["render",r],["__file","frontmatter.html.vue"]]),u=JSON.parse('{"path":"/zh/plugins/feed/frontmatter.html","title":"Frontmatter 配置","lang":"zh-CN","frontmatter":{},"headers":[{"level":2,"title":"添加与移除","slug":"添加与移除","link":"#添加与移除","children":[]},{"level":2,"title":"读取的 Frontmatter 信息","slug":"读取的-frontmatter-信息","link":"#读取的-frontmatter-信息","children":[{"level":3,"title":"title","slug":"title","link":"#title","children":[]},{"level":3,"title":"description","slug":"description","link":"#description","children":[]},{"level":3,"title":"date","slug":"date","link":"#date","children":[]},{"level":3,"title":"article","slug":"article","link":"#article","children":[]},{"level":3,"title":"copyright","slug":"copyright","link":"#copyright","children":[]},{"level":3,"title":"cover / image / banner","slug":"cover-image-banner","link":"#cover-image-banner","children":[]}]},{"level":2,"title":"Frontmatter 选项","slug":"frontmatter-选项","link":"#frontmatter-选项","children":[{"level":3,"title":"feed.title","slug":"feed-title","link":"#feed-title","children":[]},{"level":3,"title":"feed.description","slug":"feed-description","link":"#feed-description","children":[]},{"level":3,"title":"feed.content","slug":"feed-content","link":"#feed-content","children":[]},{"level":3,"title":"feed.author","slug":"feed-author","link":"#feed-author","children":[]},{"level":3,"title":"feed.contributor","slug":"feed-contributor","link":"#feed-contributor","children":[]},{"level":3,"title":"feed.guid","slug":"feed-guid","link":"#feed-guid","children":[]}]}],"git":{"updatedTime":1706762763000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":2}]},"filePathRelative":"zh/plugins/feed/frontmatter.md"}');export{d as comp,u as data}; diff --git a/assets/getter.html-CRfx9SLW.js b/assets/getter.html-CRfx9SLW.js new file mode 100644 index 0000000000..9d74e6228d --- /dev/null +++ b/assets/getter.html-CRfx9SLW.js @@ -0,0 +1,91 @@ +import{_ as s,o as n,c as e,e as a}from"./app-DI49vYHA.js";const l={},i=a(`

    Feed 获取器

    你可以通过控制插件选项中的 getter 来完全控制 Feed 项目的生成。

    getter.title

    • 类型:(page: Page) => string

    项目标题获取器

    • 类型:(page: Page) => string

    项目链接获取器

    getter.description

    • 类型:(page: Page) => string | undefined

    项目描述获取器

    提示

    因为 Atom 在摘要中支持 HTML,所以如果可能的话,你可以在这里返回 HTML 内容,但内容必须以标记 html: 开头。

    getter.content

    • 类型:(page: Page) => string

    项目内容获取器

    getter.author

    • 类型:(page: Page) => FeedAuthor[]

    项目作者获取器。

    获取器应在作者信息缺失时返回空数组。

    FeedAuthor 格式
    interface FeedAuthor {
    +  /**
    +   * 作者名字
    +   */
    +  name?: string
    +
    +  /**
    +   * 作者邮件
    +   */
    +  email?: string
    +
    +  /**
    +   * 作者网站
    +   *
    +   * @description json format only
    +   */
    +  url?: string
    +
    +  /**
    +   * 作者头像
    +   *
    +   * @description json format only
    +   */
    +  avatar?: string
    +}
    +

    getter.category

    • 类型:(page: Page) => FeedCategory[] | undefined

    项目分类获取器。

    FeedCategory 格式
    interface FeedCategory {
    +  /**
    +   * 分类名称
    +   */
    +  name: string
    +
    +  /**
    +   * 标识分类法的字符串
    +   *
    +   * @description rss format only
    +   */
    +  domain?: string
    +
    +  /**
    +   * URI 标识的分类 scheme
    +   *
    +   * @description atom format only
    +   */
    +  scheme?: string
    +}
    +

    getter.enclosure

    • 类型:(page: Page) => FeedEnclosure | undefined

    项目附件获取器。

    FeedEnclosure 格式
    interface FeedEnclosure {
    +  /**
    +   * Enclosure 地址
    +   */
    +  url: string
    +
    +  /**
    +   * 类型
    +   *
    +   * @description 应为一个标准的 MIME 类型,rss format only
    +   */
    +  type: string
    +
    +  /**
    +   * 按照字节数计算的大小
    +   *
    +   * @description rss format only
    +   */
    +  length?: number
    +}
    +

    getter.publishDate

    • 类型:(page: Page) => Date | undefined

    项目发布日期获取器

    getter.lastUpdateDate

    • 类型:(page: Page) => Date

    项目最后更新日期获取器

    getter.image

    • 类型:(page: Page) => string

    项目图片获取器

    确保返回一个完整的 URL。

    getter.contributor

    • 类型:(page: Page) => FeedContributor[]

    项目贡献者获取器

    获取器应在贡献者信息缺失时返回空数组。

    FeedContributor 格式
    interface FeedContributor {
    +  /**
    +   * 作者名字
    +   */
    +  name?: string
    +
    +  /**
    +   * 作者邮件
    +   */
    +  email?: string
    +
    +  /**
    +   * 作者网站
    +   *
    +   * @description json format only
    +   */
    +  url?: string
    +
    +  /**
    +   * 作者头像
    +   *
    +   * @description json format only
    +   */
    +  avatar?: string
    +}
    +
    • 类型:(page: Page) => string | undefined

    项目版权获取器

    `,46),t=[i];function p(r,c){return n(),e("div",null,t)}const d=s(l,[["render",p],["__file","getter.html.vue"]]),u=JSON.parse('{"path":"/zh/plugins/feed/getter.html","title":"Feed 获取器","lang":"zh-CN","frontmatter":{},"headers":[{"level":2,"title":"getter.title","slug":"getter-title","link":"#getter-title","children":[]},{"level":2,"title":"getter.link","slug":"getter-link","link":"#getter-link","children":[]},{"level":2,"title":"getter.description","slug":"getter-description","link":"#getter-description","children":[]},{"level":2,"title":"getter.content","slug":"getter-content","link":"#getter-content","children":[]},{"level":2,"title":"getter.author","slug":"getter-author","link":"#getter-author","children":[]},{"level":2,"title":"getter.category","slug":"getter-category","link":"#getter-category","children":[]},{"level":2,"title":"getter.enclosure","slug":"getter-enclosure","link":"#getter-enclosure","children":[]},{"level":2,"title":"getter.publishDate","slug":"getter-publishdate","link":"#getter-publishdate","children":[]},{"level":2,"title":"getter.lastUpdateDate","slug":"getter-lastupdatedate","link":"#getter-lastupdatedate","children":[]},{"level":2,"title":"getter.image","slug":"getter-image","link":"#getter-image","children":[]},{"level":2,"title":"getter.contributor","slug":"getter-contributor","link":"#getter-contributor","children":[]},{"level":2,"title":"getter.copyright","slug":"getter-copyright","link":"#getter-copyright","children":[]}],"git":{"updatedTime":1706762763000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":2}]},"filePathRelative":"zh/plugins/feed/getter.md"}');export{d as comp,u as data}; diff --git a/assets/getter.html-DWNp6lVm.js b/assets/getter.html-DWNp6lVm.js new file mode 100644 index 0000000000..d32f54ba24 --- /dev/null +++ b/assets/getter.html-DWNp6lVm.js @@ -0,0 +1,91 @@ +import{_ as s,o as n,c as e,e as a}from"./app-DI49vYHA.js";const l={},t=a(`

    Feed Getter

    You can take full control of feed items generation by setting getter in the plugin options.

    getter.title

    • Type: (page: Page) => string

    Item title getter

    • Type: (page: Page) => string

    Item link getter

    getter.description

    • Type: (page: Page) => string | undefined

    Item description getter

    TIP

    Due to Atom support HTML in summary, so you can return HTML content here if possible, but the content must start with mark html:.

    getter.content

    • Type: (page: Page) => string

    Item content getter

    getter.author

    • Type: (page: Page) => FeedAuthor[]

    Item author getter.

    The getter should return an empty array when author information is missing.

    FeedAuthor format
    interface FeedAuthor {
    +  /**
    +   * Author name
    +   */
    +  name?: string
    +
    +  /**
    +   * Author email
    +   */
    +  email?: string
    +
    +  /**
    +   * Author site
    +   *
    +   * @description json format only
    +   */
    +  url?: string
    +
    +  /**
    +   * Author avatar
    +   *
    +   * @description json format only
    +   */
    +  avatar?: string
    +}
    +

    getter.category

    • Type: (page: Page) => FeedCategory[] | undefined

    Item category getter.

    FeedCategory format
    interface FeedCategory {
    +  /**
    +   * Category Name
    +   */
    +  name: string
    +
    +  /**
    +   * A string that identifies a categorization taxonomy
    +   *
    +   * @description rss format only
    +   */
    +  domain?: string
    +
    +  /**
    +   * the categorization scheme via a URI
    +   *
    +   * @description atom format only
    +   */
    +  scheme?: string
    +}
    +

    getter.enclosure

    • Type: (page: Page) => FeedEnclosure | undefined

    Item enclosure getter.

    FeedEnclosure format
    interface FeedEnclosure {
    +  /**
    +   * Enclosure link
    +   */
    +  url: string
    +
    +  /**
    +   * what its type is
    +   *
    +   * @description should be a standard MIME Type, rss format only
    +   */
    +  Type: string
    +
    +  /**
    +   * Size in bytes
    +   *
    +   * @description rss format only
    +   */
    +  length?: number
    +}
    +

    getter.publishDate

    • Type: (page: Page) => Date | undefined

    Item release date getter

    getter.lastUpdateDate

    • Type: (page: Page) => Date

    Item last update date getter

    getter.image

    • Type: (page: Page) => string

    Item Image Getter

    Ensure it's returning a full URL

    getter.contributor

    • Type: (page: Page) => FeedContributor[]

    Item Contributor Getter

    The getter should return an empty array when contributor information is missing.

    FeedContributor format
    interface FeedContributor {
    +  /**
    +   * Author name
    +   */
    +  name?: string
    +
    +  /**
    +   * Author email
    +   */
    +  email?: string
    +
    +  /**
    +   * Author site
    +   *
    +   * @description json format only
    +   */
    +  url?: string
    +
    +  /**
    +   * Author avatar
    +   *
    +   * @description json format only
    +   */
    +  avatar?: string
    +}
    +
    • Type: (page: Page) => string | undefined

    Item copyright getter

    `,46),i=[t];function p(r,c){return n(),e("div",null,i)}const d=s(l,[["render",p],["__file","getter.html.vue"]]),u=JSON.parse('{"path":"/plugins/feed/getter.html","title":"Feed Getter","lang":"en-US","frontmatter":{},"headers":[{"level":2,"title":"getter.title","slug":"getter-title","link":"#getter-title","children":[]},{"level":2,"title":"getter.link","slug":"getter-link","link":"#getter-link","children":[]},{"level":2,"title":"getter.description","slug":"getter-description","link":"#getter-description","children":[]},{"level":2,"title":"getter.content","slug":"getter-content","link":"#getter-content","children":[]},{"level":2,"title":"getter.author","slug":"getter-author","link":"#getter-author","children":[]},{"level":2,"title":"getter.category","slug":"getter-category","link":"#getter-category","children":[]},{"level":2,"title":"getter.enclosure","slug":"getter-enclosure","link":"#getter-enclosure","children":[]},{"level":2,"title":"getter.publishDate","slug":"getter-publishdate","link":"#getter-publishdate","children":[]},{"level":2,"title":"getter.lastUpdateDate","slug":"getter-lastupdatedate","link":"#getter-lastupdatedate","children":[]},{"level":2,"title":"getter.image","slug":"getter-image","link":"#getter-image","children":[]},{"level":2,"title":"getter.contributor","slug":"getter-contributor","link":"#getter-contributor","children":[]},{"level":2,"title":"getter.copyright","slug":"getter-copyright","link":"#getter-copyright","children":[]}],"git":{"updatedTime":1706762763000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":2}]},"filePathRelative":"plugins/feed/getter.md"}');export{d as comp,u as data}; diff --git a/assets/git.html-CMUkyCND.js b/assets/git.html-CMUkyCND.js new file mode 100644 index 0000000000..03df0c1573 --- /dev/null +++ b/assets/git.html-CMUkyCND.js @@ -0,0 +1,30 @@ +import{_ as r,r as n,o as c,c as d,a,b as s,d as e,w as i,e as o}from"./app-DI49vYHA.js";const u={},h=s("h1",{id:"git",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#git"},[s("span",null,"git")])],-1),m=s("p",null,"This plugin will collect git information of your pages, including the created and updated time, the contributors, etc.",-1),g=o(`

    This plugin is mainly used to develop themes. You won't need to use it directly in most cases.

    Usage

    npm i -D @vuepress/plugin-git@next
    +
    import { gitPlugin } from '@vuepress/plugin-git'
    +
    +export default {
    +  plugins: [
    +    gitPlugin({
    +      // options
    +    }),
    +  ],
    +}
    +

    Git Repository

    `,5),D={href:"https://git-scm.com/book/en/Git-Basics-Getting-a-Git-Repository",target:"_blank",rel:"noopener noreferrer"},y={href:"https://git-scm.com/docs/git-clone#Documentation/git-clone.txt---depthltdepthgt",target:"_blank",rel:"noopener noreferrer"},v=o(`

    WARNING

    This plugin will significantly slow down the speed of data preparation, especially when you have a lot of pages. You can consider disabling this plugin in dev mode to get better development experience.

    Options

    createdTime

    • Type: boolean

    • Default: true

    • Details:

      Whether to collect page created time or not.

    updatedTime

    • Type: boolean

    • Default: true

    • Details:

      Whether to collect page updated time or not.

    contributors

    • Type: boolean

    • Default: true

    • Details:

      Whether to collect page contributors or not.

    Frontmatter

    gitInclude

    • Type: string[]

    • Details:

      An array of relative paths to be included when calculating page data.

    • Example:

    ---
    +gitInclude:
    +  - relative/path/to/file1
    +  - relative/path/to/file2
    +---
    +

    Page Data

    This plugin will add a git field to page data.

    After using this plugin, you can get the collected git information in page data:

    import type { GitPluginPageData } from '@vuepress/plugin-git'
    +import { usePageData } from 'vuepress/client'
    +
    +export default {
    +  setup() {
    +    const page = usePageData<GitPluginPageData>()
    +    console.log(page.value.git)
    +  },
    +}
    +

    git.createdTime

    • Type: number

    • Details:

      Unix timestamp in milliseconds of the first commit of the page.

      This attribute would take the minimum of the first commit timestamps of the current page and the files listed in gitInclude.

    git.updatedTime

    • Type: number

    • Details:

      Unix timestamp in milliseconds of the last commit of the page.

      This attribute would take the maximum of the last commit timestamps of the current page and the files listed in gitInclude.

    git.contributors

    • Type: GitContributor[]
    interface GitContributor {
    +  name: string
    +  email: string
    +  commits: number
    +}
    +
    • Details:

      The contributors information of the page.

      This attribute would also include contributors to the files listed in gitInclude.

    `,24);function b(f,C){const p=n("NpmBadge"),l=n("RouteLink"),t=n("ExternalLinkIcon");return c(),d("div",null,[h,a(p,{package:"@vuepress/plugin-git"}),m,s("p",null,[e("The "),a(l,{to:"/default-theme/config.html#lastupdated"},{default:i(()=>[e("lastUpdated")]),_:1}),e(" and "),a(l,{to:"/default-theme/config.html#contributors"},{default:i(()=>[e("contributors")]),_:1}),e(" of default theme is powered by this plugin.")]),g,s("p",null,[e("This plugin requires your project to be inside a "),s("a",D,[e("Git Repository"),a(t)]),e(", so that it can collect information from the commit history.")]),s("p",null,[e("You should ensure all commits are available when building your site. For example, CI workflows usually clone your repository with "),s("a",y,[e("--depth 1"),a(t)]),e(" to avoid fetching all commits, so you should disable the behavior to make this plugin work properly in CI.")]),v])}const E=r(u,[["render",b],["__file","git.html.vue"]]),x=JSON.parse('{"path":"/plugins/git.html","title":"git","lang":"en-US","frontmatter":{},"headers":[{"level":2,"title":"Usage","slug":"usage","link":"#usage","children":[]},{"level":2,"title":"Git Repository","slug":"git-repository","link":"#git-repository","children":[]},{"level":2,"title":"Options","slug":"options","link":"#options","children":[{"level":3,"title":"createdTime","slug":"createdtime","link":"#createdtime","children":[]},{"level":3,"title":"updatedTime","slug":"updatedtime","link":"#updatedtime","children":[]},{"level":3,"title":"contributors","slug":"contributors","link":"#contributors","children":[]}]},{"level":2,"title":"Frontmatter","slug":"frontmatter","link":"#frontmatter","children":[{"level":3,"title":"gitInclude","slug":"gitinclude","link":"#gitinclude","children":[]}]},{"level":2,"title":"Page Data","slug":"page-data","link":"#page-data","children":[{"level":3,"title":"git.createdTime","slug":"git-createdtime","link":"#git-createdtime","children":[]},{"level":3,"title":"git.updatedTime","slug":"git-updatedtime","link":"#git-updatedtime","children":[]},{"level":3,"title":"git.contributors","slug":"git-contributors","link":"#git-contributors","children":[]}]}],"git":{"updatedTime":1706605723000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":1}]},"filePathRelative":"plugins/git.md"}');export{E as comp,x as data}; diff --git a/assets/git.html-bRRpxIHY.js b/assets/git.html-bRRpxIHY.js new file mode 100644 index 0000000000..41a79777e1 --- /dev/null +++ b/assets/git.html-bRRpxIHY.js @@ -0,0 +1,30 @@ +import{_ as c,r as a,o as r,c as d,a as n,b as e,d as s,w as t,e as p}from"./app-DI49vYHA.js";const u={},D=e("h1",{id:"git",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#git"},[e("span",null,"git")])],-1),h=e("p",null,"该插件会收集你的页面的 Git 信息,包括创建和更新时间、贡献者等。",-1),m=p(`

    该插件主要用于开发主题,大部分情况下你不需要直接使用它。

    使用方法

    npm i -D @vuepress/plugin-git@next
    +
    import { gitPlugin } from '@vuepress/plugin-git'
    +
    +export default {
    +  plugins: [
    +    gitPlugin({
    +      // 配置项
    +    }),
    +  ],
    +}
    +

    Git 仓库

    `,5),g={href:"https://git-scm.com/book/en/Git-Basics-Getting-a-Git-Repository",target:"_blank",rel:"noopener noreferrer"},v={href:"https://git-scm.com/docs/git-clone#Documentation/git-clone.txt---depthltdepthgt",target:"_blank",rel:"noopener noreferrer"},b=p(`

    注意

    该插件会显著降低准备数据的速度,特别是在你的页面数量很多的时候。你可以考虑在 dev 模式下禁用该插件来获取更好的开发体验。

    配置项

    createdTime

    • 类型: boolean

    • 默认值: true

    • 详情:

      是否收集页面的创建时间。

    updatedTime

    • 类型: boolean

    • 默认值: true

    • 详情:

      是否收集页面的更新时间。

    contributors

    • 类型: boolean

    • 默认值: true

    • 详情:

      是否收集页面的贡献者。

    Frontmatter

    gitInclude

    • 类型: string[]

    • 详情:

      文件相对路径组成的数组,该数组中的文件会在计算页面数据时被包含在内。

    • 示例:

    ---
    +gitInclude:
    +  - relative/path/to/file1
    +  - relative/path/to/file2
    +---
    +

    页面数据

    该插件会向页面数据中添加一个 git 字段。

    在使用该插件后,可以在页面数据中获取该插件收集到的 Git 信息:

    import type { GitPluginPageData } from '@vuepress/plugin-git'
    +import { usePageData } from 'vuepress/client'
    +
    +export default {
    +  setup() {
    +    const page = usePageData<GitPluginPageData>()
    +    console.log(page.value.git)
    +  },
    +}
    +

    git.createdTime

    • 类型: number

    • 详情:

      页面第一次提交的 Unix 毫秒时间戳。

      该属性将取当前页面及 gitInclude 中所列文件的第一次提交的时间戳的最小值。

    git.updatedTime

    • 类型: number

    • 详情:

      页面最后一次提交的 Unix 毫秒时间戳。

      该属性将取当前页面及 gitInclude 中所列文件的最后一次提交的时间戳的最大值。

    git.contributors

    • 类型: GitContributor[]
    interface GitContributor {
    +  name: string
    +  email: string
    +  commits: number
    +}
    +
    • 详情:

      页面的贡献者信息。

      该属性将会包含 gitInclude 所列文件的贡献者。

    `,24);function y(C,f){const o=a("NpmBadge"),l=a("RouteLink"),i=a("ExternalLinkIcon");return r(),d("div",null,[D,n(o,{package:"@vuepress/plugin-git"}),h,e("p",null,[s("默认主题的 "),n(l,{to:"/zh/default-theme/config.html#lastupdated"},{default:t(()=>[s("lastUpdated")]),_:1}),s(" 和 "),n(l,{to:"/zh/default-theme/config.html#contributors"},{default:t(()=>[s("contributors")]),_:1}),s(" 就是由该插件支持的。")]),m,e("p",null,[s("该插件要求你的项目在 "),e("a",g,[s("Git 仓库"),n(i)]),s(" 下,这样它才能从提交历史记录中收集信息。")]),e("p",null,[s("在构建站点时,你应该确保所有的提交记录是可以获取到的。举例来说, CI 工作流通常会在克隆你的仓库时添加 "),e("a",v,[s("--depth 1"),n(i)]),s(" 参数来避免拉取全部的提交记录,因此你需要禁用这个功能,以便该插件在 CI 可以中正常使用。")]),b])}const k=c(u,[["render",y],["__file","git.html.vue"]]),x=JSON.parse('{"path":"/zh/plugins/git.html","title":"git","lang":"zh-CN","frontmatter":{},"headers":[{"level":2,"title":"使用方法","slug":"使用方法","link":"#使用方法","children":[]},{"level":2,"title":"Git 仓库","slug":"git-仓库","link":"#git-仓库","children":[]},{"level":2,"title":"配置项","slug":"配置项","link":"#配置项","children":[{"level":3,"title":"createdTime","slug":"createdtime","link":"#createdtime","children":[]},{"level":3,"title":"updatedTime","slug":"updatedtime","link":"#updatedtime","children":[]},{"level":3,"title":"contributors","slug":"contributors","link":"#contributors","children":[]}]},{"level":2,"title":"Frontmatter","slug":"frontmatter","link":"#frontmatter","children":[{"level":3,"title":"gitInclude","slug":"gitinclude","link":"#gitinclude","children":[]}]},{"level":2,"title":"页面数据","slug":"页面数据","link":"#页面数据","children":[{"level":3,"title":"git.createdTime","slug":"git-createdtime","link":"#git-createdtime","children":[]},{"level":3,"title":"git.updatedTime","slug":"git-updatedtime","link":"#git-updatedtime","children":[]},{"level":3,"title":"git.contributors","slug":"git-contributors","link":"#git-contributors","children":[]}]}],"git":{"updatedTime":1706605723000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":1}]},"filePathRelative":"zh/plugins/git.md"}');export{k as comp,x as data}; diff --git a/assets/google-analytics.html-OhT-gEp5.js b/assets/google-analytics.html-OhT-gEp5.js new file mode 100644 index 0000000000..134351282e --- /dev/null +++ b/assets/google-analytics.html-OhT-gEp5.js @@ -0,0 +1,26 @@ +import{_ as i,r as o,o as r,c,a as n,b as s,d as e,e as a}from"./app-DI49vYHA.js";const p={},d=s("h1",{id:"google-analytics",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#google-analytics"},[s("span",null,"google-analytics")])],-1),u={href:"https://analytics.google.com/",target:"_blank",rel:"noopener noreferrer"},g={href:"https://developers.google.com/analytics/devguides/collection/gtagjs",target:"_blank",rel:"noopener noreferrer"},D={href:"https://support.google.com/analytics/answer/10089681",target:"_blank",rel:"noopener noreferrer"},h=a(`

    Usage

    npm i -D @vuepress/plugin-google-analytics@next
    +
    import { googleAnalyticsPlugin } from '@vuepress/plugin-google-analytics'
    +
    +export default {
    +  plugins: [
    +    googleAnalyticsPlugin({
    +      // options
    +    }),
    +  ],
    +}
    +

    Reporting Events

    `,4),v={href:"https://support.google.com/analytics/answer/9234069",target:"_blank",rel:"noopener noreferrer"},y=s("code",null,"page_view",-1),m=s("code",null,"first_visit",-1),_=s("p",null,[e("So if you only want to collect some basic data of your site, you don't need to do anything else except setting the "),s("a",{href:"#id"},"Measurement ID"),e(" correctly.")],-1),b=s("code",null,"gtag()",-1),f=s("code",null,"window",-1),C={href:"https://developers.google.com/analytics/devguides/collection/ga4/events",target:"_blank",rel:"noopener noreferrer"},k=s("h2",{id:"options",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#options"},[s("span",null,"Options")])],-1),E=s("h3",{id:"id",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#id"},[s("span",null,"id")])],-1),x=s("li",null,[s("p",null,[e("Type: "),s("code",null,"string")])],-1),A=s("p",null,"Details:",-1),X=s("p",null,[e("The Measurement ID of Google Analytics 4, which should start with "),s("code",null,"'G-'"),e(".")],-1),w={href:"https://support.google.com/analytics/answer/9539598",target:"_blank",rel:"noopener noreferrer"},I=s("li",null,[s("p",null,"Example:")],-1),G=a(`
    export default {
    +  plugins: [
    +    googleAnalyticsPlugin({
    +      id: 'G-XXXXXXXXXX',
    +    }),
    +  ],
    +}
    +

    debug

    `,2),N=s("li",null,[s("p",null,[e("Type: "),s("code",null,"boolean")])],-1),F=s("p",null,"Details:",-1),T=s("code",null,"true",-1),V={href:"https://support.google.com/analytics/answer/7201382",target:"_blank",rel:"noopener noreferrer"},P=s("li",null,[s("p",null,"Example:")],-1),S=a(`
    export default {
    +  plugins: [
    +    googleAnalyticsPlugin({
    +      id: 'G-XXXXXXXXXX',
    +      debug: true,
    +    }),
    +  ],
    +}
    +
    `,1);function B(M,U){const t=o("NpmBadge"),l=o("ExternalLinkIcon");return r(),c("div",null,[d,n(t,{package:"@vuepress/plugin-google-analytics"}),s("p",null,[e("Integrate "),s("a",u,[e("Google Analytics"),n(l)]),e(" into VuePress.")]),s("p",null,[e("This plugin will import "),s("a",g,[e("gtag.js"),n(l)]),e(" for "),s("a",D,[e("Google Analytics 4"),n(l)]),e(".")]),h,s("p",null,[e("Google Analytics will "),s("a",v,[e("automatically collect some events"),n(l)]),e(", such as "),y,e(", "),m,e(", etc.")]),_,s("p",null,[e("After using this plugin, the global "),b,e(" function is available on the "),f,e(" object, and you can use it for "),s("a",C,[e("custom events reporting"),n(l)]),e(".")]),k,E,s("ul",null,[x,s("li",null,[A,X,s("p",null,[e("You can follow the instructions "),s("a",w,[e("here"),n(l)]),e(' to find your Measurement ID. Notice the difference between Google Analytics 4 Measurement ID (i.e. "G-" ID) and Universal Analytics Tracking ID (i.e. "UA-" ID).')])]),I]),G,s("ul",null,[N,s("li",null,[F,s("p",null,[e("Set to "),T,e(" to enable sending events to DebugView. "),s("a",V,[e("See more information on DebugView"),n(l)]),e(".")])]),P]),S])}const O=i(p,[["render",B],["__file","google-analytics.html.vue"]]),R=JSON.parse('{"path":"/plugins/google-analytics.html","title":"google-analytics","lang":"en-US","frontmatter":{},"headers":[{"level":2,"title":"Usage","slug":"usage","link":"#usage","children":[{"level":3,"title":"Reporting Events","slug":"reporting-events","link":"#reporting-events","children":[]}]},{"level":2,"title":"Options","slug":"options","link":"#options","children":[{"level":3,"title":"id","slug":"id","link":"#id","children":[]},{"level":3,"title":"debug","slug":"debug","link":"#debug","children":[]}]}],"git":{"updatedTime":1707223547000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":2}]},"filePathRelative":"plugins/google-analytics.md"}');export{O as comp,R as data}; diff --git a/assets/google-analytics.html-R2_6EU47.js b/assets/google-analytics.html-R2_6EU47.js new file mode 100644 index 0000000000..294391bcc5 --- /dev/null +++ b/assets/google-analytics.html-R2_6EU47.js @@ -0,0 +1,26 @@ +import{_ as t,r as o,o as c,c as p,a as e,b as s,d as n,e as a}from"./app-DI49vYHA.js";const r={},d=s("h1",{id:"google-analytics",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#google-analytics"},[s("span",null,"google-analytics")])],-1),u={href:"https://analytics.google.com/",target:"_blank",rel:"noopener noreferrer"},g={href:"https://developers.google.com/analytics/devguides/collection/gtagjs",target:"_blank",rel:"noopener noreferrer"},D={href:"https://support.google.com/analytics/answer/10089681",target:"_blank",rel:"noopener noreferrer"},h=a(`

    使用方法

    npm i -D @vuepress/plugin-google-analytics@next
    +
    import { googleAnalyticsPlugin } from '@vuepress/plugin-google-analytics'
    +
    +export default {
    +  plugins: [
    +    googleAnalyticsPlugin({
    +      // 配置项
    +    }),
    +  ],
    +}
    +

    上报事件

    `,4),v={href:"https://support.google.com/analytics/answer/9234069",target:"_blank",rel:"noopener noreferrer"},y=s("code",null,"page_view",-1),_=s("code",null,"first_visit",-1),m=s("p",null,[n("因此,如果你只是想收集站点的一些基础数据,你只需要正确设置 "),s("a",{href:"#id"},"Measurement ID"),n(" ,不需要再额外做其他事情。")],-1),b=s("code",null,"gtag()",-1),C=s("code",null,"window",-1),f={href:"https://developers.google.com/analytics/devguides/collection/ga4/events",target:"_blank",rel:"noopener noreferrer"},k=s("h2",{id:"选项",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#选项"},[s("span",null,"选项")])],-1),E=s("h3",{id:"id",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#id"},[s("span",null,"id")])],-1),x=s("li",null,[s("p",null,[n("类型: "),s("code",null,"string")])],-1),A=s("p",null,"详情:",-1),X=s("p",null,[n("Google Analytics 4 的 Measurement ID ,应以 "),s("code",null,"'G-'"),n(" 开头。")],-1),w={href:"https://support.google.com/analytics/answer/9539598",target:"_blank",rel:"noopener noreferrer"},G=s("li",null,[s("p",null,"示例:")],-1),I=a(`
    export default {
    +  plugins: [
    +    googleAnalyticsPlugin({
    +      id: 'G-XXXXXXXXXX',
    +    }),
    +  ],
    +}
    +

    debug

    `,2),N=s("li",null,[s("p",null,[n("类型: "),s("code",null,"boolean")])],-1),F=s("p",null,"详情:",-1),V=s("code",null,"true",-1),P={href:"https://support.google.com/analytics/answer/7201382",target:"_blank",rel:"noopener noreferrer"},B=s("li",null,[s("p",null,"示例:")],-1),M=a(`
    export default {
    +  plugins: [
    +    googleAnalyticsPlugin({
    +      id: 'G-XXXXXXXXXX',
    +      debug: true,
    +    }),
    +  ],
    +}
    +
    `,1);function z(T,j){const i=o("NpmBadge"),l=o("ExternalLinkIcon");return c(),p("div",null,[d,e(i,{package:"@vuepress/plugin-google-analytics"}),s("p",null,[n("将 "),s("a",u,[n("Google Analytics"),e(l)]),n(" 集成到 VuePress 中。")]),s("p",null,[n("该插件会通过引入 "),s("a",g,[n("gtag.js"),e(l)]),n(" 来启用 "),s("a",D,[n("Google Analytics 4"),e(l)]),n(" 。")]),h,s("p",null,[n("Google Analytics 会 "),s("a",v,[n("自动收集部分事件"),e(l)]),n(" ,比如 "),y,n(", "),_,n(" 等。")]),m,s("p",null,[n("在引入该插件之后,一个全局的 "),b,n(" 函数会被挂载到 "),C,n(" 对象上,你可以使用它进行 "),s("a",f,[n("自定义事件的上报"),e(l)]),n(" 。")]),k,E,s("ul",null,[x,s("li",null,[A,X,s("p",null,[n("你可以通过 "),s("a",w,[n("这里"),e(l)]),n(' 的指引来找到你的 Measurement ID 。注意区分 Google Analytics 4 的 Measurement ID (即 "G-" 开头的 ID) 和 Universal Analytics 的 Tracking ID (即 "UA-" 开头的 ID)。')])]),G]),I,s("ul",null,[N,s("li",null,[F,s("p",null,[n("设置为 "),V,n(" 可以向 DebugView 发送事件。"),s("a",P,[n("了解更多关于 DebugView 的信息"),e(l)]),n(" 。")])]),B]),M])}const S=t(r,[["render",z],["__file","google-analytics.html.vue"]]),U=JSON.parse('{"path":"/zh/plugins/google-analytics.html","title":"google-analytics","lang":"zh-CN","frontmatter":{},"headers":[{"level":2,"title":"使用方法","slug":"使用方法","link":"#使用方法","children":[{"level":3,"title":"上报事件","slug":"上报事件","link":"#上报事件","children":[]}]},{"level":2,"title":"选项","slug":"选项","link":"#选项","children":[{"level":3,"title":"id","slug":"id","link":"#id","children":[]},{"level":3,"title":"debug","slug":"debug","link":"#debug","children":[]}]}],"git":{"updatedTime":1707223547000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":2}]},"filePathRelative":"zh/plugins/google-analytics.md"}');export{S as comp,U as data}; diff --git a/assets/guide.html-6nwdbxs_.js b/assets/guide.html-6nwdbxs_.js new file mode 100644 index 0000000000..32546dc2ba --- /dev/null +++ b/assets/guide.html-6nwdbxs_.js @@ -0,0 +1,218 @@ +import{_ as e,r as t,o as c,c as r,b as o,d as s,a,w as l,e as p}from"./app-DI49vYHA.js";const i={},D=p(`

    With @vuepress/plugin-blog, you can easily bring blog feature into your theme.

    Collecting Articles

    The plugin filters all pages using filter option to drop pages you don't want.

    By default, all pages generating from Markdown files but not homepage are considered as articles.

    You can fully customize pages to collect through option filter, which accepts a function (page: Page) => boolean.

    Gathering Info

    You should set getInfo option with a function accepting Page as argument and returning an object containing the info you want.

    The plugin will collect all the info you want and write them to routeMeta field of each page, so you will be able to get this information through Composition API later.

    Demo
    import { blogPlugin } from '@vuepress/plugin-blog'
    +
    +export default {
    +  name: 'vuepress-theme-xxx',
    +  plugins: [
    +    blogPlugin({
    +      filter: ({ filePathRelative, frontmatter }) => {
    +        // drop those pages which is NOT generated from file
    +        if (!filePathRelative) return false
    +
    +        // drop those pages in \`archives\` directory
    +        if (filePathRelative.startsWith('archives/')) return false
    +
    +        // drop those pages which do not use default layout
    +        if (frontmatter.home || frontmatter.layout) return false
    +
    +        return true
    +      },
    +
    +      getInfo: ({ frontmatter, git = {}, data = {} }) => {
    +        // getting page info
    +        const info: Record<string, any> = {
    +          author: frontmatter.author || '',
    +          categories: frontmatter.categories || [],
    +          date: frontmatter.date || git.createdTime || null,
    +          tags: frontmatter.tags || [],
    +          excerpt: data.excerpt || '',
    +        }
    +
    +        return info
    +      },
    +    }),
    +    // other plugins ...
    +  ],
    +}
    +

    Customizing Categories and Types

    Basically, you would want 2 types of collection in your blog:

    • Category:

      "Category" means grouping articles with their labels.

      For example, each article may have their "categories" and "tags".

    • Type:

      "Type" means identifying articles with conditions.

      For example, you may want to describe some of your articles as diary.

    After understanding description of these 2 types, you can set category and type options, each accepts an array, and each element represents a configuration.

    Let's start with 2 examples here.

    Imagine you are setting tags for each articles with tag field in page frontmatter. You want a tag mapping page in /tag/ with TagMap layout, and group each tag list with tagName in /tag/tagName with TagList layout, you probably need a configuration like this:

    import { blogPlugin } from '@vuepress/plugin-blog'
    +
    +export default {
    +  name: 'vuepress-theme-xxx',
    +  plugins: [
    +    blogPlugin({
    +      // other options ...
    +      category: [
    +        {
    +          key: 'tag',
    +          getter: ({ frontmatter }) => frontmatter.tag || [],
    +          path: '/tag/',
    +          layout: 'TagMap',
    +          frontmatter: () => ({ title: 'Tag page' }),
    +          itemPath: '/tag/:name/',
    +          itemLayout: 'TagList',
    +          itemFrontmatter: (name) => ({ title: \`Tag \${name}\` }),
    +        },
    +      ],
    +    }),
    +    // other plugins ...
    +  ],
    +}
    +

    Also, you may want to star some of your articles, and display them to visitors. When you are setting star: true in frontmatter to mark them, you probably need a configuration like this to display them in /star/ path with StarList layout:

    import { blogPlugin } from '@vuepress/plugin-blog'
    +
    +export default {
    +  name: 'vuepress-theme-xxx',
    +  plugins: [
    +    blogPlugin({
    +      // other options ...
    +      type: [
    +        {
    +          key: 'star',
    +          filter: ({ frontmatter }) => frontmatter.star,
    +          path: '/star/',
    +          layout: 'StarList',
    +          frontmatter: () => ({ title: 'Star page' }),
    +        },
    +      ],
    +    }),
    +    // other plugins ...
    +  ],
    +}
    +
    `,18),y=p(`

    Using Composition API in Client-side

    When generating each page, the plugin will set following information under frontmatter.blog:

    interface BlogFrontmatterOptions {
    +  /** Current type of the page */
    +  type: 'category' | 'type'
    +  /** Unique key under current category or tag */
    +  key: string
    +  /**
    +   * Current category name
    +   *
    +   * @description Only available in category item page
    +   */
    +  name?: string
    +}
    +

    So you can invoke useBlogCategory() and useBlogType() directly, and the result will be the category or type bind to current route.

    Also, you can pass key you want as argument, then you will get information bind to that key.

    So with node side settings above, you can get information about "tag" and "star" in client side:

    TagMap layout:

    <script setup lang="ts">
    +import { useBlogCategory } from '@vuepress/plugin-blog'
    +import { RouteLink } from 'vuepress/client'
    +
    +const categoryMap = useBlogCategory('tag')
    +</script>
    +
    +<template>
    +  <div>
    +    <h1>Tag page</h1>
    +    <ul>
    +      <li v-for="({ items, path }, name) in categoryMap.map">
    +        <RouteLink :key="name" :to="path" class="category">
    +          {{ name }}
    +          <span class="category-num">
    +            {{ items.length }}
    +          </span>
    +        </RouteLink>
    +      </li>
    +    </ul>
    +  </div>
    +</template>
    +

    TagList layout:

    <script setup lang="ts">
    +import { useBlogCategory } from '@vuepress/plugin-blog'
    +
    +const categoryMap = useBlogCategory('tag')
    +</script>
    +
    +<template>
    +  <div>
    +    <h1>Tag page</h1>
    +    <div class="category-wrapper">
    +      <RouteLink
    +        v-for="({ items, path }, name) in categoryMap.map"
    +        :key="name"
    +        :to="path"
    +        class="category"
    +      >
    +        {{ name }}
    +        <span class="category-num">
    +          {{ items.length }}
    +        </span>
    +      </RouteLink>
    +    </div>
    +    <div class="article-wrapper" v-if="categoryMap.currentItems">
    +      <div v-if="!categoryMap.currentItems.length">Nothing in here.</div>
    +      <article
    +        v-for="{ info, path } in categoryMap.currentItems"
    +        class="article"
    +        @click="$router.push(path)"
    +      >
    +        <header class="title">
    +          {{
    +            (isTimeline
    +              ? \`\${new Date(info.date).toLocaleDateString()}: \`
    +              : '') + info.title
    +          }}
    +        </header>
    +        <hr />
    +        <div class="article-info">
    +          <span v-if="info.author" class="author"
    +            >Author: {{ info.author }}</span
    +          >
    +          <span v-if="info.date && !isTimeline" class="date"
    +            >Date: {{ new Date(info.date).toLocaleDateString() }}</span
    +          >
    +          <span v-if="info.category" class="category"
    +            >Category: {{ info.category.join(', ') }}</span
    +          >
    +          <span v-if="info.tag" class="tag"
    +            >Tag: {{ info.tag.join(', ') }}</span
    +          >
    +        </div>
    +        <div v-if="info.excerpt" class="excerpt" v-html="info.excerpt" />
    +      </article>
    +    </div>
    +  </div>
    +</template>
    +

    StarList layout:

    <script setup lang="ts">
    +import { useBlogType } from '@vuepress/plugin-blog/client'
    +import { RouteLink } from 'vuepress/client'
    +
    +import ArticleList from '../components/ArticleList.vue'
    +import ParentLayout from '@vuepress/theme-default/layouts/Layout.vue'
    +
    +const stars = useBlogType('star')
    +</script>
    +
    +<template>
    +  <div class="article-wrapper" v-if="stars.items">
    +    <div v-if="!stars.items.length">Nothing in here.</div>
    +    <article
    +      v-for="{ info, path } in stars.items"
    +      class="article"
    +      @click="$router.push(path)"
    +    >
    +      <header class="title">
    +        {{
    +          (isTimeline ? \`\${new Date(info.date).toLocaleDateString()}: \` : '') +
    +          info.title
    +        }}
    +      </header>
    +      <hr />
    +      <div class="article-info">
    +        <span v-if="info.author" class="author">Author: {{ info.author }}</span>
    +        <span v-if="info.date && !isTimeline" class="date"
    +          >Date: {{ new Date(info.date).toLocaleDateString() }}</span
    +        >
    +        <span v-if="info.category" class="category"
    +          >Category: {{ info.category.join(', ') }}</span
    +        >
    +        <span v-if="info.tag" class="tag">Tag: {{ info.tag.join(', ') }}</span>
    +      </div>
    +      <div v-if="info.excerpt" class="excerpt" v-html="info.excerpt" />
    +    </article>
    +  </div>
    +</template>
    +
    `,12),d=p(`

    I18n Support

    This plugin adds native i18n support, so your settings will be automatically applied to each language.

    For example, if user has the following locales' config, and you are setting the "star" example above:

    export default {
    +  locales: {
    +    '/': {
    +      lang: 'en-US',
    +    },
    +    '/zh/': {
    +      lang: 'zh-CN',
    +    },
    +  },
    +}
    +

    Then /zh/star/ and /star/ will both be available, and only articles under the correct locale will appear.

    Generating Excerpt

    This plugin provides a built-in excerpt generator, which can be enabled by setting excerpt option to true.

    Excerpt introduction

    An excerpt is an HTML fragment that is used to display a short description of an article in the blog list, so the excerpt has the following restrictions:

    • It doesn't support any unknown tags (including all Vue components) and Vue syntax, so these contents will be removed when generating. If you have custom components (non-Vue components), set isCustomElement option.
    • Since the snippet is an HTML fragment, you will not be able to import any images via relative paths or aliases, they will be removed directly. If you want to keep images, please use absolute path based on .vuepress/public or full URL to ensure they can be accessed in other places.

    The excerpt generator will try to find a valid excerpt separator from markdown contents, if it finds one, it will use content before the separator. The separator is default <!-- more -->, and you can customize it by setting excerptSeparator option.

    If it cannot find a valid separator, it will parse content from the beginning of markdown file, and stop till its length reaches a preset value. The value is default 300, and you can customize it by setting excerptLength option.

    To choose which page should generate excerpt, you can use excerptFilter option.

    Example

    Normally you may want to use frontmatter.description if users set them, so you can let filter function return false if frontmatter.description is not empty.

    `,12);function C(u,v){const n=t("RouteLink");return c(),r("div",null,[D,o("p",null,[s("See, setting these 2 types is easy. For full options, please see "),a(n,{to:"/plugins/blog/config.html#blog-category-config"},{default:l(()=>[s("Category Config")]),_:1}),s(" and "),a(n,{to:"/plugins/blog/config.html#blog-type-config"},{default:l(()=>[s("Type Config")]),_:1}),s(".")]),y,o("p",null,[s("For return types, please see "),a(n,{to:"/plugins/blog/config.html#composition-api"},{default:l(()=>[s("Composition API Return Types")]),_:1}),s(".")]),d])}const g=e(i,[["render",C],["__file","guide.html.vue"]]),b=JSON.parse('{"path":"/plugins/blog/guide.html","title":"Guide","lang":"en-US","frontmatter":{"title":"Guide","icon":"lightbulb"},"headers":[{"level":2,"title":"Collecting Articles","slug":"collecting-articles","link":"#collecting-articles","children":[]},{"level":2,"title":"Gathering Info","slug":"gathering-info","link":"#gathering-info","children":[]},{"level":2,"title":"Customizing Categories and Types","slug":"customizing-categories-and-types","link":"#customizing-categories-and-types","children":[]},{"level":2,"title":"Using Composition API in Client-side","slug":"using-composition-api-in-client-side","link":"#using-composition-api-in-client-side","children":[]},{"level":2,"title":"I18n Support","slug":"i18n-support","link":"#i18n-support","children":[]},{"level":2,"title":"Generating Excerpt","slug":"generating-excerpt","link":"#generating-excerpt","children":[]}],"git":{"updatedTime":1708365679000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":2}]},"filePathRelative":"plugins/blog/guide.md"}');export{g as comp,b as data}; diff --git a/assets/guide.html-BNXyZOh6.js b/assets/guide.html-BNXyZOh6.js new file mode 100644 index 0000000000..479d587a52 --- /dev/null +++ b/assets/guide.html-BNXyZOh6.js @@ -0,0 +1,23 @@ +import{_ as r,r as l,o as c,c as p,b as e,d as s,a as o,w as i,e as n}from"./app-DI49vYHA.js";const d={},h=n('

    Guide

    Intro

    Make your VuePress site a Progressive Web Application (PWA)[1].

    ',3),u={href:"https://developers.google.com/web/tools/workbox/modules/workbox-build",target:"_blank",rel:"noopener noreferrer"},f={href:"https://github.com/yyx990803/register-service-worker",target:"_blank",rel:"noopener noreferrer"},g={class:"custom-container warning"},y=e("p",{class:"custom-container-title"},"WARNING",-1),m=n(`

    A PWA uses a Service Worker [2] (SW for short) to cache and proxy site content.

    Web App Manifests

    To make your website fully compliant with PWA, a Web app manifests [3] file is needed, and your pwa should satisfy the installability [4] specification.

    You can set manifest option to customize the manifest file, or provide a manifest.webmanifest or manifest.json in public folder. The former has higher priority.

    The plugin will automatically generate manifest.webmanifest for you and add manifest link declaration in each page, while you should still at least set a valid icon through manifest.icons or other icon related options in the PWA plugin.

    WARNING

    The installability [4:1] specification requires at least one valid icon to be declared in the manifest.

    So if you do not configure manifest.icons, visitors can only enjoy the offline accessibility brought by the Service Worker cache, while cannot install your site as a PWA.

    Besides the plugin does not process anything in the manifest by default, but outputs them as-is. This means that if you plan to deploy to a subdirectory, you should append the URL prefix to manifest Urls yourself. If everything you need is all under base directory, you can set appendBase: true in plugin options to let the plugin append base to any links in manifest.

    Cache Control

    To better control what the Service Worker can pre-cache, the plugin provides related options for cache control.

    Default cache

    By default, the plugin will pre-cache all js css files, and only homepage and 404 html are cached. The plugin will also cache font files (woff, woff2, eot, ttf, otf) and SVG icons.

    Image Cache

    If your site has only a few important images, and want them displayed in offline mode, you can cache site images by setting cacheImage: true .

    We recognize images by file extension. Any files ending with .png, .jpg, .jpeg, .gif, .bmp, .webp will be regarded as images.

    HTML Cache

    If you have small sites, and would like to make document fully offline available, you can set cacheHTML to true to cache all HTML files.

    Why only home and 404 page been cached by default?

    Though VuePress generates HTML files through SSG[5] for all pages, these files are mainly used for SEO[6] and allow you to directly configure the backend without SPA[7] Visit any link.

    VuePress is essentially an SPA. This means that you only need to cache the home page and enter from the home page to access all pages normally. Therefore, not caching other HTML by default can effectively reduce the cache size (40% smaller in size) and speed up the SW update speed.

    But this also has the disadvantage. If the user enters the site directly from a non-home page, the HTML file for the first page still needs to be loaded from the internet. Also, in offline environment, users can only enter through the homepage and then navigate to the corresponding page by themselves. If they directly access a link, an inaccessible prompt will appear.

    Size Control

    To prevent large files from being included in the pre-cache list, any files > 2 MB or images > 1 MB will be omitted. You can customize these limits with maxSize and maxImageSize options (in KB unit).

    Update Control

    We provide the update option to control how users receive updates.

    The default value of the update option is "available", which means that when new content available, the new SW will be installed and its resources will be fetched silently in the background. A pop-up window appears once the new SW is ready, and users can choose whether to refresh immediately to view new content. This means users are reading old content before a new SW is ready.

    If your project is still in building stage, and you want to alert the user that he may be reading outdated content, you can set this to "hint". This allows users to be notified that new content has been published within seconds after visiting docs. But the negative effect of this is that if the user chooses to update before the new SW is ready, he will need to get all the resources of the page from the internet before the new SW installs and controls the page.

    If your docs are stable, or you're hosting a blog and don't care much about users receiving the latest version right away, you can set this to "disabled", which means that the new SW will be installed completely silently in the background and start waiting, when all pages controlled by old SW are all closed, the new SW will start to take control and provide users with new content during next visit. This setting can prevent users from being disturbed during the visit.

    To speed up user access under weak or no network conditions through SW, but also want users to always access new content, you can set this option to "force". This means any old SW will be removed as soon as a new SW is detected, and all pages are refreshed to ensure the user is browsing the latest content. The biggest disadvantage is that all users will experience unexpected sudden refresh within seconds after reentering an updated site.

    Popups

    When new content is detected (new SW detected), a update found popup appears; and when the new content is ready, an update ready popup appears.

    If you are not satisfied with the default popup content, you can use your own component. Import PWAFoundPopup or PWAReadyPopup from @vuepress/plugin-pwa/client and use its slot to customize the popup content, then pass the component path to foundComponent or readyComponent option:

    <script setup lang="ts">
    +import { PWAFoundPopup } from '@vuepress/plugin-pwa/client'
    +</script>
    +<template>
    +  <PWAFoundPopup v-slot="{ found, refresh }">
    +    <div v-if="found">
    +      New content is found.
    +      <button @click="refresh">Refresh</button>
    +    </div>
    +  </PWAFoundPopup>
    +</template>
    +
    <script setup lang="ts">
    +import { PWAReadyPopup } from '@vuepress/plugin-pwa/client'
    +</script>
    +<template>
    +  <PWAReadyPopup v-slot="{ isReady, reload }">
    +    <div v-if="isReady">
    +      New content is ready.
    +      <button @click="reload">Apply</button>
    +    </div>
    +  </PWAReadyPopup>
    +</template>
    +

    Other Options

    `,31),b=e("code",null,"generateSwConfig",-1),v=e("code",null,"workbox-build",-1),D=e("h2",{id:"further-reading",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#further-reading"},[e("span",null,"Further Reading")])],-1),w=e("p",null,"For more details, please see:",-1),_={href:"https://web.dev/progressive-web-apps/",target:"_blank",rel:"noopener noreferrer"},C={href:"https://developer.mozilla.org/en-US/docs/Web/Progressive_web_apps",target:"_blank",rel:"noopener noreferrer"},k={href:"https://w3c.github.io/manifest/",target:"_blank",rel:"noopener noreferrer"},W=e("hr",{class:"footnotes-sep"},null,-1),S={class:"footnotes"},P={class:"footnotes-list"},x={id:"footnote1",class:"footnote-item"},A=e("p",null,[e("strong",null,"PWA introduction")],-1),E=e("p",null,"PWA, full name Progressive Web app. PWA standard is stipulated by W3C.",-1),T=e("p",null,"It allows sites to install the site as an App on supported platform through a browser that supports this feature.",-1),q={href:"https://developer.mozilla.org/en-US/docs/Web/Progressive_web_apps",target:"_blank",rel:"noopener noreferrer"},F=e("a",{href:"#footnote-ref1",class:"footnote-backref"},"↩︎",-1),I=n('
  • Service Worker Introduction

    1. The Service Worker will get and cache all the files registered in it during the registration process.

    2. After the registration complete, the Service Worker is activated, and starts to proxy and control all your requests.

    3. Whenever you want to initiate an access request through the browser, the Service Worker will check whether it exists in its own cache list, if it exists, it will directly return the cached result, otherwise it will call its own fetch method to get it. You can use a custom fetch method to fully control the result of the request for resources in the web page, such as providing a fallback web page when offline.

    4. Every time the user reopens the site, the Service Worker will request to the link when it was registered. If a new version of Service Worker is detected, it will update itself and start caching the list of resources registered in the new Service Worker . After the content update is successfully obtained, the Service Worker will trigger the update event. The user can be notified through this event, for example, a pop-up window will be displayed in the lower right corner, prompting the user that new content is available and allowing the user to trigger an update.

    ↩︎
  • ',1),M={id:"footnote3",class:"footnote-item"},z=e("p",null,[e("strong",null,"Manifest File")],-1),R=e("p",null,"The manifest file uses the JSON format and is responsible for declaring various information of the PWA, such as name, description, icon, and shortcut actions.",-1),N=e("p",null,"In order for your site to be registered as a PWA, you need to meet the basic specifications of the manifest to make the browser consider the site as an installable PWA and allow users to install it.",-1),B={class:"custom-container tip"},L=e("p",{class:"custom-container-title"},"TIP",-1),O={href:"https://developer.mozilla.org/en-US/docs/Web/Manifest",target:"_blank",rel:"noopener noreferrer"},G={href:"https://w3c.github.io/manifest/",target:"_blank",rel:"noopener noreferrer"},U=e("a",{href:"#footnote-ref3",class:"footnote-backref"},"↩︎",-1),V=n('
  • Installable

    To let the site be registered as a PWA, the site needs to successfully register a valid service worker by itself, and declare a valid manifest file with its link in meta tag.

    The manifest file should contain at least name (or short_name) icons start_url.

    On safari, the maximum cache size of the service worker is 50 MB. ↩︎ ↩︎

  • SSG: Static Site Generation, ↩︎

  • SEO: Search Engine Optimization. ↩︎

  • SPA: Single Page Application, most of them only have the homepage, and use history mode to handle routing instead of actually navigating between pages. ↩︎

  • ',4);function H(j,Y){const t=l("ExternalLinkIcon"),a=l("RouteLink");return c(),p("div",null,[h,e("p",null,[s("This plugin uses "),e("a",u,[s("workbox-build"),o(t)]),s(" to generate service worker file, and uses "),e("a",f,[s("register-service-worker"),o(t)]),s(" to register service worker.")]),e("div",g,[y,e("p",null,[s("If you enabled this plugin once and you want to disable it, you might need "),o(a,{to:"/plugins/remove-pwa.html"},{default:i(()=>[s("`@vuepress/plugin-remove-pwa")]),_:1}),s(" to remove the existing service worker.")])]),m,e("p",null,[s("The plugin also provides other PWA-related options, such as Microsoft tile icon and color settings, Apple icon and so on. If you are an advanced user, you can also set "),b,s(" to configure "),v,s(". Check "),o(a,{to:"/plugins/pwa/config.html#options"},{default:i(()=>[s("Plugin options")]),_:1}),s(" for more details.")]),D,w,e("ul",null,[e("li",null,[e("a",_,[s("Google PWA"),o(t)])]),e("li",null,[e("a",C,[s("MDN PWA"),o(t)])]),e("li",null,[e("a",k,[s("W3C Manifest Specification"),o(t)])])]),W,e("section",S,[e("ol",P,[e("li",x,[A,E,T,e("p",null,[s("See "),e("a",q,[s("https://developer.mozilla.org/en-US/docs/Web/Progressive_web_apps"),o(t)]),s(" for details. "),F])]),I,e("li",M,[z,R,N,e("div",B,[L,e("p",null,[s("For Manifest standards and specifications, please see "),e("a",O,[s("MDN Web app manifests"),o(t)]),s(" and "),e("a",G,[s("W3C Manifest"),o(t)]),s(".")])]),U]),V])])])}const K=r(d,[["render",H],["__file","guide.html.vue"]]),Q=JSON.parse('{"path":"/plugins/pwa/guide.html","title":"Guide","lang":"en-US","frontmatter":{},"headers":[{"level":2,"title":"Intro","slug":"intro","link":"#intro","children":[]},{"level":2,"title":"Web App Manifests","slug":"web-app-manifests","link":"#web-app-manifests","children":[]},{"level":2,"title":"Cache Control","slug":"cache-control","link":"#cache-control","children":[{"level":3,"title":"Default cache","slug":"default-cache","link":"#default-cache","children":[]},{"level":3,"title":"Image Cache","slug":"image-cache","link":"#image-cache","children":[]},{"level":3,"title":"HTML Cache","slug":"html-cache","link":"#html-cache","children":[]},{"level":3,"title":"Size Control","slug":"size-control","link":"#size-control","children":[]}]},{"level":2,"title":"Update Control","slug":"update-control","link":"#update-control","children":[{"level":3,"title":"Popups","slug":"popups","link":"#popups","children":[]}]},{"level":2,"title":"Other Options","slug":"other-options","link":"#other-options","children":[]},{"level":2,"title":"Further Reading","slug":"further-reading","link":"#further-reading","children":[]}],"git":{"updatedTime":1708365979000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":1}]},"filePathRelative":"plugins/pwa/guide.md"}');export{K as comp,Q as data}; diff --git a/assets/guide.html-BVrSzIwm.js b/assets/guide.html-BVrSzIwm.js new file mode 100644 index 0000000000..2936e2ecec --- /dev/null +++ b/assets/guide.html-BVrSzIwm.js @@ -0,0 +1,37 @@ +import{_ as c,r as s,o as d,c as p,b as e,d as t,a as n,w as o,e as l}from"./app-DI49vYHA.js";const g={},u=e("h1",{id:"guide",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#guide"},[e("span",null,"Guide")])],-1),h={href:"https://ogp.me/",target:"_blank",rel:"noopener noreferrer"},y={href:"https://www.w3.org/TR/json-ld-api/",target:"_blank",rel:"noopener noreferrer"},m=l(`

    Out of Box

    The plugin works out of the box. Without any config, it will extract information from the page content as much as possible to complete the necessary tags required by OGP and JSON-LD.

    By default, the plugin will read the site config and page frontmatter to automatically generate tags as much as possible. Such as site name, page title, page type, writing date, last update date, and article tags are all automatically generated.

    The following are the <meta> tags and their values that will be injected into <head> by default:

    Default OGP Generation

    The following are the <meta> tags and their value injected into <head> by default to satisfy OGP:

    Meta NameValue
    og:urloptions.hostname + path
    og:site_namesiteConfig.title
    og:titlepage.title
    og:descriptionpage.frontmatter.description || auto generated (when autoDescription is true in plugin options)
    og:type"article"
    og:imageoptions.hostname + page.frontmatter.image ||first image in page || fallbackImage in plugin options
    og:updated_timepage.git.updatedTime
    og:localepage.lang
    og:locale:alternateOther languages in siteData.locales
    twitter:card"summary_large_image" (only available when image found)
    twitter:image:altpage.title (only available when image found)
    article:authorpage.frontmatter.author || options.author
    article:tagpage.frontmatter.tags || page.frontmatter.tag
    article:published_timepage.frontmatter.date || page.git.createdTime
    article:modified_timepage.git.updatedTime

    Default JSON-LD Generation

    Property NameValue
    @context"https://schema.org"
    @type"NewsArticle"
    headlinepage.title
    imageimage in page || options.hostname + page.frontmatter.image || siteFavIcon in plugin options
    datePublishedpage.frontmatter.date || page.git.createdTime
    dateModifiedpage.git.updatedTime
    authorpage.frontmatter.author || options.author

    Setting Tags Directly

    You can configure the head option in the page's frontmatter to add specific tags to the page <head> to enhance SEO. For example:

    ---
    +head:
    +  - - meta
    +    - name: keywords
    +      content: SEO plugin
    +---
    +

    Will automatically inject <meta name="keywords" content="SEO plugin" />.

    Customize Generation

    The plugin also gives you full control over the build logic.

    Page Type

    For most pages, there are basically only two types: articles and website, so the plugin provides the isArticle option to allow you to provide logic for identifying articles.

    The option accepts a function in the format (page: Page) => boolean, by default all non-home pages generated from Markdown files are treated as articles.

    TIP

    If a page does fit into the "unpopular" genre like books, music, etc., you can handle them by setting the three options below.

    OGP

    You can use the plugin options ogp to pass in a function to modify the default OGP object to your needs and return it.

    function ogp(
    +  /** OGP Object inferred by plugin */
    +  ogp: SeoContent,
    +  /** Page Object */
    +  page: Page,
    +  /** VuePress App */
    +  app: App,
    +): SeoContent
    +
    `,22),D=l(`

    For example, if you are using a third-party theme and set a banner in frontmatter for each article according to the theme requirements, then you can pass in the following ogp:

    seoPlugin({
    +  ogp: (ogp, page) => ({
    +    ...ogp,
    +    'og:image': page.frontmatter.banner || ogp['og:image'],
    +  }),
    +})
    +

    JSON-LD

    Like OGP, you can use the plugin options jsonLd to pass in a function to modify the default JSON-LD object to your needs and return it.

    function jsonLd(
    +  /** JSON-LD Object inferred by plugin */
    +  jsonLD: ArticleSchema | BlogPostingSchema | WebPageSchema,
    +  /** Page Object */
    +  page: Page,
    +  /** VuePress App */
    +  app: App,
    +): ArticleSchema | BlogPostingSchema | WebPageSchema
    +

    If you are deploying your content to different sites, or same content under different URLs, you may need to set canonical option to provide a "Canonical Link" for your page. You can either set a string which will be appended before page route link, or adding a custom function (page: Page) => string | null to return a canonical link if necessary.

    Example

    If your sites are deployed under docs directory in example.com, but available in:

    • http://example.com/docs/xxx
    • https://example.com/docs/xxx
    • http://www.example.com/docs/xxx
    • https://www.example.com/docs/xxx (primary)

    To let search engine results always be the primary choice, you may need to set canonical to https://www.example.com/docs/, so that search engine will know that the fourth URL is preferred to be indexed.

    Customize head Tags

    Sometimes you may need to fit other protocols or provide the corresponding SEO tags in the format provided by other search engines. In this case, you can use the customHead option, whose type is:

    function customHead(
    +  /** Head tag config */
    +  head: HeadConfig[],
    +  /** Page Object */
    +  page: Page,
    +  /** VuePress App */
    +  app: App,
    +): void
    +

    You should modify the head array in this function directly.

    SEO Introduction

    Search engine optimization (SEO) is the process of improving the quality and quantity of site traffic to a site or a web page from search engines. SEO targets unpaid traffic (known as "natural" or "organic" results) rather than direct traffic or paid traffic. Unpaid traffic may originate from different kinds of searches, including image search, video search, academic search, news search, and industry-specific vertical search engines.

    As an internet marketing strategy, SEO considers how search engines work, the computer-programmed algorithms that dictate search engine behavior, what people search for, the actual search terms or keywords typed into search engines, and which search engines are preferred by their targeted audience. SEO is performed because a site will receive more visitors from a search engine when sites rank higher on the search engine results page (SERP). These visitors can then potentially be converted into customers.

    `,16),f={href:"https://ogp.me/",target:"_blank",rel:"noopener noreferrer"},b=e("strong",null,"O",-1),v=e("strong",null,"G",-1),x=e("strong",null,"Pr",-1),C=e("p",null,[t("This plugin perfectly supports this protocol and will automatically generate "),e("code",null,""),t(" tags that conform to the protocol.")],-1),k={href:"https://www.w3.org/TR/json-ld-api/",target:"_blank",rel:"noopener noreferrer"},E=e("p",null,'This plugin will generate "NewsArticle" scheme for article pages.',-1),_={href:"https://www.w3.org/TR/rdfa-primer/",target:"_blank",rel:"noopener noreferrer"},w={href:"https://schema.org/",target:"_blank",rel:"noopener noreferrer"},O=e("p",null,"Schema definition site for structural markup",-1),S=e("h2",{id:"related-tools",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#related-tools"},[e("span",null,"Related Tools")])],-1),P={href:"https://search.google.com/test/rich-results",target:"_blank",rel:"noopener noreferrer"};function T(A,j){const a=s("ExternalLinkIcon"),i=s("RouteLink"),r=s("ProjectLink");return d(),p("div",null,[u,e("p",null,[t("This plugin will make your site fully support "),e("a",h,[t("Open Content Protocol OGP"),n(a)]),t(" and "),e("a",y,[t("JSON-LD 1.1"),n(a)]),t(" to enhance the SEO of the site.")]),m,e("p",null,[t("For detailed parameter structure, see "),n(i,{to:"/plugins/seo/config.html"},{default:o(()=>[t("Config")]),_:1}),t(".")]),D,e("ul",null,[e("li",null,[e("p",null,[e("a",f,[t("Open Content Protocol OGP"),n(a)]),t(" ("),b,t("pen "),v,t("raph "),x,t("otocol)")]),C]),e("li",null,[e("p",null,[e("a",k,[t("JSON-LD 1.1"),n(a)])]),E]),e("li",null,[e("p",null,[e("a",_,[t("RDFa 1.1"),n(a)])]),e("p",null,[t("RDFa mainly marks HTML structure. This is what the plugin cannot support. "),n(r,{type:"theme",name:"hope"},{default:o(()=>[t("vuepress-theme-hope")]),_:1}),t(" uses this feature to pass Google's rich media structure test. You can consider using it.")])]),e("li",null,[e("p",null,[e("a",w,[t("Schema.Org"),n(a)])]),O])]),S,e("p",null,[t("You can use "),e("a",P,[t("Google Rich Media Structure Test Tool"),n(a)]),t(" to test this site.")])])}const L=c(g,[["render",T],["__file","guide.html.vue"]]),q=JSON.parse('{"path":"/plugins/seo/guide.html","title":"Guide","lang":"en-US","frontmatter":{},"headers":[{"level":2,"title":"Out of Box","slug":"out-of-box","link":"#out-of-box","children":[{"level":3,"title":"Default OGP Generation","slug":"default-ogp-generation","link":"#default-ogp-generation","children":[]},{"level":3,"title":"Default JSON-LD Generation","slug":"default-json-ld-generation","link":"#default-json-ld-generation","children":[]}]},{"level":2,"title":"Setting Tags Directly","slug":"setting-tags-directly","link":"#setting-tags-directly","children":[]},{"level":2,"title":"Customize Generation","slug":"customize-generation","link":"#customize-generation","children":[{"level":3,"title":"Page Type","slug":"page-type","link":"#page-type","children":[]},{"level":3,"title":"OGP","slug":"ogp","link":"#ogp","children":[]},{"level":3,"title":"JSON-LD","slug":"json-ld","link":"#json-ld","children":[]}]},{"level":2,"title":"Canonical Link","slug":"canonical-link","link":"#canonical-link","children":[{"level":3,"title":"Customize head Tags","slug":"customize-head-tags","link":"#customize-head-tags","children":[]}]},{"level":2,"title":"SEO Introduction","slug":"seo-introduction","link":"#seo-introduction","children":[]},{"level":2,"title":"Related Documents","slug":"related-documents","link":"#related-documents","children":[]},{"level":2,"title":"Related Tools","slug":"related-tools","link":"#related-tools","children":[]}],"git":{"updatedTime":1706762763000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":3}]},"filePathRelative":"plugins/seo/guide.md"}');export{L as comp,q as data}; diff --git a/assets/guide.html-BXx1nNKq.js b/assets/guide.html-BXx1nNKq.js new file mode 100644 index 0000000000..01dab8ca7f --- /dev/null +++ b/assets/guide.html-BXx1nNKq.js @@ -0,0 +1 @@ +import{_ as s,r as a,o as r,c as i,b as e,d as t,a as l,w as n,e as c}from"./app-DI49vYHA.js";const h={},p=c('

    指南

    使用

    插件可为你生成以下三种格式的 feed 文件:

    • Atom 1.0
    • JSON 1.1
    • RSS 2.0

    请按照需要生成的格式,在插件选项中设置 atom, jsonrsstrue

    为了正确生成 Feed 链接,你需要在插件选项中设置 hostname

    可读的预览

    ',7),u={href:"/zh/atom.xml",target:"_blank",rel:"noopener noreferrer"},_={href:"/zh/rss.xml",target:"_blank",rel:"noopener noreferrer"},f=c('

    如果你想在开发服务器中预览 Feed,你需要在插件选项中设置 devServer: true。如果你没有使用默认的 http://localhost:{port},你还需要设置 devHostname

    频道设置

    你可以通过设置 channel 选项来自自定义 Feed 频道的各项信息。

    我们推荐进行如下设置:

    • 将建立 Feed 的日期转换为 ISOString 写入到 channel.pubDate
    • 通过 channel.ttl 中设置内容的更新周期(单位: 分钟)
    • 通过 channel.copyright 设置版权信息
    • 通过 channel.author 设置频道作者。
    ',5),m=e("h2",{id:"feed-生成",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#feed-生成"},[e("span",null,"Feed 生成")])],-1),g=e("p",null,"默认情况下,所有文章均会被添加至 feed 流。",-1),x=e("code",null,"feed",-1),k=e("code",null,"getter",-1),v=e("h3",{id:"多语言配置",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#多语言配置"},[e("span",null,"多语言配置")])],-1),F=e("p",null,"插件会针对每个语言生成单独的 Feed。",-1),b=e("p",null,[t("你可以通过插件选项中的 "),e("code",null,"locales"),t(" 分别对不同语言提供不同的默认设置。")],-1);function z(S,N){const d=a("ExternalLinkIcon"),o=a("RouteLink");return r(),i("div",null,[p,e("p",null,[t("当你在浏览器中打开 Feed 文件时,我们会通过 xsl 模板将 atom 和 rss feed xml 魔法般地转换为可读的 html。你可以查看本站的 "),e("a",u,[t("atom"),l(d)]),t(" 和 "),e("a",_,[t("rss"),l(d)]),t(" feed 作为案例!")]),f,e("p",null,[t("详细的选项及其默认值详见 "),l(o,{to:"/zh/plugins/feed/channel.html"},{default:n(()=>[t("配置 → 频道设置")]),_:1})]),m,g,e("p",null,[t("你可以在 frontmatter 中配置 "),x,t(" 和其他选项控制每个页面的 Feed 项目内容,详见 "),l(o,{to:"/zh/plugins/feed/frontmatter.html"},{default:n(()=>[t("Frontmatter 选项")]),_:1}),t(" 了解它们如何被转换。")]),e("p",null,[t("你可以通过配置插件选项中的 "),k,t(" 完全控制 Feed 项目的生成逻辑。 详细的选项及其默认值详见 "),l(o,{to:"/zh/plugins/feed/getter.html"},{default:n(()=>[t("配置 → Feed 获取器")]),_:1})]),v,F,b])}const R=s(h,[["render",z],["__file","guide.html.vue"]]),V=JSON.parse('{"path":"/zh/plugins/feed/guide.html","title":"指南","lang":"zh-CN","frontmatter":{},"headers":[{"level":2,"title":"使用","slug":"使用","link":"#使用","children":[]},{"level":2,"title":"可读的预览","slug":"可读的预览","link":"#可读的预览","children":[]},{"level":2,"title":"频道设置","slug":"频道设置","link":"#频道设置","children":[]},{"level":2,"title":"Feed 生成","slug":"feed-生成","link":"#feed-生成","children":[{"level":3,"title":"多语言配置","slug":"多语言配置","link":"#多语言配置","children":[]}]}],"git":{"updatedTime":1706674769000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":1}]},"filePathRelative":"zh/plugins/feed/guide.md"}');export{R as comp,V as data}; diff --git a/assets/guide.html-BguhT2bm.js b/assets/guide.html-BguhT2bm.js new file mode 100644 index 0000000000..847d593e33 --- /dev/null +++ b/assets/guide.html-BguhT2bm.js @@ -0,0 +1 @@ +import{_ as s,r as l,o as d,c,b as e,d as t,a as n,w as a,e as r}from"./app-DI49vYHA.js";const h={},u=r('

    Guide

    Usage

    The plugin can generate feed files in the following three formats for you:

    • Atom 1.0
    • JSON 1.1
    • RSS 2.0

    Please set atom, json or rss to true in the plugin options according to the formats you want to generate.

    To correctly generate feed links, you need to set hostname in the plugin options,

    Readable Preview

    ',7),f={href:"/atom.xml",target:"_blank",rel:"noopener noreferrer"},p={href:"/rss.xml",target:"_blank",rel:"noopener noreferrer"},g=r('

    If you want to preview your feed in devServer, set devServer: true in plugin options. You may also need to set devHostname if you are not using the default http://localhost:{port}.

    Channel settings

    You can customize the feed channel information by setting the channel option.

    We recommend the following settings:

    • Convert the date of creating the feed to ISOString and write it into channel.pubDate
    • The update period of the content set in channel.ttl (unit: minutes)
    • Set copyright information via channel.copyright
    • Set the channel author via channel.author.
    ',5),m=e("h2",{id:"feed-generation",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#feed-generation"},[e("span",null,"Feed Generation")])],-1),_=e("p",null,"By default, all articles are added to the feed stream.",-1),v=e("code",null,"feed",-1),b=e("code",null,"getter",-1),x=e("h3",{id:"i18n-config",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#i18n-config"},[e("span",null,"I18n Config")])],-1),k=e("p",null,"The plugin generates separate feeds for each language.",-1),w=e("p",null,[t("You can provide different settings for different languages via "),e("code",null,"locales"),t(" in the plugin options.")],-1);function y(S,C){const i=l("ExternalLinkIcon"),o=l("RouteLink");return d(),c("div",null,[u,e("p",null,[t("When you open the feed file in browser, we magically convert atom and rss feed xml to human readable html via xsl template. Check "),e("a",f,[t("atom"),n(i)]),t(" and "),e("a",p,[t("rss"),n(i)]),t(" feed of this site as an example!")]),g,e("p",null,[t("For detailed options and their default values, see "),n(o,{to:"/plugins/feed/channel.html"},{default:a(()=>[t("Channel Config")]),_:1})]),m,_,e("p",null,[t("You can set "),v,t(" and other options in page frontmatter to control contents of feed item. See "),n(o,{to:"/plugins/feed/frontmatter.html"},{default:a(()=>[t("Frontmatter Config")]),_:1}),t(" for how they are converted.")]),e("p",null,[t("You can take full control of feed items generation by configuring the "),b,t(" in the plugin options. For detailed options and their default values, see "),n(o,{to:"/plugins/feed/getter.html"},{default:a(()=>[t("Configuration → Feed Getter")]),_:1}),t(".")]),x,k,w])}const I=s(h,[["render",y],["__file","guide.html.vue"]]),N=JSON.parse('{"path":"/plugins/feed/guide.html","title":"Guide","lang":"en-US","frontmatter":{},"headers":[{"level":2,"title":"Usage","slug":"usage","link":"#usage","children":[]},{"level":2,"title":"Readable Preview","slug":"readable-preview","link":"#readable-preview","children":[]},{"level":2,"title":"Channel settings","slug":"channel-settings","link":"#channel-settings","children":[]},{"level":2,"title":"Feed Generation","slug":"feed-generation","link":"#feed-generation","children":[{"level":3,"title":"I18n Config","slug":"i18n-config","link":"#i18n-config","children":[]}]}],"git":{"updatedTime":1706674769000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":1}]},"filePathRelative":"plugins/feed/guide.md"}');export{I as comp,N as data}; diff --git a/assets/guide.html-Bnwyqjr4.js b/assets/guide.html-Bnwyqjr4.js new file mode 100644 index 0000000000..5f071f2aa4 --- /dev/null +++ b/assets/guide.html-Bnwyqjr4.js @@ -0,0 +1,9 @@ +import{_ as e,o as t,c as a,e as s}from"./app-DI49vYHA.js";const i={},o=s(`

    Guide

    This plugin will automatically generate a Sitemap for your site. To let this plugin work, you need to pass the deployed domain name to the hostname option of the plugin. If you want to preview in devServer, set devServer options.

    The plugin will automatically generate the last update time of the page based on the Git timestamp of the page, and will also declare the alternative links of the page in other languages according to the locales' config.

    By default, all site links except 404 page will be added to the Sitemap.

    To add other pages to the Sitemap outside the VuePress project page, please turn them into an array and pass to the extraUrls plugin option.

    If you don't want certain pages to appear in the sitemap, you can turn their paths into an array and pass to the excludePaths plugin option, or set sitemap to false in the frontmatter of the corresponding page.

    Output Location

    You can also control the output link through the sitemapFilename option of the plugin, the link is relative to output directory. By default, the plugin will use sitemap.xml.

    Change Frequency

    The default update cycle of the page is daily (every day). To modify the entire page cycle, please set changefreq in the plugin options. You can also set sitemap.changefreq in the frontmatter of the page. Note that page has a higher priority.

    The legal frequencies are:

    • "always"
    • "hourly"
    • "daily"
    • "weekly"
    • "monthly"
    • "yearly"
    • "never"

    Priority

    You can set priority in the plugin to provide a default value. At the same time you can set the priority for each page through sitemap.priority in frontmatter. Acceptable values are floating point numbers from 0 to 1.

    Modify Time

    You can use option modifyTimeGetter to return a time in ISO string format, which is generated by the Git plugin by default.

    The following is an example based on the last modification time of a file.

    // Based on file last modified time
    +({
    +   modifyTimeGetter: (page, app) =>
    +     fs.statSync(app.dir.source(page.filePathRelative)).mtime.toISOString();
    +})
    +

    Sitemap Intro

    Sitemaps provide SEO (Search Engine Optimization):

    • Provide search engine spiders with links of the entire site;
    • Provide some links for search engine spiders to dynamic pages or pages that are difficult to reach by other methods;
    • If a visitor attempts to access a URL that does not exist within the site's domain, the visitor will be directed to a "file not found" error page, and the sitemap can be used as a navigation page.

    A sitemap enhances SEO by making all pages findable.

    Most search engines only follow a limited number of links within a page, so when the site is very large, a sitemap becomes essential to make everything on the site accessible to search engines and visitors.

    Sitemaps is a protocol for site administrators to publish pages that can be crawled on a site to search engine spiders. The content of sitemap files must follow the definition in XML format. Each URL can contain the update period and last update time, the priority of the URL across the site. This allows search engines to crawl site content better and more efficiently.

    Together with robots.txt

    Sitemap is basically used by search engines, when using this plugin, you'd better ensure that you have a valid robots.txt in the .vuepress/public directory to allow search engines spiders to visit your site. The simplest robots.txt is as follows (allow all search engines to access all paths)

    User-agent: *
    +
    +Allow: /
    +
    `,26),n=[o];function l(p,r){return t(),a("div",null,n)}const d=e(i,[["render",l],["__file","guide.html.vue"]]),h=JSON.parse('{"path":"/plugins/sitemap/guide.html","title":"Guide","lang":"en-US","frontmatter":{},"headers":[{"level":2,"title":"Control Sitemap Link","slug":"control-sitemap-link","link":"#control-sitemap-link","children":[]},{"level":2,"title":"Output Location","slug":"output-location","link":"#output-location","children":[]},{"level":2,"title":"Change Frequency","slug":"change-frequency","link":"#change-frequency","children":[]},{"level":2,"title":"Priority","slug":"priority","link":"#priority","children":[]},{"level":2,"title":"Modify Time","slug":"modify-time","link":"#modify-time","children":[]},{"level":2,"title":"Sitemap Intro","slug":"sitemap-intro","link":"#sitemap-intro","children":[]}],"git":{"updatedTime":1706723991000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":2}]},"filePathRelative":"plugins/sitemap/guide.md"}');export{d as comp,h as data}; diff --git a/assets/guide.html-DMncVq9E.js b/assets/guide.html-DMncVq9E.js new file mode 100644 index 0000000000..3744cc4c60 --- /dev/null +++ b/assets/guide.html-DMncVq9E.js @@ -0,0 +1,215 @@ +import{_ as e,r as t,o as c,c as r,b as o,d as s,a,w as l,e as p}from"./app-DI49vYHA.js";const D={},i=p(`

    使用 @vuepress/plugin-blog,你可以轻松地将博客功能引入主题。

    收集文章并生成信息

    起步时,插件会首选过滤并选择那些需要作为文章的页面。这将剔除你不想要的页面,并在后续处理中排除它们。

    默认情况下,所有从 Markdown 文件生成但不是主页的页面,都将被视作文章。

    你可能需要设置 filter 选项来完全自定义要收集的页面。 filter 接受一个形状为 (page: Page) => boolean 的函数。

    接着,你应该设置 getInfo 选项为一个接受 Page 作为参数并返回包含所需信息的对象的函数。这样稍后,你可以从组合 API 中获取这些信息。

    案例
    import { blogPlugin } from '@vuepress/plugin-blog'
    +
    +export default {
    +  name: 'vuepress-theme-xxx',
    +  plugins: [
    +    blogPlugin({
    +      filter: ({ filePathRelative, frontmatter }) => {
    +        // 舍弃那些不是从 Markdown 文件生成的页面
    +        if (!filePathRelative) return false
    +
    +        // 舍弃 \`archives\` 文件夹的页面
    +        if (filePathRelative.startsWith('archives/')) return false
    +
    +        // 舍弃那些没有使用默认布局的页面
    +        if (frontmatter.home || frontmatter.layout) return false
    +
    +        return true
    +      },
    +
    +      getInfo: ({ frontmatter, git = {}, data = {} }) => {
    +        // 获取页面信息
    +        const info: Record<string, any> = {
    +          author: frontmatter.author || '',
    +          categories: frontmatter.categories || [],
    +          date: frontmatter.date || git.createdTime || null,
    +          tags: frontmatter.tags || [],
    +          excerpt: data.excerpt || '',
    +        }
    +
    +        return info
    +      },
    +    }),
    +    // 其他插件 ...
    +  ],
    +}
    +

    自定义类别和类型

    基本上,你的博客中需要两种“类型”:

    • 类别:

      “类别”是用文章的标签 (或类别) 对它们进行分组。

      例如,每篇文章可能都有对应的“分类”和“标签”。

    • 类型:

      “类型”是过滤不同条件的文章。

      例如,你的帖子中可能有日记或笔记。当帖子带有写作日期信息时,它可以称为“时间线项目”。

    了解这两种类型的描述后,你可以设置 categorytype 选项,它们都接受一个数组,每个元素代表一个配置。

    让我们从此处 2 个例子开始。

    假设你想为每篇文章设置标签,并且你正在通过 frontmatter.tag 设置它们。同时,你想要在 /tag/ 中使用 TagMap 布局的标签页面,并在/tag/标签名称 中使用 TagList 布局对标签按名称进行分组,你可能需要这样的配置:

    import { blogPlugin } from '@vuepress/plugin-blog'
    +
    +export default {
    +  name: 'vuepress-theme-xxx',
    +  plugins: [
    +    blogPlugin({
    +      // 其他配置 ...
    +      category: [
    +        {
    +          key: 'tag',
    +          getter: ({ frontmatter }) => frontmatter.tag || [],
    +          path: '/tag/',
    +          layout: 'TagMap',
    +          frontmatter: () => ({ title: '标签页' }),
    +          itemPath: '/tag/:name/',
    +          itemLayout: 'TagList',
    +          itemFrontmatter: (name) => ({ title: \`\${name}标签\` }),
    +        },
    +      ],
    +    }),
    +    // 其他插件 ...
    +  ],
    +}
    +

    此外,你可能希望为你的一些文章加注星标,并将其展示给访问者。当你在 frontmatter 中设置 star: true 来标记它们时,你可能需要这样的配置来在 /star/ 路径中以 StarList 布局显示它们:

    import { blogPlugin } from '@vuepress/plugin-blog'
    +
    +export default {
    +  name: 'vuepress-theme-xxx',
    +  plugins: [
    +    blogPlugin({
    +      // 其他配置 ...
    +      type: [
    +        {
    +          key: 'star',
    +          filter: ({ frontmatter }) => frontmatter.star,
    +          path: '/star/',
    +          layout: 'StarList',
    +          frontmatter: () => ({ title: '星标文章' }),
    +        },
    +      ],
    +    }),
    +    // 其他插件 ...
    +  ],
    +}
    +
    `,16),y=p(`

    在客户端使用组合 API

    当生成每个页面时,插件将在 frontmatter.blog 中设置如下信息

    interface BlogFrontmatterOptions {
    +  /** 当前页面的类型 */
    +  type: 'category' | 'type'
    +  /** 在当前分类或类别下全局唯一的 key */
    +  key: string
    +  /**
    +   * 当前的分类名称
    +   *
    +   * @description 仅在分类子项目页面中可用
    +   */
    +  name?: string
    +}
    +

    所以你可以直接调用 useBlogCategory()useBlogType(),结果将是当前路由绑定的类别或类型。

    此外,你可以通过传递所需的 key 作为参数,来将获得绑定到该 key 的信息。

    对于上方的 Node 配置而言,你可以在客户端通过如下方式获取 tag 和 star 的信息:

    TagMap 布局:

    <template>
    +  <div>
    +    <h1>Tag page</h1>
    +    <ul>
    +      <li v-for="({ items, path }, name) in categoryMap.map">
    +        <RouteLink :key="name" :to="path" class="category">
    +          {{ name }}
    +          <span class="category-num">
    +            {{ items.length }}
    +          </span>
    +        </RouteLink>
    +      </li>
    +    </ul>
    +  </div>
    +</template>
    +<script setup lang="ts">
    +import { useBlogCategory } from '@vuepress/plugin-blog'
    +import { RouteLink } from 'vuepress/client'
    +
    +const categoryMap = useBlogCategory('tag')
    +</script>
    +

    TagList 布局:

    <template>
    +  <div>
    +    <h1>Tag page</h1>
    +    <div class="category-wrapper">
    +      <RouteLink
    +        v-for="({ items, path }, name) in categoryMap.map"
    +        :key="name"
    +        :to="path"
    +        class="category"
    +      >
    +        {{ name }}
    +        <span class="category-num">
    +          {{ items.length }}
    +        </span>
    +      </RouteLink>
    +    </div>
    +    <div class="article-wrapper" v-if="categoryMap.currentItems">
    +      <div v-if="!categoryMap.currentItems.length">Nothing in here.</div>
    +      <article
    +        v-for="{ info, path } in categoryMap.currentItems"
    +        class="article"
    +        @click="$router.push(path)"
    +      >
    +        <header class="title">
    +          {{
    +            (isTimeline
    +              ? \`\${new Date(info.date).toLocaleDateString()}: \`
    +              : '') + info.title
    +          }}
    +        </header>
    +        <hr />
    +        <div class="article-info">
    +          <span v-if="info.author" class="author"
    +            >Author: {{ info.author }}</span
    +          >
    +          <span v-if="info.date && !isTimeline" class="date"
    +            >Date: {{ new Date(info.date).toLocaleDateString() }}</span
    +          >
    +          <span v-if="info.category" class="category"
    +            >Category: {{ info.category.join(', ') }}</span
    +          >
    +          <span v-if="info.tag" class="tag"
    +            >Tag: {{ info.tag.join(', ') }}</span
    +          >
    +        </div>
    +        <div v-if="info.excerpt" class="excerpt" v-html="info.excerpt" />
    +      </article>
    +    </div>
    +  </div>
    +</template>
    +<script setup lang="ts">
    +import { useBlogCategory } from '@vuepress/plugin-blog'
    +import { RouteLink } from 'vuepress/client'
    +
    +const categoryMap = useBlogCategory('tag')
    +</script>
    +

    StarList 布局:

    <template>
    +  <div class="article-wrapper" v-if="stars.items">
    +    <div v-if="!stars.items.length">Nothing in here.</div>
    +    <article
    +      v-for="{ info, path } in stars.items"
    +      class="article"
    +      @click="$router.push(path)"
    +    >
    +      <header class="title">
    +        {{
    +          (isTimeline ? \`\${new Date(info.date).toLocaleDateString()}: \` : '') +
    +          info.title
    +        }}
    +      </header>
    +      <hr />
    +      <div class="article-info">
    +        <span v-if="info.author" class="author">Author: {{ info.author }}</span>
    +        <span v-if="info.date && !isTimeline" class="date"
    +          >Date: {{ new Date(info.date).toLocaleDateString() }}</span
    +        >
    +        <span v-if="info.category" class="category"
    +          >Category: {{ info.category.join(', ') }}</span
    +        >
    +        <span v-if="info.tag" class="tag">Tag: {{ info.tag.join(', ') }}</span>
    +      </div>
    +      <div v-if="info.excerpt" class="excerpt" v-html="info.excerpt" />
    +    </article>
    +  </div>
    +</template>
    +<script setup lang="ts">
    +import { useBlogType } from '@vuepress/plugin-blog/client'
    +
    +import ArticleList from '../components/ArticleList.vue'
    +import ParentLayout from '@vuepress/theme-default/layouts/Layout.vue'
    +
    +const stars = useBlogType('star')
    +</script>
    +
    `,12),C=p(`

    多语言支持

    该插件添加了原生多语言支持,因此你的设置将自动应用于每种语言。

    例如,如果用户进行了以下 locales 配置,并且你正在设置上面的“star”示例:

    export default {
    +  locales: {
    +    '/': {
    +      lang: 'en-US',
    +    },
    +    '/zh/': {
    +      lang: 'zh-CN',
    +    },
    +  },
    +}
    +

    那么 /zh/star//star/ 都将可用,并且只会显示对应语言下的文章。

    摘要生成

    这个插件提供了一个内置的摘要生成器,可以通过将 excerpt 选项设置为 true 来启用。

    摘要介绍

    摘要是一个 HTML 片段,被用于在博客列表中显示文章的简短描述,所以摘要有如下限制:

    • 摘要不支持任何未知标签以及 Vue 语法,所以此类内容会在生成时被移除。如果你有自定义组件 (非 Vue 组件),请配置 isCustomElement 选项。
    • 由于摘要是一个 HTML 片段,所以你将无法通过相对路径或别名引入任何图片,这些图片会被直接移除。如果你想要保留图片,请使用基于 .vuepress/public 的绝对路径或完整路径以确保它们可以在其他地址被访问。

    摘要生成器将尝试从 Frontmatter 内容中找到有效的摘要分隔符,如果找到,它将使用分隔符之前的内容,分隔符默认为 <!-- more -->,并且你可以通过 excerptSeparator 选项来自定义它。

    如果找不到有效的分隔符,它将从 Markdown 文件的开头开始解析内容,直到长度达到预设值时停止。该值默认为 300,你可以通过设置 excerptLength 选项来自定义它。

    要选择哪个页面应该生成摘要,你可以使用 excerptFilter 选项。

    示例

    通常,如果用户设置了 frontmatter.description,你可能希望使用它们,因此如果 frontmatter.description 不为空,你可以让过滤器函数返回 false

    `,12);function d(v,u){const n=t("RouteLink");return c(),r("div",null,[i,o("p",null,[s("看,设置这两种类型很容易。有关完整选项,请参阅 "),a(n,{to:"/zh/plugins/blog/config.html#%E5%8D%9A%E5%AE%A2%E5%88%86%E7%B1%BB%E9%85%8D%E7%BD%AE"},{default:l(()=>[s("博客分类配置")]),_:1}),s(" 和 "),a(n,{to:"/zh/plugins/blog/config.html#%E5%8D%9A%E5%AE%A2%E7%B1%BB%E5%9E%8B%E9%85%8D%E7%BD%AE"},{default:l(()=>[s("博客分类配置")]),_:1}),s("。")]),y,o("p",null,[s("有关返回类型,请参阅 "),a(n,{to:"/zh/plugins/blog/config.html#%E5%8F%AF%E7%BB%84%E5%90%88%E5%BC%8F-API"},{default:l(()=>[s("Composition API 返回类型")]),_:1}),s("。")]),C])}const E=e(D,[["render",d],["__file","guide.html.vue"]]),b=JSON.parse('{"path":"/zh/plugins/blog/guide.html","title":"指南","lang":"zh-CN","frontmatter":{"title":"指南","icon":"lightbulb"},"headers":[{"level":2,"title":"收集文章并生成信息","slug":"收集文章并生成信息","link":"#收集文章并生成信息","children":[]},{"level":2,"title":"自定义类别和类型","slug":"自定义类别和类型","link":"#自定义类别和类型","children":[]},{"level":2,"title":"在客户端使用组合 API","slug":"在客户端使用组合-api","link":"#在客户端使用组合-api","children":[]},{"level":2,"title":"多语言支持","slug":"多语言支持","link":"#多语言支持","children":[]},{"level":2,"title":"摘要生成","slug":"摘要生成","link":"#摘要生成","children":[]}],"git":{"updatedTime":1708365679000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":2}]},"filePathRelative":"zh/plugins/blog/guide.md"}');export{E as comp,b as data}; diff --git a/assets/guide.html-Dt9xe1YY.js b/assets/guide.html-Dt9xe1YY.js new file mode 100644 index 0000000000..3a752f07c2 --- /dev/null +++ b/assets/guide.html-Dt9xe1YY.js @@ -0,0 +1,37 @@ +import{_ as p,r as a,o as r,c as i,b as e,d as s,a as n,w as l,e as o}from"./app-DI49vYHA.js";const D={},g=e("h1",{id:"指南",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#指南"},[e("span",null,"指南")])],-1),h=e("code",null,"",-1),y={href:"https://ogp.me/",target:"_blank",rel:"noopener noreferrer"},u={href:"https://www.w3.org/TR/json-ld-api/",target:"_blank",rel:"noopener noreferrer"},m=o(`

    开箱即用

    插件开箱即用,在不做任何配置的情况下,会尽可能通过页面内容,提取对应的信息补全 OGP 与 JSON-LD 所需的必要标签。

    默认情况下,插件会读取站点配置、主题配置与页面的 frontmatter 来尽可能自动生成。诸如站点名称,页面标题,页面类型,写作日期,最后更新日期,文章标签均会自动生成。

    默认的 OGP 生成逻辑

    属性名称
    og:urloptions.hostname + path
    og:site_namesiteConfig.title
    og:titlepage.title
    og:descriptionpage.frontmatter.description || 自动生成 (当插件选项中的 autoDescriptiontrue 时)
    og:type"article"
    og:imageoptions.hostname + page.frontmatter.image || 页面的第一张图片|| 插件选项的 fallbackImage
    og:updated_timepage.git.updatedTime
    og:localepage.lang
    og:locale:alternatesiteData.locales 包含的其他语言
    twitter:card"summary_large_image" (仅在找到图片时)
    twitter:image:altpage.title (仅在找到图片时)
    article:authorpage.frontmatter.author || options.author
    article:tagpage.frontmatter.tags || page.frontmatter.tag
    article:published_timepage.frontmatter.date || page.git.createdTime
    article:modified_timepage.git.updatedTime

    默认的 JSON-LD 生成逻辑

    属性名
    @context"https://schema.org"
    @type"NewsArticle"
    headlinepage.title
    image页面中的图片|| options.hostname + page.frontmatter.image
    datePublishedpage.frontmatter.date || page.git.createdTime
    dateModifiedpage.git.updatedTime
    authorpage.frontmatter.author || options.author

    直接添加 head 标签

    你可以在页面的 frontmatter 中配置 head 选项,自主添加特定标签到页面 <head> 以增强 SEO。

    如:

    ---
    +head:
    +  - - meta
    +    - name: keywords
    +      content: SEO plugin
    +---
    +

    会自动注入 <meta name="keywords" content="SEO plugin" />

    自定义生成过程

    本插件也支持你完全控制生成逻辑。

    页面类型

    对于大多数页面,基本只有文章和网页两种类型,所以插件提供了 isArticle 选项让你提供辨别文章的逻辑。

    选项接受一个 (page: Page) => boolean 格式的函数,默认情况下从 Markdown 文件生成的非主页页面都会被视为文章。

    提示

    如果某个网页的确符合图书、音乐之类的“冷门”类型,你可以通过设置下方三个选项处理它们。

    OGP

    你可以使用插件选项的 ogp 传入一个函数来按照你的需要修改默认 OGP 对象并返回。

    function ogp(
    +  /** 插件推断的 OGP 信息 */
    +  ogp: SeoContent,
    +  /** 页面对象 */
    +  page: Page,
    +  /** VuePress App */
    +  app: App,
    +): SeoContent
    +
    `,21),v=o(`

    比如你在使用某个第三方主题,并按照主题要求为每篇文章在 Front Matter 中设置了 banner,那你可以传入这样的 ogp:

    seoPlugin({
    +  ogp: (ogp, page) => ({
    +    ...ogp,
    +    'og:image': page.frontmatter.banner || ogp['og:image'],
    +  }),
    +})
    +

    JSON-LD

    同 OGP,你可以使用插件选项的 jsonLd 传入一个函数来按照你的需要修改默认 JSON-LD 对象并返回。

    function jsonLd(
    +  /** 由插件推断出的 JSON-LD 对象 */
    +  jsonLD: ArticleSchema | BlogPostingSchema | WebPageSchema,
    +  /** 页面对象 */
    +  page: Page,
    +  /** VuePress App */
    +  app: App,
    +): ArticleSchema | BlogPostingSchema | WebPageSchema
    +

    规范链接

    如果你将内容部署到不同的站点,或不同 URL 下的相同内容,你可能需要设置 canonical 选项为你的页面提供 “规范链接”。 你可以设置一个字符串,这样它会附加在页面路由链接之前,或者添加一个自定义函数 (page: Page) => string | null 返回规范链接。

    例子

    如果你的站点部署在 example.com 的 docs 文件夹下,但同时在下列网址中可用:

    • http://example.com/docs/xxx
    • https://example.com/docs/xxx
    • http://www.example.com/docs/xxx
    • https://www.example.com/docs/xxx (首选)

    要让搜索引擎结果始终是首选,你可能需要将 canonical 设置为 https://www.example.com/docs/,以便搜索引擎知道首选第四个 URL 作为索引结果。

    自定义 head 标签

    有些时候你可能需要符合其他协议或按照其他搜索引擎提供的格式提供对应的 SEO 标签,此时你可以使用 customHead 选项,其类型为:

    function customHead(
    +  /** head 标签配置 */
    +  head: HeadConfig[],
    +  /** 页面对象 */
    +  page: Page,
    +  /** VuePress App */
    +  app: App,
    +): void
    +

    你应该直接修改传入的 head 参数。

    SEO 介绍

    搜索引擎优化 (Search Engine Optimization),是一种透过了解搜索引擎的运作规则来调整网站,以及提高目的网站在有关搜索引擎内排名的方式。由于不少研究发现,搜索引擎的用户往往只会留意搜索结果最前面的几个条目,所以不少网站都希望透过各种形式来影响搜索引擎的排序,让自己的网站可以有优秀的搜索排名。 所谓“针对搜索引擎作最优化的处理”,是指为了要让网站更容易被搜索引擎接受。搜索引擎会将网站彼此间的内容做一些相关性的资料比对,然后再由浏览器将这些内容以最快速且接近最完整的方式,呈现给搜索者。搜索引擎优化就是通过搜索引擎的规则进行优化,为用户打造更好的用户体验,最终的目的就是做好用户体验。

    相关文档

    `,15),x={href:"https://ogp.me/",target:"_blank",rel:"noopener noreferrer"},b=e("strong",null,"O",-1),C=e("strong",null,"G",-1),_=e("strong",null,"Pr",-1),E=e("p",null,[s("本插件完美支持该协议,会自动生成符合该协议的 "),e("code",null,""),s(" 标签。")],-1),f={href:"https://www.w3.org/TR/json-ld-api/",target:"_blank",rel:"noopener noreferrer"},k=e("p",null,"本插件会为文章类页面生成 NewsArticle 类标签。",-1),w={href:"https://www.w3.org/TR/rdfa-primer/",target:"_blank",rel:"noopener noreferrer"},A={href:"https://schema.org/",target:"_blank",rel:"noopener noreferrer"},O=e("p",null,"结构标记的 Schema 定义站点",-1),P=e("h2",{id:"相关工具",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#相关工具"},[e("span",null,"相关工具")])],-1),S={href:"https://search.google.com/test/rich-results",target:"_blank",rel:"noopener noreferrer"};function B(F,L){const t=a("ExternalLinkIcon"),c=a("RouteLink"),d=a("ProjectLink");return r(),i("div",null,[g,e("p",null,[s("本插件会通过向网站 "),h,s(" 注入标签,让你的网站完全支持 "),e("a",y,[s("开放内容协议 OGP"),n(t)]),s(" 和 "),e("a",u,[s("JSON-LD 1.1"),n(t)]),s(",以全面增强站点的搜索引擎优化性。")]),m,e("p",null,[s("详细的参数结构详见 "),n(c,{to:"/zh/plugins/seo/config.html"},{default:l(()=>[s("配置")]),_:1}),s("。")]),v,e("ul",null,[e("li",null,[e("p",null,[e("a",x,[s("开放内容协议 OGP"),n(t)]),s(" ("),b,s("pen "),C,s("raph "),_,s("otocal)")]),E]),e("li",null,[e("p",null,[e("a",f,[s("JSON-LD 1.1"),n(t)])]),k]),e("li",null,[e("p",null,[e("a",w,[s("RDFa 1.1"),n(t)])]),e("p",null,[s("RDFa 主要标记 HTML 结构。这是插件无法支持的内容,"),n(d,{type:"theme",name:"hope",path:"/zh/"},{default:l(()=>[s("vuepress-theme-hope")]),_:1}),s(" 使用了这一功能通过了谷歌的富媒体结构测试。你可以考虑搭配使用。")])]),e("li",null,[e("p",null,[e("a",A,[s("Schema.Org"),n(t)])]),O])]),P,e("p",null,[s("你可以使用 "),e("a",S,[s("Google 富媒体结构测试工具"),n(t)]),s(" 测试本站点。")])])}const j=p(D,[["render",B],["__file","guide.html.vue"]]),q=JSON.parse('{"path":"/zh/plugins/seo/guide.html","title":"指南","lang":"zh-CN","frontmatter":{},"headers":[{"level":2,"title":"开箱即用","slug":"开箱即用","link":"#开箱即用","children":[{"level":3,"title":"默认的 OGP 生成逻辑","slug":"默认的-ogp-生成逻辑","link":"#默认的-ogp-生成逻辑","children":[]},{"level":3,"title":"默认的 JSON-LD 生成逻辑","slug":"默认的-json-ld-生成逻辑","link":"#默认的-json-ld-生成逻辑","children":[]}]},{"level":2,"title":"直接添加 head 标签","slug":"直接添加-head-标签","link":"#直接添加-head-标签","children":[]},{"level":2,"title":"自定义生成过程","slug":"自定义生成过程","link":"#自定义生成过程","children":[{"level":3,"title":"页面类型","slug":"页面类型","link":"#页面类型","children":[]},{"level":3,"title":"OGP","slug":"ogp","link":"#ogp","children":[]},{"level":3,"title":"JSON-LD","slug":"json-ld","link":"#json-ld","children":[]}]},{"level":2,"title":"规范链接","slug":"规范链接","link":"#规范链接","children":[{"level":3,"title":"自定义 head 标签","slug":"自定义-head-标签","link":"#自定义-head-标签","children":[]}]},{"level":2,"title":"SEO 介绍","slug":"seo-介绍","link":"#seo-介绍","children":[]},{"level":2,"title":"相关文档","slug":"相关文档","link":"#相关文档","children":[]},{"level":2,"title":"相关工具","slug":"相关工具","link":"#相关工具","children":[]}],"git":{"updatedTime":1706762763000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":3}]},"filePathRelative":"zh/plugins/seo/guide.md"}');export{j as comp,q as data}; diff --git a/assets/guide.html-I8tYpU3u.js b/assets/guide.html-I8tYpU3u.js new file mode 100644 index 0000000000..0d9227871c --- /dev/null +++ b/assets/guide.html-I8tYpU3u.js @@ -0,0 +1,23 @@ +import{_ as r,r as t,o as c,c as i,b as s,d as e,a as o,w as p,e as a}from"./app-DI49vYHA.js";const d={},u=a('

    指南

    介绍

    将你的 VuePress 站点变成渐进式网络应用程序 (PWA)[1]

    ',3),h={href:"https://developers.google.com/web/tools/workbox/modules/workbox-build",target:"_blank",rel:"noopener noreferrer"},f={href:"https://github.com/yyx990803/register-service-worker",target:"_blank",rel:"noopener noreferrer"},D={class:"custom-container warning"},y=s("p",{class:"custom-container-title"},"注意",-1),v=a('

    A PWA uses a Service Worker [2] (SW for short) to cache and proxy site content.

    一个 PWA 使用 Service Worker [2:1] (简称 SW) 来获取并托管网站内容。

    网络 App 清单

    ',3),g={href:"https://developer.mozilla.org/zh-CN/docs/Web/Manifest",target:"_blank",rel:"noopener noreferrer"},m=s("sup",{class:"footnote-ref"},[s("a",{href:"#footnote3"},"[3]"),s("a",{class:"footnote-anchor",id:"footnote-ref3"})],-1),_=a(`

    你可以通过设置 manifest 选项来自定义 manifest 文件,或者在 public 文件夹中提供 manifest.webmanifestmanifest.json。前者优先级更高。

    插件会自动为你生成 manifest.webmanifest,并在每个页面的 <head> 中添加清单链接声明,但是 你至少应该通过 manifest.icons 或 PWA 插件中的其他选项设置一个有效的图标。

    注意

    可安装性[3:1]规范要求 manifest 中至少声明一个有效的图标。

    所以如果你不配置 manifest.icons,访问者只能享受到 Service Worker 缓存带来的离线可访问性,而并不能作为 PWA 进行安装。

    此外,该插件默认不处理清单中的任何内容,而是按原样输出。 这意味着,如果你计划部署到子目录,则应自行将 URL 前缀附加到自己的清单 Urls 中。如果你需要的所有东西都在 base 文件夹下,你可以在插件选项中设置 appendBase: true 让插件将 base 自动附加到任何地址。

    缓存控制

    为了更好的控制 Service Worker 可以预缓存的内容,插件提供了相关的缓存控制选项。

    默认缓存

    默认情况下插件会预缓存所有的 JS 和 CSS 文件,但仅缓存主页和 404 页面的 HTML。插件同时还会缓存字体文件 (woff, woff2, eot, ttf, otf) 和 SVG 图标。

    图片缓存

    如果你的站点只有少量重要图片,并希望它们在离线模式下显示,你可以通过设置 cacheImage 选项为 true 来缓存站点图片。

    我们通过文件后缀名识别图片,任何以 .png, .jpg, .jpeg, .gif, .bmp, .webp 结尾的文件都会视为图片。

    HTML 缓存

    当你网站体积不大,并且希望文档完全离线可用时,你可以通过设置 cacheHTMLtrue 来缓存所有 HTML 页面。

    为什么默认不缓存非主页和 404 页面

    虽然说 VuePress 为所有的页面通过 SSG[4] 生成了 HTML 文件,但是这些文件主要用于 SEO[5],并能够让你在后端不做 SPA[6] 配置的情况下能够直接访问任何链接。

    VuePress 本质上是一个 SPA。这意味着你只需要缓存主页并从主页进入即可正常访问所有页面。所以默认不缓存其他 HTML 能够有效减小缓存大小 (可以缩减大约 40% 的体积),加快 SW 更新速度。

    但是这样做也有缺点,如果用户直接从非主页进入网站,首个页面的 HTML 文件仍需要从互联网加载。同时离线环境下,用户只能通过主页进入再自行导航到对应页面,直接访问某个链接会出现无法访问的提示。

    大小控制

    为了防止在预缓存列表中包含大文件,任何 > 2 MB 的文件或 > 1 MB 的图片都将被忽略。 你可以通过 maxSizemaxImageSize 来自定义大小限制 (单位为 KB)。

    更新控制

    我们提供 update 选项控制用户如何接收更新。

    update 选项的默认值是 "available",这意味着当网站内容更新后,新的 SW 会在后台静默安装,并在安装结束后弹窗提示用户新内容就绪。用户可以自主选择是否立即刷新查看新内容。这意味在新 SW 就绪前用户会访问旧版本网站。

    如果你的文档仍在建设期,希望尽早提示用户他可能在阅读已过时的内容,你可以将其设置为 "hint"。这样用户在进入文档后数秒内就可以收到新内容已发布的通知。但这样做的负面效果是如果用户在新 SW 就绪前选择更新,那么他将在新 SW 安装并接管页面前,需要从互联网获取页面的全部资源。

    如果你的文档很稳定,或者你在托管博客,不太关心用户立即接收到最新版本,你可以将其设置为 "disabled",这意味着新的 SW 将在后台完全静默安装并在安装后等待,当旧版本 SW 控制的页面全部关闭后,新 SW 将再下次访问接管并提供用户新内容。此设置可以避免用户在访中被弹窗打扰。

    如果你希望通过 SW 来加速用户在弱网或无网条件下的访问,但同时希望用户时刻访问新内容,你可以将此选项设置为 "force"。这意味着检测到新 SW 后旧 SW 将会被立刻销毁并且页面会被刷新以确保用户浏览最新内容。最大的缺点就是致新 SW 发布后,用户在重新进入网站后的几秒内会遇到预期之外的突然刷新,并且他们将必须通过互联网访问文档并完全重新安装最新的 SW。

    更新提示弹窗

    当检测到新内容 (检测到新的 SW) 时,更新提示弹窗将会出现;当新内容就绪时,更新就绪弹窗将会出现。

    如果你对默认的弹窗不满意,你可以自行编写组件更换。从 @vuepress/plugin-pwa/client 中导入 PWAFoundPopupPWAReadyPopup 并使用其 slot 来自定义弹窗内容,然后将组件路径传递给 foundComponentreadyComponent 选项。

    <script setup lang="ts">
    +import { PWAFoundPopup } from '@vuepress/plugin-pwa/client'
    +</script>
    +<template>
    +  <PWAFoundPopup v-slot="{ found, refresh }">
    +    <div v-if="found">
    +      已找到新内容
    +      <button @click="refresh">刷新</button>
    +    </div>
    +  </PWAFoundPopup>
    +</template>
    +
    <script setup lang="ts">
    +import { PWAReadyPopup } from '@vuepress/plugin-pwa/client'
    +</script>
    +<template>
    +  <PWAReadyPopup v-slot="{ isReady, reload }">
    +    <div v-if="isReady">
    +      新内容已就绪
    +      <button @click="reload">应用</button>
    +    </div>
    +  </PWAReadyPopup>
    +</template>
    +

    其他选项

    `,28),b=s("code",null,"generateSwConfig",-1),C=s("code",null,"workbox-build",-1),k=s("h2",{id:"相关阅读",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#相关阅读"},[s("span",null,"相关阅读")])],-1),W=s("p",null,"更多内容,请详见:",-1),S={href:"https://web.dev/progressive-web-apps/",target:"_blank",rel:"noopener noreferrer"},P={href:"https://developer.mozilla.org/zh-CN/docs/Web/Progressive_web_apps",target:"_blank",rel:"noopener noreferrer"},E={href:"https://w3c.github.io/manifest/",target:"_blank",rel:"noopener noreferrer"},w=s("hr",{class:"footnotes-sep"},null,-1),x={class:"footnotes"},A={class:"footnotes-list"},q={id:"footnote1",class:"footnote-item"},F=s("p",null,[s("strong",null,"PWA 介绍")],-1),z=s("p",null,"PWA 全称 Progressive Web app,即渐进式网络应用程序,标准由 W3C 规定。",-1),M=s("p",null,"它允许网站通过支持该特性的浏览器将网站作为 App 安装在对应平台上。",-1),L={href:"https://developer.mozilla.org/zh-CN/docs/Web/Progressive_web_apps",target:"_blank",rel:"noopener noreferrer"},N=s("a",{href:"#footnote-ref1",class:"footnote-backref"},"↩︎",-1),R=a('
  • Service Worker 简要介绍

    1. Service Worker 会在注册过程中获取注册在其中的所有文件并缓存它们。

    2. 注册成功后,Service Worker 激活,并开始代理并控制你的全部请求。

    3. 每当你想要通过浏览器发起访问请求后,Service Worker 将会查看其是否存在与自身缓存列表中,若存在则直接返回缓存好的结果,否则调用自身的 fetch 方法进行获取。你可以通过自定义 fetch 方法,来完全控制网页内资源获取请求的结果,比如在离线时提供一个 fallback 的网页。

    4. 每次用户重新打开网站时,Service Worker 会向自身注册时的地址发出校验命令,如果检测到新版本的 Service Worker,则会更新自身,并开始缓存注册在新 Service Worker 中的资源列表。成功获取内容更新后,Service Worker 将会触发 update 事件。可以通过此事件提示用户,比如将在右下角显示一个弹出窗口,提示用户新内容可用并允许用户触发更新。

    ↩︎ ↩︎
  • 可安装性

    想要让网站可以注册为 PWA,网站需要自行成功注册有效的 Service Worker,同时拥有合法的 manifest 清单文件并在网站中声明它。

    清单文件应至少包含 name(或 short_name) icons start_url

    在 Safari 中,SW 的最大缓存空间为 50 MB。 ↩︎ ↩︎

  • SSG: Static Site Generating,静态站点生成。 ↩︎

  • ',3),T={id:"footnote5",class:"footnote-item"},B=s("p",null,[s("strong",null,"SEO"),e(": "),s("strong",null,"S"),e("earch "),s("strong",null,"E"),e("ngine "),s("strong",null,"O"),e("ptimization,搜索引擎增强,")],-1),H={href:"https://mister-hope.com/code/website/html/definition/seo.html",target:"_blank",rel:"noopener noreferrer"},V=s("a",{href:"#footnote-ref5",class:"footnote-backref"},"↩︎",-1),G=s("li",{id:"footnote6",class:"footnote-item"},[s("p",null,[s("strong",null,"SPA"),e(": "),s("strong",null,"S"),e("ingle "),s("strong",null,"P"),e("age "),s("strong",null,"A"),e("pplication, 单页应用")]),s("p",null,[e("大多只有主页,并使用 history mode 处理路由,而不是真的在页面之间导航。 "),s("a",{href:"#footnote-ref6",class:"footnote-backref"},"↩︎")])],-1);function O(I,j){const n=t("ExternalLinkIcon"),l=t("RouteLink");return c(),i("div",null,[u,s("p",null,[e("此插件使用 "),s("a",h,[e("workbox-build"),o(n)]),e(" 生成 Service Worker 文件,并使用 "),s("a",f,[e("register-service-worker"),o(n)]),e(" 注册 Service Worker。")]),s("div",D,[y,s("p",null,[e("如果你启用过该插件,并想要禁用它,你可能需要 "),o(l,{to:"/zh/plugins/remove-pwa.html"},{default:p(()=>[e("`@vuepress/plugin-remove-pwa")]),_:1}),e(" 来移除现有的 Service Worker 。")])]),v,s("p",null,[e("为了使你的网站符合 PWA 的要求,一个 "),s("a",g,[e("网络 App 清单"),o(n)]),e(" 文件是必要的,并且你的 PWA 应满足可安装性"),m,e("要求。")]),_,s("p",null,[e("插件还提供了其他 PWA 相关选项,比如微软磁贴图标与颜色设置,苹果图标等。 如果你是一个高级用户,你也可以设置 "),b,e(" 来配置 "),C,e("。查看 "),o(l,{to:"/zh/plugins/pwa/config.html#%E9%80%89%E9%A1%B9"},{default:p(()=>[e("插件选项")]),_:1}),e(" 了解更多细节。")]),k,W,s("ul",null,[s("li",null,[s("a",S,[e("Google PWA"),o(n)])]),s("li",null,[s("a",P,[e("MDN PWA"),o(n)])]),s("li",null,[s("a",E,[e("W3C Manifest 规范"),o(n)])])]),w,s("section",x,[s("ol",A,[s("li",q,[F,z,M,s("p",null,[e("访问 "),s("a",L,[e("https://developer.mozilla.org/zh-CN/docs/Web/Progressive_web_apps"),o(n)]),e(" 查看详情。 "),N])]),R,s("li",T,[B,s("p",null,[e("详见 "),s("a",H,[e("SEO 介绍"),o(n)]),e(),V])]),G])])])}const U=r(d,[["render",O],["__file","guide.html.vue"]]),K=JSON.parse('{"path":"/zh/plugins/pwa/guide.html","title":"指南","lang":"zh-CN","frontmatter":{},"headers":[{"level":2,"title":"介绍","slug":"介绍","link":"#介绍","children":[]},{"level":2,"title":"网络 App 清单","slug":"网络-app-清单","link":"#网络-app-清单","children":[]},{"level":2,"title":"缓存控制","slug":"缓存控制","link":"#缓存控制","children":[{"level":3,"title":"默认缓存","slug":"默认缓存","link":"#默认缓存","children":[]},{"level":3,"title":"图片缓存","slug":"图片缓存","link":"#图片缓存","children":[]},{"level":3,"title":"HTML 缓存","slug":"html-缓存","link":"#html-缓存","children":[]},{"level":3,"title":"大小控制","slug":"大小控制","link":"#大小控制","children":[]}]},{"level":2,"title":"更新控制","slug":"更新控制","link":"#更新控制","children":[{"level":3,"title":"更新提示弹窗","slug":"更新提示弹窗","link":"#更新提示弹窗","children":[]}]},{"level":2,"title":"其他选项","slug":"其他选项","link":"#其他选项","children":[]},{"level":2,"title":"相关阅读","slug":"相关阅读","link":"#相关阅读","children":[]}],"git":{"updatedTime":1708365979000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":1}]},"filePathRelative":"zh/plugins/pwa/guide.md"}');export{U as comp,K as data}; diff --git a/assets/guide.html-MqAl9Hwl.js b/assets/guide.html-MqAl9Hwl.js new file mode 100644 index 0000000000..efe3e3e61e --- /dev/null +++ b/assets/guide.html-MqAl9Hwl.js @@ -0,0 +1,9 @@ +import{_ as e,o as s,c as a,e as l}from"./app-DI49vYHA.js";const n={},o=l(`

    指南

    本插件会为你的网站自动生成 Sitemap。为了使插件正常工作,你需要将部署的域名传递给插件的 hostname 选项。如果你想在开发服务器中预览,请配置 devServer 选项。

    插件会自动根据页面的 Git 的时间戳生成页面的最后更新时间,同时会根据站点的多语言配置声明页面的其他语言版本替代地址。

    控制 Sitemap 链接

    默认情况下,所有除 404 页面以外的网站链接均会被添加进 Sitemap。

    如果你希望在 VuePress 项目页面之外,添加其他页面链接到 Sitemap,请将它们变成数组传入插件的 extraUrls 选项。

    如果你需要排除一些页面路径,你可以将它们变成数组传入到插件的 excludePaths 选项。你也可以在对应页面的 frontmatter 中,设置 sitemapfalse

    输出位置

    你还可以通过插件的 sitemapFilename 选项控制输出的地址,此地址相对于输出目录,默认为 sitemap.xml

    更新周期

    页面默认的更新周期是 daily (每天),如果你希望修改全部的页面周期,请在插件选项中设置 changefreq 。你也可以在页面的 frontmatter 中设置 sitemap.changefreq,页面具有更高的优先级。

    合法的频率有:

    • "always"
    • "hourly"
    • "daily"
    • "weekly"
    • "monthly"
    • "yearly"
    • "never"

    优先级

    你可以在插件中设置 priority 以提供一个默认值。同时你可以通过 frontmatter 中的 sitemap.priority 来为每个页面设置优先级。可接受的值为 01 的浮点数。

    修改时间获取

    你可以通过插件的 modifyTimeGetter 来返回一个 ISO 字符串格式的时间,默认会通过 Git 插件生成。

    以下是一个基于文件最后修改时间的例子。

    // 基于文件最后修改时间
    +({
    +  modifyTimeGetter: (page, app) =>
    +    fs.statSync(app.dir.source(page.filePathRelative)).mtime.toISOString();
    +})
    +

    Sitemap 介绍

    网站地图 (Sitemap) 提供搜索引擎优化 (SEO):

    • 为搜索引擎爬虫提供可以浏览整个网站的链接;
    • 为搜索引擎爬虫提供一些链接,指向动态页面或者采用其他方法比较难以到达的页面;
    • 如果访问者试图访问网站所在域内并不存在的 URL,那么这个访问者就会被转到“无法找到文件”的错误页面,而网站地图可以作为导航页。

    网站地图通过使所有页面可被找到来增强搜索引擎优化的效果。

    大部分搜索引擎只跟踪页面内有限数量的链接,因此当网站非常大的时候,网站地图对于使搜索引擎和访问者可以访问网站中的所有内容就变得必不可少了。

    Sitemaps 是站点管理员向搜索引擎爬虫公布站点可被抓取页面的协议,sitemap 文件内容必须遵循 XML 格式的定义。每个 URL 可以包含更新的周期和时间、URL 在整个站点中的优先级。这样可以让搜索引擎更佳有效的抓取网站内容。

    同步配置 robots.txt

    由于 Sitemap 面向搜索引擎,配合此插件使用时,你最好保证你在 .vuepress/public 文件夹下放置了有效的 robots.txt,以允许搜索引擎收录。一个最简单的 robots.txt 如下 (允许所有搜索引擎访问所有路径)

    User-agent: *
    +
    +Allow: /
    +
    `,26),t=[o];function p(i,c){return s(),a("div",null,t)}const r=e(n,[["render",p],["__file","guide.html.vue"]]),m=JSON.parse('{"path":"/zh/plugins/sitemap/guide.html","title":"指南","lang":"zh-CN","frontmatter":{},"headers":[{"level":2,"title":"控制 Sitemap 链接","slug":"控制-sitemap-链接","link":"#控制-sitemap-链接","children":[]},{"level":2,"title":"输出位置","slug":"输出位置","link":"#输出位置","children":[]},{"level":2,"title":"更新周期","slug":"更新周期","link":"#更新周期","children":[]},{"level":2,"title":"优先级","slug":"优先级","link":"#优先级","children":[]},{"level":2,"title":"修改时间获取","slug":"修改时间获取","link":"#修改时间获取","children":[]},{"level":2,"title":"Sitemap 介绍","slug":"sitemap-介绍","link":"#sitemap-介绍","children":[]}],"git":{"updatedTime":1706723991000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":2}]},"filePathRelative":"zh/plugins/sitemap/guide.md"}');export{r as comp,m as data}; diff --git a/assets/index.html-10UTHdZ8.js b/assets/index.html-10UTHdZ8.js new file mode 100644 index 0000000000..50983362f3 --- /dev/null +++ b/assets/index.html-10UTHdZ8.js @@ -0,0 +1 @@ +import{_ as e,o as t,c as o}from"./app-DI49vYHA.js";const r={};function a(i,m){return t(),o("div")}const s=e(r,[["render",a],["__file","index.html.vue"]]),c=JSON.parse('{"path":"/zh/","title":"首页","lang":"zh-CN","frontmatter":{"home":true,"title":"首页","heroImage":"/images/hero.png","actions":[{"text":"主题","link":"./themes/","type":"primary"},{"text":"插件","link":"./plugins/","type":"primary"}],"footer":"MIT 协议 | 版权所有 © 2018-至今 VuePress 社区"},"headers":[],"git":{"updatedTime":1707215383000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":2}]},"filePathRelative":"zh/README.md"}');export{s as comp,c as data}; diff --git a/assets/index.html-3S31PeiG.js b/assets/index.html-3S31PeiG.js new file mode 100644 index 0000000000..941e1e57bf --- /dev/null +++ b/assets/index.html-3S31PeiG.js @@ -0,0 +1,11 @@ +import{_ as a,r as e,o as l,c as o,a as i,b as s,e as p}from"./app-DI49vYHA.js";const c={},r=s("h1",{id:"blog",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#blog"},[s("span",null,"blog")])],-1),t=p(`

    使用

    npm i -D @vuepress/plugin-blog@next
    +
    import { blogPlugin } from '@vuepress/plugin-blog'
    +
    +export default {
    +  plugins: [
    +    blogPlugin({
    +      // 选项
    +    }),
    +  ],
    +}
    +
    `,3);function d(u,m){const n=e("NpmBadge");return l(),o("div",null,[r,i(n,{package:"@vuepress/plugin-blog"}),t])}const v=a(c,[["render",d],["__file","index.html.vue"]]),b=JSON.parse('{"path":"/zh/plugins/blog/","title":"blog","lang":"zh-CN","frontmatter":{},"headers":[{"level":2,"title":"使用","slug":"使用","link":"#使用","children":[]}],"git":{"updatedTime":1707125652000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":1}]},"filePathRelative":"zh/plugins/blog/README.md"}');export{v as comp,b as data}; diff --git a/assets/index.html-4jsKLsHE.js b/assets/index.html-4jsKLsHE.js new file mode 100644 index 0000000000..98b98abec7 --- /dev/null +++ b/assets/index.html-4jsKLsHE.js @@ -0,0 +1,11 @@ +import{_ as n,r as e,o as l,c as p,a as i,b as s,e as o}from"./app-DI49vYHA.js";const c={},r=s("h1",{id:"pwa",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#pwa"},[s("span",null,"pwa")])],-1),t=o(`

    使用

    npm i -D @vuepress/plugin-pwa@next
    +
    import { pwaPlugin } from '@vuepress/plugin-pwa'
    +
    +export default {
    +  plugins: [
    +    pwaPlugin({
    +      // 选项
    +    }),
    +  ],
    +}
    +
    `,3);function d(u,m){const a=e("NpmBadge");return l(),p("div",null,[r,i(a,{package:"@vuepress/plugin-pwa"}),t])}const v=n(c,[["render",d],["__file","index.html.vue"]]),h=JSON.parse('{"path":"/zh/plugins/pwa/","title":"pwa","lang":"zh-CN","frontmatter":{},"headers":[{"level":2,"title":"使用","slug":"使用","link":"#使用","children":[]}],"git":{"updatedTime":1708365979000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":1}]},"filePathRelative":"zh/plugins/pwa/README.md"}');export{v as comp,h as data}; diff --git a/assets/index.html-6kqMvCYf.js b/assets/index.html-6kqMvCYf.js new file mode 100644 index 0000000000..5ace488650 --- /dev/null +++ b/assets/index.html-6kqMvCYf.js @@ -0,0 +1,11 @@ +import{_ as e,r as n,o as l,c as i,a as p,b as s,e as t}from"./app-DI49vYHA.js";const o={},c=s("h1",{id:"sitemap",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#sitemap"},[s("span",null,"sitemap")])],-1),r=t(`

    Usage

    npm i -D @vuepress/plugin-sitemap@next
    +
    import { sitemapPlugin } from '@vuepress/plugin-sitemap'
    +
    +export default {
    +  plugins: [
    +    sitemapPlugin({
    +      // options
    +    }),
    +  ],
    +}
    +
    `,3);function d(m,u){const a=n("NpmBadge");return l(),i("div",null,[c,p(a,{package:"@vuepress/plugin-sitemap"}),r])}const v=e(o,[["render",d],["__file","index.html.vue"]]),g=JSON.parse('{"path":"/plugins/sitemap/","title":"sitemap","lang":"en-US","frontmatter":{},"headers":[{"level":2,"title":"Usage","slug":"usage","link":"#usage","children":[]}],"git":{"updatedTime":1706762763000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":2}]},"filePathRelative":"plugins/sitemap/README.md"}');export{v as comp,g as data}; diff --git a/assets/index.html-B1QMRWf6.js b/assets/index.html-B1QMRWf6.js new file mode 100644 index 0000000000..ab6199ffc5 --- /dev/null +++ b/assets/index.html-B1QMRWf6.js @@ -0,0 +1,11 @@ +import{_ as e,r as n,o as l,c as i,a as p,b as s,e as t}from"./app-DI49vYHA.js";const o={},c=s("h1",{id:"sitemap",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#sitemap"},[s("span",null,"sitemap")])],-1),r=t(`

    使用

    npm i -D @vuepress/plugin-sitemap@next
    +
    import { sitemapPlugin } from '@vuepress/plugin-sitemap'
    +
    +export default {
    +  plugins: [
    +    sitemapPlugin({
    +      // 选项
    +    }),
    +  ],
    +}
    +
    `,3);function d(m,u){const a=n("NpmBadge");return l(),i("div",null,[c,p(a,{package:"@vuepress/plugin-sitemap"}),r])}const v=e(o,[["render",d],["__file","index.html.vue"]]),h=JSON.parse('{"path":"/zh/plugins/sitemap/","title":"sitemap","lang":"zh-CN","frontmatter":{},"headers":[{"level":2,"title":"使用","slug":"使用","link":"#使用","children":[]}],"git":{"updatedTime":1706762763000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":2}]},"filePathRelative":"zh/plugins/sitemap/README.md"}');export{v as comp,h as data}; diff --git a/assets/index.html-B8jQgsFD.js b/assets/index.html-B8jQgsFD.js new file mode 100644 index 0000000000..5cf445cf35 --- /dev/null +++ b/assets/index.html-B8jQgsFD.js @@ -0,0 +1 @@ +import{_ as t,r as o,o as a,c as n,a as l}from"./app-DI49vYHA.js";const r={};function c(s,_){const e=o("Catalog");return a(),n("div",null,[l(e)])}const d=t(r,[["render",c],["__file","index.html.vue"]]),m=JSON.parse('{"path":"/zh/tools/helper/node/","title":"Node","lang":"en-US","frontmatter":{"title":"Node"},"headers":[],"git":{},"filePathRelative":null}');export{d as comp,m as data}; diff --git a/assets/index.html-BEvkK6U_.js b/assets/index.html-BEvkK6U_.js new file mode 100644 index 0000000000..3f626c6785 --- /dev/null +++ b/assets/index.html-BEvkK6U_.js @@ -0,0 +1 @@ +import{_ as a,r as n,o as h,c as p,a as l,b as e,w as s,d as t}from"./app-DI49vYHA.js";const u={},c=e("h1",{id:"vuepress-helper",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#vuepress-helper"},[e("span",null,"@vuepress/helper")])],-1),d=e("p",null,"此包为 VuePress 开发者提供辅助函数。",-1),i=e("p",null,[e("code",null,"@vuepress/helper"),t(": Node.js 一侧的辅助函数。")],-1),_=e("code",null,"@vuepress/helper/client",-1),m=e("code",null,"@vuepress/helper/shared",-1);function f(v,N){const r=n("NpmBadge"),o=n("RouteLink");return h(),p("div",null,[c,l(r,{package:"@vuepress/helper"}),d,e("ul",null,[e("li",null,[i,e("ul",null,[e("li",null,[l(o,{to:"/zh/tools/helper/node/bundler.html"},{default:s(()=>[t("打包器相关")]),_:1})]),e("li",null,[l(o,{to:"/zh/tools/helper/node/page.html"},{default:s(()=>[t("页面相关")]),_:1})])])]),e("li",null,[e("p",null,[l(o,{to:"/zh/tools/helper/client.html"},{default:s(()=>[_]),_:1}),t(": 客户端一侧的辅助函数。")])]),e("li",null,[e("p",null,[l(o,{to:"/zh/tools/helper/shared.html"},{default:s(()=>[m]),_:1}),t(": Node.js 和客户端共享的辅助函数。")])])])])}const z=a(u,[["render",f],["__file","index.html.vue"]]),g=JSON.parse('{"path":"/zh/tools/helper/","title":"@vuepress/helper","lang":"zh-CN","frontmatter":{},"headers":[],"git":{"updatedTime":1706960075000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":1}]},"filePathRelative":"zh/tools/helper/README.md"}');export{z as comp,g as data}; diff --git a/assets/index.html-BFeBzGTv.js b/assets/index.html-BFeBzGTv.js new file mode 100644 index 0000000000..38498cbf0d --- /dev/null +++ b/assets/index.html-BFeBzGTv.js @@ -0,0 +1,11 @@ +import{_ as a,r as e,o as l,c as o,a as i,b as s,e as p}from"./app-DI49vYHA.js";const c={},t=s("h1",{id:"blog",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#blog"},[s("span",null,"blog")])],-1),r=p(`

    Usage

    npm i -D @vuepress/plugin-blog@next
    +
    import { blogPlugin } from '@vuepress/plugin-blog'
    +
    +export default {
    +  plugins: [
    +    blogPlugin({
    +      // options
    +    }),
    +  ],
    +}
    +
    `,3);function d(u,m){const n=e("NpmBadge");return l(),o("div",null,[t,i(n,{package:"@vuepress/plugin-blog"}),r])}const D=a(c,[["render",d],["__file","index.html.vue"]]),v=JSON.parse('{"path":"/plugins/blog/","title":"blog","lang":"en-US","frontmatter":{},"headers":[{"level":2,"title":"Usage","slug":"usage","link":"#usage","children":[]}],"git":{"updatedTime":1707125652000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":1}]},"filePathRelative":"plugins/blog/README.md"}');export{D as comp,v as data}; diff --git a/assets/index.html-BNzZzBwI.js b/assets/index.html-BNzZzBwI.js new file mode 100644 index 0000000000..5123eaffb7 --- /dev/null +++ b/assets/index.html-BNzZzBwI.js @@ -0,0 +1 @@ +import{_ as a,r as o,o as n,c as s,a as r,b as e}from"./app-DI49vYHA.js";const c={},l=e("h1",{id:"主题",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#主题"},[e("span",null,"主题")])],-1);function m(i,h){const t=o("Catalog");return n(),s("div",null,[l,r(t,{level:1})])}const _=a(c,[["render",m],["__file","index.html.vue"]]),p=JSON.parse('{"path":"/zh/themes/","title":"主题","lang":"zh-CN","frontmatter":{},"headers":[],"git":{"updatedTime":1708364547000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":1}]},"filePathRelative":"zh/themes/README.md"}');export{_ as comp,p as data}; diff --git a/assets/index.html-BiVwZvP3.js b/assets/index.html-BiVwZvP3.js new file mode 100644 index 0000000000..6fb328479e --- /dev/null +++ b/assets/index.html-BiVwZvP3.js @@ -0,0 +1 @@ +import{_ as a,r as o,o as n,c as s,a as l,b as e}from"./app-DI49vYHA.js";const r={},c=e("h1",{id:"plugins",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#plugins"},[e("span",null,"Plugins")])],-1);function i(m,p){const t=o("Catalog");return n(),s("div",null,[c,l(t,{level:1})])}const _=a(r,[["render",i],["__file","index.html.vue"]]),u=JSON.parse('{"path":"/plugins/","title":"Plugins","lang":"en-US","frontmatter":{},"headers":[],"git":{"updatedTime":1708364547000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":1}]},"filePathRelative":"plugins/README.md"}');export{_ as comp,u as data}; diff --git a/assets/index.html-BszZ_ZuB.js b/assets/index.html-BszZ_ZuB.js new file mode 100644 index 0000000000..99f2629d64 --- /dev/null +++ b/assets/index.html-BszZ_ZuB.js @@ -0,0 +1,11 @@ +import{_ as n,r as a,o as l,c as i,a as p,b as s,e as o}from"./app-DI49vYHA.js";const c={},t=s("h1",{id:"feed",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#feed"},[s("span",null,"feed")])],-1),r=o(`

    Usage

    npm i -D @vuepress/plugin-feed@next
    +
    import { feedPlugin } from '@vuepress/plugin-feed'
    +
    +export default {
    +  plugins: [
    +    feedPlugin({
    +      // options
    +    }),
    +  ],
    +}
    +
    `,3);function d(u,m){const e=a("NpmBadge");return l(),i("div",null,[t,p(e,{package:"@vuepress/plugin-feed"}),r])}const v=n(c,[["render",d],["__file","index.html.vue"]]),g=JSON.parse('{"path":"/plugins/feed/","title":"feed","lang":"en-US","frontmatter":{},"headers":[{"level":2,"title":"Usage","slug":"usage","link":"#usage","children":[]}],"git":{"updatedTime":1706762763000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":2}]},"filePathRelative":"plugins/feed/README.md"}');export{v as comp,g as data}; diff --git a/assets/index.html-C8BL235-.js b/assets/index.html-C8BL235-.js new file mode 100644 index 0000000000..06aeccc9ff --- /dev/null +++ b/assets/index.html-C8BL235-.js @@ -0,0 +1 @@ +import{_ as t,r as o,o as a,c as n,a as l}from"./app-DI49vYHA.js";const r={};function c(s,_){const e=o("Catalog");return a(),n("div",null,[l(e)])}const d=t(r,[["render",c],["__file","index.html.vue"]]),m=JSON.parse('{"path":"/tools/helper/node/","title":"Node","lang":"en-US","frontmatter":{"title":"Node"},"headers":[],"git":{},"filePathRelative":null}');export{d as comp,m as data}; diff --git a/assets/index.html-C94lRzr8.js b/assets/index.html-C94lRzr8.js new file mode 100644 index 0000000000..e4666a30f9 --- /dev/null +++ b/assets/index.html-C94lRzr8.js @@ -0,0 +1,11 @@ +import{_ as n,r as a,o as l,c as o,a as i,b as s,e as p}from"./app-DI49vYHA.js";const c={},t=s("h1",{id:"seo",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#seo"},[s("span",null,"seo")])],-1),r=p(`

    Usage

    npm i -D @vuepress/plugin-seo@next
    +
    import { seoPlugin } from '@vuepress/plugin-seo'
    +
    +export default {
    +  plugins: [
    +    seoPlugin({
    +      // options
    +    }),
    +  ],
    +}
    +
    `,3);function d(u,m){const e=a("NpmBadge");return l(),o("div",null,[t,i(e,{package:"@vuepress/plugin-seo"}),r])}const v=n(c,[["render",d],["__file","index.html.vue"]]),g=JSON.parse('{"path":"/plugins/seo/","title":"seo","lang":"en-US","frontmatter":{},"headers":[{"level":2,"title":"Usage","slug":"usage","link":"#usage","children":[]}],"git":{"updatedTime":1706762763000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":2}]},"filePathRelative":"plugins/seo/README.md"}');export{v as comp,g as data}; diff --git a/assets/index.html-CN0Aolsb.js b/assets/index.html-CN0Aolsb.js new file mode 100644 index 0000000000..4575be81aa --- /dev/null +++ b/assets/index.html-CN0Aolsb.js @@ -0,0 +1 @@ +import{_ as a,r,o as i,c as p,a as l,b as e,w as s,d as t}from"./app-DI49vYHA.js";const d={},u=e("h1",{id:"vuepress-helper",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#vuepress-helper"},[e("span",null,"@vuepress/helper")])],-1),h=e("p",null,"This package is a helper utility for VuePress developers.",-1),c=e("p",null,[e("code",null,"@vuepress/helper"),t(": Node.js side helper utilities.")],-1),_=e("code",null,"@vuepress/helper/client",-1),m=e("code",null,"@vuepress/helper/shared",-1);function f(v,g){const n=r("NpmBadge"),o=r("RouteLink");return i(),p("div",null,[u,l(n,{package:"@vuepress/helper"}),h,e("ul",null,[e("li",null,[c,e("ul",null,[e("li",null,[l(o,{to:"/tools/helper/node/bundler.html"},{default:s(()=>[t("Bundler Related")]),_:1})]),e("li",null,[l(o,{to:"/tools/helper/node/page.html"},{default:s(()=>[t("Page Related")]),_:1})])])]),e("li",null,[e("p",null,[l(o,{to:"/tools/helper/client.html"},{default:s(()=>[_]),_:1}),t(": Client side helper utilities.")])]),e("li",null,[e("p",null,[l(o,{to:"/tools/helper/shared.html"},{default:s(()=>[m]),_:1}),t(": Utilities that are both available at Node.js side or Client.")])])])])}const N=a(d,[["render",f],["__file","index.html.vue"]]),k=JSON.parse('{"path":"/tools/helper/","title":"@vuepress/helper","lang":"en-US","frontmatter":{},"headers":[],"git":{"updatedTime":1706960075000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":1}]},"filePathRelative":"tools/helper/README.md"}');export{N as comp,k as data}; diff --git a/assets/index.html-CYCrKtK1.js b/assets/index.html-CYCrKtK1.js new file mode 100644 index 0000000000..d104c437e4 --- /dev/null +++ b/assets/index.html-CYCrKtK1.js @@ -0,0 +1,9 @@ +import{_ as a,r as n,o as l,c as t,a as o,b as e,e as i}from"./app-DI49vYHA.js";const c={},p=e("h1",{id:"theme-default",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#theme-default"},[e("span",null,"theme-default")])],-1),r=e("h2",{id:"usage",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#usage"},[e("span",null,"Usage")])],-1),d=i(`

    Install @vuepress/theme-default :

    npm install @vuepress/theme-default@next
    +
    import { defaultTheme } from '@vuepress/theme-default'
    +
    +export default {
    +  theme: defaultTheme({
    +    // set theme config here
    +  }),
    +}
    +
    `,3);function m(u,h){const s=n("NpmBadge");return l(),t("div",null,[p,r,o(s,{package:"@vuepress/theme-default"}),d])}const D=a(c,[["render",m],["__file","index.html.vue"]]),f=JSON.parse('{"path":"/themes/default/","title":"theme-default","lang":"en-US","frontmatter":{},"headers":[{"level":2,"title":"Usage","slug":"usage","link":"#usage","children":[]}],"git":{"updatedTime":1707124819000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":2}]},"filePathRelative":"themes/default/README.md"}');export{D as comp,f as data}; diff --git a/assets/index.html-Cq71pHF1.js b/assets/index.html-Cq71pHF1.js new file mode 100644 index 0000000000..df3fa4513b --- /dev/null +++ b/assets/index.html-Cq71pHF1.js @@ -0,0 +1,9 @@ +import{_ as a,r as n,o as l,c as t,a as o,b as s,e as p}from"./app-DI49vYHA.js";const i={},c=s("h1",{id:"默认主题",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#默认主题"},[s("span",null,"默认主题")])],-1),r=s("h2",{id:"使用方法",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#使用方法"},[s("span",null,"使用方法")])],-1),d=p(`

    安装默认主题:

    npm i -D @vuepress/theme-default@next
    +

    在配置文件中指定主题:

    import { defaultTheme } from '@vuepress/theme-default'
    +
    +export default {
    +  theme: defaultTheme({
    +    // 在这里添加主题配置
    +  }),
    +}
    +
    `,4);function m(u,h){const e=n("NpmBadge");return l(),t("div",null,[c,r,o(e,{package:"@vuepress/theme-default"}),d])}const D=a(i,[["render",m],["__file","index.html.vue"]]),b=JSON.parse('{"path":"/zh/themes/default/","title":"默认主题","lang":"zh-CN","frontmatter":{},"headers":[{"level":2,"title":"使用方法","slug":"使用方法","link":"#使用方法","children":[]}],"git":{"updatedTime":1707124819000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":2}]},"filePathRelative":"zh/themes/default/README.md"}');export{D as comp,b as data}; diff --git a/assets/index.html-Crkx8NN1.js b/assets/index.html-Crkx8NN1.js new file mode 100644 index 0000000000..9f0f629207 --- /dev/null +++ b/assets/index.html-Crkx8NN1.js @@ -0,0 +1,11 @@ +import{_ as n,r as a,o as l,c as o,a as i,b as s,e as p}from"./app-DI49vYHA.js";const c={},r=s("h1",{id:"seo",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#seo"},[s("span",null,"seo")])],-1),t=p(`

    使用

    npm i -D @vuepress/plugin-seo@next
    +
    import { seoPlugin } from '@vuepress/plugin-seo'
    +
    +export default {
    +  plugins: [
    +    seoPlugin({
    +      // 选项
    +    }),
    +  ],
    +}
    +
    `,3);function d(u,m){const e=a("NpmBadge");return l(),o("div",null,[r,i(e,{package:"@vuepress/plugin-seo"}),t])}const v=n(c,[["render",d],["__file","index.html.vue"]]),h=JSON.parse('{"path":"/zh/plugins/seo/","title":"seo","lang":"zh-CN","frontmatter":{},"headers":[{"level":2,"title":"使用","slug":"使用","link":"#使用","children":[]}],"git":{"updatedTime":1706762763000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":2}]},"filePathRelative":"zh/plugins/seo/README.md"}');export{v as comp,h as data}; diff --git a/assets/index.html-D5ijgUnW.js b/assets/index.html-D5ijgUnW.js new file mode 100644 index 0000000000..15632861dc --- /dev/null +++ b/assets/index.html-D5ijgUnW.js @@ -0,0 +1 @@ +import{_ as t,r as o,o as a,c as n,a as l}from"./app-DI49vYHA.js";const s={};function c(r,_){const e=o("Catalog");return a(),n("div",null,[l(e)])}const m=t(s,[["render",c],["__file","index.html.vue"]]),p=JSON.parse('{"path":"/tools/","title":"Tools","lang":"en-US","frontmatter":{"title":"Tools"},"headers":[],"git":{},"filePathRelative":null}');export{m as comp,p as data}; diff --git a/assets/index.html-NGgl-cMu.js b/assets/index.html-NGgl-cMu.js new file mode 100644 index 0000000000..855f811582 --- /dev/null +++ b/assets/index.html-NGgl-cMu.js @@ -0,0 +1 @@ +import{_ as e,o as t,c as o}from"./app-DI49vYHA.js";const r={};function i(m,n){return t(),o("div")}const a=e(r,[["render",i],["__file","index.html.vue"]]),c=JSON.parse('{"path":"/","title":"Home","lang":"en-US","frontmatter":{"home":true,"title":"Home","heroImage":"/images/hero.png","actions":[{"text":"Themes","link":"./themes/","type":"primary"},{"text":"Plugins","link":"./plugins/","type":"primary"}],"footer":"MIT Licensed | Copyright © 2018-present VuePress Community"},"headers":[],"git":{"updatedTime":1707215383000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":2}]},"filePathRelative":"README.md"}');export{a as comp,c as data}; diff --git a/assets/index.html-Y1wYcIZr.js b/assets/index.html-Y1wYcIZr.js new file mode 100644 index 0000000000..f4b1f7acc7 --- /dev/null +++ b/assets/index.html-Y1wYcIZr.js @@ -0,0 +1 @@ +import{_ as a,r as o,o as s,c as n,a as r,b as e}from"./app-DI49vYHA.js";const c={},m=e("h1",{id:"themes",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#themes"},[e("span",null,"Themes")])],-1);function l(i,h){const t=o("Catalog");return s(),n("div",null,[m,r(t,{level:1})])}const _=a(c,[["render",l],["__file","index.html.vue"]]),p=JSON.parse('{"path":"/themes/","title":"Themes","lang":"en-US","frontmatter":{},"headers":[],"git":{"updatedTime":1708364547000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":1}]},"filePathRelative":"themes/README.md"}');export{_ as comp,p as data}; diff --git a/assets/index.html-YB60ZmkU.js b/assets/index.html-YB60ZmkU.js new file mode 100644 index 0000000000..e66161e185 --- /dev/null +++ b/assets/index.html-YB60ZmkU.js @@ -0,0 +1,11 @@ +import{_ as n,r as a,o as l,c as i,a as p,b as s,e as o}from"./app-DI49vYHA.js";const c={},r=s("h1",{id:"feed",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#feed"},[s("span",null,"feed")])],-1),t=o(`

    使用

    npm i -D @vuepress/plugin-feed@next
    +
    import { feedPlugin } from '@vuepress/plugin-feed'
    +
    +export default {
    +  plugins: [
    +    feedPlugin({
    +      // 选项
    +    }),
    +  ],
    +}
    +
    `,3);function d(u,m){const e=a("NpmBadge");return l(),i("div",null,[r,p(e,{package:"@vuepress/plugin-feed"}),t])}const v=n(c,[["render",d],["__file","index.html.vue"]]),h=JSON.parse('{"path":"/zh/plugins/feed/","title":"feed","lang":"zh-CN","frontmatter":{},"headers":[{"level":2,"title":"使用","slug":"使用","link":"#使用","children":[]}],"git":{"updatedTime":1706762763000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":2}]},"filePathRelative":"zh/plugins/feed/README.md"}');export{v as comp,h as data}; diff --git a/assets/index.html-dNgmFSRH.js b/assets/index.html-dNgmFSRH.js new file mode 100644 index 0000000000..a4ab4e39e2 --- /dev/null +++ b/assets/index.html-dNgmFSRH.js @@ -0,0 +1 @@ +import{_ as t,r as o,o as a,c as n,a as l}from"./app-DI49vYHA.js";const s={};function c(r,_){const e=o("Catalog");return a(),n("div",null,[l(e)])}const m=t(s,[["render",c],["__file","index.html.vue"]]),p=JSON.parse('{"path":"/zh/tools/","title":"Tools","lang":"en-US","frontmatter":{"title":"Tools"},"headers":[],"git":{},"filePathRelative":null}');export{m as comp,p as data}; diff --git a/assets/index.html-ia4XVneA.js b/assets/index.html-ia4XVneA.js new file mode 100644 index 0000000000..7cda89d29e --- /dev/null +++ b/assets/index.html-ia4XVneA.js @@ -0,0 +1,11 @@ +import{_ as n,r as e,o as l,c as p,a as i,b as s,e as o}from"./app-DI49vYHA.js";const c={},t=s("h1",{id:"pwa",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#pwa"},[s("span",null,"pwa")])],-1),r=o(`

    Usage

    npm i -D @vuepress/plugin-pwa@next
    +
    import { pwaPlugin } from '@vuepress/plugin-pwa'
    +
    +export default {
    +  plugins: [
    +    pwaPlugin({
    +      // options
    +    }),
    +  ],
    +}
    +
    `,3);function d(u,m){const a=e("NpmBadge");return l(),p("div",null,[t,i(a,{package:"@vuepress/plugin-pwa"}),r])}const v=n(c,[["render",d],["__file","index.html.vue"]]),g=JSON.parse('{"path":"/plugins/pwa/","title":"pwa","lang":"en-US","frontmatter":{},"headers":[{"level":2,"title":"Usage","slug":"usage","link":"#usage","children":[]}],"git":{"updatedTime":1708365979000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":1}]},"filePathRelative":"plugins/pwa/README.md"}');export{v as comp,g as data}; diff --git a/assets/index.html-l9neTtpK.js b/assets/index.html-l9neTtpK.js new file mode 100644 index 0000000000..220e500cab --- /dev/null +++ b/assets/index.html-l9neTtpK.js @@ -0,0 +1 @@ +import{_ as a,r as o,o as n,c as s,a as r,b as e}from"./app-DI49vYHA.js";const c={},l=e("h1",{id:"插件",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#插件"},[e("span",null,"插件")])],-1);function i(m,d){const t=o("Catalog");return n(),s("div",null,[l,r(t,{level:1})])}const h=a(c,[["render",i],["__file","index.html.vue"]]),p=JSON.parse('{"path":"/zh/plugins/","title":"插件","lang":"zh-CN","frontmatter":{},"headers":[],"git":{"updatedTime":1708364547000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":1}]},"filePathRelative":"zh/plugins/README.md"}');export{h as comp,p as data}; diff --git a/assets/locale.html-CBiyLOGJ.js b/assets/locale.html-CBiyLOGJ.js new file mode 100644 index 0000000000..abc44cd5f7 --- /dev/null +++ b/assets/locale.html-CBiyLOGJ.js @@ -0,0 +1,21 @@ +import{_ as i,r as o,o as p,c,b as e,d as l,a,w as s,e as t}from"./app-DI49vYHA.js";const d={},r=e("h1",{id:"locale-config",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#locale-config"},[e("span",null,"Locale Config")])],-1),u=e("p",null,"These options configure locale-related texts.",-1),h=e("p",null,"If your site is served in a different language besides English, you should set these options per locale to provide translations.",-1),g=e("h2",{id:"repolabel",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#repolabel"},[e("span",null,"repoLabel")])],-1),m=e("li",null,[e("p",null,[l("Type: "),e("code",null,"string")])],-1),f=e("p",null,"Details:",-1),b=e("p",null,"Specify the repository label of your project.",-1),_=e("p",null,[l("This will be used as the text of the "),e("em",null,"repository link"),l(", which will be displayed as the last item of the navbar.")],-1),D=e("h2",{id:"selectlanguagetext",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#selectlanguagetext"},[e("span",null,"selectLanguageText")])],-1),y=e("li",null,[e("p",null,[l("Type: "),e("code",null,"string")])],-1),v=e("p",null,"Details:",-1),x=e("p",null,[l("Specify the text of the "),e("em",null,"select language menu"),l(".")],-1),k=e("em",null,"select language menu",-1),C=t('

    selectLanguageAriaLabel

    • Type: string

    • Details:

      Specify the aria-label attribute of the select language menu.

      This is mainly for a11y purpose.

    selectLanguageName

    ',3),w=e("li",null,[e("p",null,[l("Type: "),e("code",null,"string")])],-1),T=e("p",null,"Details:",-1),E=e("p",null,"Specify the name of the language of a locale.",-1),L=e("strong",null,"only take effect inside",-1),S=e("em",null,"select language menu",-1),N=e("li",null,[e("p",null,"Example:")],-1),F=t(`
    export default {
    +  locales: {
    +    '/': {
    +      lang: 'en-US',
    +    },
    +    '/zh/': {
    +      lang: 'zh-CN',
    +    },
    +  },
    +  theme: defaultTheme({
    +    locales: {
    +      '/': {
    +        selectLanguageName: 'English',
    +      },
    +      '/zh/': {
    +        selectLanguageName: '简体中文',
    +      },
    +    },
    +  }),
    +}
    +
    • Type: null | string

    • Details:

      aria-label value for main navigation in navbar.

    • Type: null | string

    • Details:

      aria-label value for next/previous page navigation.

    editLinkText

    • Type: string

    • Default: 'Edit this page'

    • Details:

      Specify the text of the edit this page link.

    lastUpdatedText

    • Type: string

    • Default: 'Last Updated'

    • Details:

      Specify the text of the last updated timestamp label.

    contributorsText

    • Type: string

    • Default: 'Contributors'

    • Details:

      Specify the text of the contributors list label.

    tip

    `,12),I=e("li",null,[e("p",null,[l("Type: "),e("code",null,"string")])],-1),A=e("li",null,[e("p",null,[l("Default: "),e("code",null,"'TIP'")])],-1),R=e("p",null,"Details:",-1),U=e("h2",{id:"warning",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#warning"},[e("span",null,"warning")])],-1),B=e("li",null,[e("p",null,[l("Type: "),e("code",null,"string")])],-1),M=e("li",null,[e("p",null,[l("Default: "),e("code",null,"'WARNING'")])],-1),P=e("p",null,"Details:",-1),V=e("h2",{id:"danger",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#danger"},[e("span",null,"danger")])],-1),z=e("li",null,[e("p",null,[l("Type: "),e("code",null,"string")])],-1),H=e("li",null,[e("p",null,[l("Default: "),e("code",null,"'DANGER'")])],-1),W=e("p",null,"Details:",-1),G=t('

    notFound

    • Type: string[]

    • Default: ['Not Found']

    • Details:

      Specify the messages of the 404 page.

      The message will be randomly picked from the array when users enter the 404 page.

    backToHome

    • Type: string

    • Default: 'Back to home'

    • Details:

      Specify the text of the back to home link in the 404 page.

    openInNewWindow

    ',5),j=e("li",null,[e("p",null,[l("Type: "),e("code",null,"string")])],-1),J=e("li",null,[e("p",null,[l("Default: "),e("code",null,"'open in new window'")])],-1),O=e("p",null,"Details:",-1),q=e("code",null,"sr-only",-1),K=e("p",null,"This is mainly for a11y purpose.",-1),Q=e("p",null,"Also see:",-1),X=e("h2",{id:"togglecolormode",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#togglecolormode"},[e("span",null,"toggleColorMode")])],-1),Y=e("li",null,[e("p",null,[l("Type: "),e("code",null,"string")])],-1),Z=e("li",null,[e("p",null,[l("Default: "),e("code",null,"'toggle color mode'")])],-1),$=e("li",null,[e("p",null,"Details:"),e("p",null,"Title text for the color mode toggle button."),e("p",null,"This is mainly for a11y purpose.")],-1),ee=e("p",null,"Also see:",-1),le=t('

    toggleSidebar

    • Type: string

    • Default: 'toggle sidebar'

    • Details:

      Title text for sidebar toggle button.

      This is mainly for a11y purpose.

    ',2);function ne(ae,se){const n=o("RouteLink");return p(),c("div",null,[r,u,h,g,e("ul",null,[m,e("li",null,[f,b,_,e("p",null,[l("If you don't set this option explicitly, it will be automatically inferred from the "),a(n,{to:"/themes/default/config.html#repo"},{default:s(()=>[l("repo")]),_:1}),l(" option.")])])]),D,e("ul",null,[y,e("li",null,[v,x,e("p",null,[l("The "),k,l(" will appear next to the repository button in the navbar when you set multiple "),a(n,{to:"/themes/default/config.html#locales"},{default:s(()=>[l("locales")]),_:1}),l(" in your site config.")])])]),C,e("ul",null,[w,e("li",null,[T,E,e("p",null,[l("This option will "),L,l(" the "),a(n,{to:"/themes/default/config.html#locales"},{default:s(()=>[l("locales")]),_:1}),l(" of your theme config. It will be used as the language name of the locale, which will be displayed in the "),S,l(".")])]),N]),F,e("ul",null,[I,A,e("li",null,[R,e("p",null,[l("Specify the default title of the tip "),a(n,{to:"/themes/default/markdown.html#custom-containers"},{default:s(()=>[l("custom containers")]),_:1}),l(".")])])]),U,e("ul",null,[B,M,e("li",null,[P,e("p",null,[l("Specify the default title of the warning "),a(n,{to:"/themes/default/markdown.html#custom-containers"},{default:s(()=>[l("custom containers")]),_:1}),l(".")])])]),V,e("ul",null,[z,H,e("li",null,[W,e("p",null,[l("Specify the default title of the danger "),a(n,{to:"/themes/default/markdown.html#custom-containers"},{default:s(()=>[l("custom containers")]),_:1}),l(".")])])]),G,e("ul",null,[j,J,e("li",null,[O,e("p",null,[l("Specify the "),q,l(" text of the "),a(n,{to:"/plugins/external-link-icon.html#externallinkicon"},{default:s(()=>[l("ExternalLinkIcon")]),_:1}),l(".")]),K]),e("li",null,[Q,e("ul",null,[e("li",null,[a(n,{to:"/themes/default/plugin.html#themeplugins-externallinkicon"},{default:s(()=>[l("Default Theme > Plugin Config > themePlugins.externalLinkIcon")]),_:1})])])])]),X,e("ul",null,[Y,Z,$,e("li",null,[ee,e("ul",null,[e("li",null,[a(n,{to:"/themes/default/config.html#colormodeswitch"},{default:s(()=>[l("Default Theme > Config > colorModeSwitch")]),_:1})])])])]),le])}const ie=i(d,[["render",ne],["__file","locale.html.vue"]]),oe=JSON.parse('{"path":"/themes/default/locale.html","title":"Locale Config","lang":"en-US","frontmatter":{},"headers":[{"level":2,"title":"repoLabel","slug":"repolabel","link":"#repolabel","children":[]},{"level":2,"title":"selectLanguageText","slug":"selectlanguagetext","link":"#selectlanguagetext","children":[]},{"level":2,"title":"selectLanguageAriaLabel","slug":"selectlanguagearialabel","link":"#selectlanguagearialabel","children":[]},{"level":2,"title":"selectLanguageName","slug":"selectlanguagename","link":"#selectlanguagename","children":[]},{"level":2,"title":"navbarLabel","slug":"navbarlabel","link":"#navbarlabel","children":[]},{"level":2,"title":"pageNavbarLabel","slug":"pagenavbarlabel","link":"#pagenavbarlabel","children":[]},{"level":2,"title":"editLinkText","slug":"editlinktext","link":"#editlinktext","children":[]},{"level":2,"title":"lastUpdatedText","slug":"lastupdatedtext","link":"#lastupdatedtext","children":[]},{"level":2,"title":"contributorsText","slug":"contributorstext","link":"#contributorstext","children":[]},{"level":2,"title":"tip","slug":"tip","link":"#tip","children":[]},{"level":2,"title":"warning","slug":"warning","link":"#warning","children":[]},{"level":2,"title":"danger","slug":"danger","link":"#danger","children":[]},{"level":2,"title":"notFound","slug":"notfound","link":"#notfound","children":[]},{"level":2,"title":"backToHome","slug":"backtohome","link":"#backtohome","children":[]},{"level":2,"title":"openInNewWindow","slug":"openinnewwindow","link":"#openinnewwindow","children":[]},{"level":2,"title":"toggleColorMode","slug":"togglecolormode","link":"#togglecolormode","children":[]},{"level":2,"title":"toggleSidebar","slug":"togglesidebar","link":"#togglesidebar","children":[]}],"git":{"updatedTime":1706960075000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":3}]},"filePathRelative":"themes/default/locale.md"}');export{ie as comp,oe as data}; diff --git a/assets/locale.html-DZtMkHzN.js b/assets/locale.html-DZtMkHzN.js new file mode 100644 index 0000000000..616860d473 --- /dev/null +++ b/assets/locale.html-DZtMkHzN.js @@ -0,0 +1,21 @@ +import{_ as c,r as o,o as p,c as d,b as l,d as e,a as n,w as s,e as t}from"./app-DI49vYHA.js";const r={},u=l("h1",{id:"语言配置",tabindex:"-1"},[l("a",{class:"header-anchor",href:"#语言配置"},[l("span",null,"语言配置")])],-1),h=l("p",null,"这些选项用于配置与语言相关的文本。",-1),g=l("p",null,"如果你的站点是以英语以外的其他语言提供服务的,你应该为每个语言设置这些选项来提供翻译。",-1),_=l("h2",{id:"repolabel",tabindex:"-1"},[l("a",{class:"header-anchor",href:"#repolabel"},[l("span",null,"repoLabel")])],-1),b=l("li",null,[l("p",null,[e("类型: "),l("code",null,"string")])],-1),m=l("p",null,"详情:",-1),v=l("p",null,"项目仓库的标签。",-1),D=l("p",null,[e("它将被用作 "),l("em",null,"仓库链接"),e(" 的文字。"),l("em",null,"仓库链接"),e(" 将会显示为导航栏的最后一个元素。")],-1),x=l("h2",{id:"selectlanguagetext",tabindex:"-1"},[l("a",{class:"header-anchor",href:"#selectlanguagetext"},[l("span",null,"selectLanguageText")])],-1),f=l("li",null,[l("p",null,[e("类型: "),l("code",null,"string")])],-1),E=l("p",null,"详情:",-1),k=l("p",null,[l("em",null,"选择语言菜单"),e(" 的文字。")],-1),y={href:"https://v2.vuepress.vuejs.org/zh/config.html#locales",target:"_blank",rel:"noopener noreferrer"},C=l("em",null,"选择语言菜单",-1),w=t('

    selectLanguageAriaLabel

    • 类型: string

    • 详情:

      选择语言菜单aria-label 属性。

      它主要是为了站点的可访问性 (a11y) 。

    selectLanguageName

    ',3),L=l("li",null,[l("p",null,[e("类型: "),l("code",null,"string")])],-1),A=l("p",null,"详情:",-1),N=l("p",null,"Locale 的语言名称。",-1),z=l("em",null,"选择语言菜单",-1),T=l("li",null,[l("p",null,"示例:")],-1),F=t(`
    export default {
    +  locales: {
    +    '/': {
    +      lang: 'en-US',
    +    },
    +    '/zh/': {
    +      lang: 'zh-CN',
    +    },
    +  },
    +  theme: defaultTheme({
    +    locales: {
    +      '/': {
    +        selectLanguageName: 'English',
    +      },
    +      '/zh/': {
    +        selectLanguageName: '简体中文',
    +      },
    +    },
    +  }),
    +}
    +
    • 类型:null | string

    • 详情:

      导航栏中主导航 aria-label 属性的值。

    • 类型:null | string

    • 详情:

      下一页/上一页导航 aria-label 属性的值

    editLinkText

    • 类型: string

    • 默认值: 'Edit this page'

    • 详情:

      编辑此页 链接的文字。

    lastUpdatedText

    • 类型: string

    • 默认值: 'Last Updated'

    • 详情:

      最近更新时间戳 标签的文字。

    contributorsText

    • 类型: string

    • 默认值: 'Contributors'

    • 详情:

      贡献者列表 标签的文字。

    tip

    `,12),B=l("li",null,[l("p",null,[e("类型: "),l("code",null,"string")])],-1),I=l("li",null,[l("p",null,[e("默认值: "),l("code",null,"'TIP'")])],-1),S=l("p",null,"详情:",-1),R=l("h2",{id:"warning",tabindex:"-1"},[l("a",{class:"header-anchor",href:"#warning"},[l("span",null,"warning")])],-1),M=l("li",null,[l("p",null,[e("类型: "),l("code",null,"string")])],-1),U=l("li",null,[l("p",null,[e("默认值: "),l("code",null,"'WARNING'")])],-1),V=l("p",null,"详情:",-1),W=l("h2",{id:"danger",tabindex:"-1"},[l("a",{class:"header-anchor",href:"#danger"},[l("span",null,"danger")])],-1),H=l("li",null,[l("p",null,[e("类型: "),l("code",null,"string")])],-1),P=l("li",null,[l("p",null,[e("默认值: "),l("code",null,"'DANGER'")])],-1),G=l("p",null,"详情:",-1),j=t('

    notFound

    • 类型: string[]

    • 默认值: ['Not Found']

    • 详情:

      404 页面的提示信息。

      当用户进入 404 页面时,会从数组中随机选取一条信息进行展示。

    backToHome

    • 类型: string

    • 默认值: 'Back to home'

    • 详情:

      404 页面中 返回首页 链接的文字。

    openInNewWindow

    ',5),J=l("li",null,[l("p",null,[e("类型: "),l("code",null,"string")])],-1),O=l("li",null,[l("p",null,[e("默认值: "),l("code",null,"'open in new window'")])],-1),q=l("p",null,"详情:",-1),K=l("code",null,"sr-only",-1),Q=l("p",null,"它主要是为了站点的可访问性 (a11y) 。",-1),X=l("p",null,"参考:",-1),Y=l("h2",{id:"togglecolormode",tabindex:"-1"},[l("a",{class:"header-anchor",href:"#togglecolormode"},[l("span",null,"toggleColorMode")])],-1),Z=l("li",null,[l("p",null,[e("类型: "),l("code",null,"string")])],-1),$=l("li",null,[l("p",null,[e("默认值: "),l("code",null,"'toggle color mode'")])],-1),ll=l("li",null,[l("p",null,"详情:"),l("p",null,"切换颜色模式按钮的标题文字。"),l("p",null,"它主要是为了站点的可访问性 (a11y) 。")],-1),el=l("p",null,"参考:",-1),nl=t('

    toggleSidebar

    • 类型: string

    • 默认值: 'toggle sidebar'

    • 详情:

      切换侧边栏按钮的标题文字。

      它主要是为了站点的可访问性 (a11y) 。

    ',2);function al(sl,tl){const a=o("RouteLink"),i=o("ExternalLinkIcon");return p(),d("div",null,[u,h,g,_,l("ul",null,[b,l("li",null,[m,v,D,l("p",null,[e("如果你不明确指定该配置项,它将会根据 "),n(a,{to:"/zh/themes/default/config.html#repo"},{default:s(()=>[e("repo")]),_:1}),e(" 配置项自动推断。")])])]),x,l("ul",null,[f,l("li",null,[E,k,l("p",null,[e("如果你在站点配置中设置了多个 "),l("a",y,[e("locales"),n(i)]),e(" ,那么 "),C,e(" 就会显示在导航栏中仓库按钮的旁边。")])])]),w,l("ul",null,[L,l("li",null,[A,N,l("p",null,[e("该配置项 "),l("strong",null,[e("仅能在主题配置的 "),n(a,{to:"/zh/themes/default/config.html#locales"},{default:s(()=>[e("locales")]),_:1}),e(" 的内部生效")]),e(" 。它将被用作 locale 的语言名称,展示在 "),z,e(" 内。")])]),T]),F,l("ul",null,[B,I,l("li",null,[S,l("p",null,[e("Tip "),n(a,{to:"/zh/themes/default/markdown.html#%E8%87%AA%E5%AE%9A%E4%B9%89%E5%AE%B9%E5%99%A8"},{default:s(()=>[e("自定义容器")]),_:1}),e(" 的默认标题。")])])]),R,l("ul",null,[M,U,l("li",null,[V,l("p",null,[e("Warning "),n(a,{to:"/zh/themes/default/markdown.html#%E8%87%AA%E5%AE%9A%E4%B9%89%E5%AE%B9%E5%99%A8"},{default:s(()=>[e("自定义容器")]),_:1}),e(" 的默认标题。")])])]),W,l("ul",null,[H,P,l("li",null,[G,l("p",null,[e("Danger "),n(a,{to:"/zh/themes/default/markdown.html#%E8%87%AA%E5%AE%9A%E4%B9%89%E5%AE%B9%E5%99%A8"},{default:s(()=>[e("自定义容器")]),_:1}),e(" 的默认标题。")])])]),j,l("ul",null,[J,O,l("li",null,[q,l("p",null,[n(a,{to:"/zh/plugins/external-link-icon.html#externallinkicon"},{default:s(()=>[e("ExternalLinkIcon")]),_:1}),e(". 链接内的 "),K,e(" 文字。")]),Q]),l("li",null,[X,l("ul",null,[l("li",null,[n(a,{to:"/zh/themes/default/plugin.html#themeplugins-externallinkicon"},{default:s(()=>[e("默认主题 > 插件配置 > themePlugins.externalLinkIcon")]),_:1})])])])]),Y,l("ul",null,[Z,$,ll,l("li",null,[el,l("ul",null,[l("li",null,[n(a,{to:"/zh/themes/default/config.html#colormodeswitch"},{default:s(()=>[e("默认主题 > 配置 > colorModeSwitch")]),_:1})])])])]),nl])}const il=c(r,[["render",al],["__file","locale.html.vue"]]),cl=JSON.parse('{"path":"/zh/themes/default/locale.html","title":"语言配置","lang":"zh-CN","frontmatter":{},"headers":[{"level":2,"title":"repoLabel","slug":"repolabel","link":"#repolabel","children":[]},{"level":2,"title":"selectLanguageText","slug":"selectlanguagetext","link":"#selectlanguagetext","children":[]},{"level":2,"title":"selectLanguageAriaLabel","slug":"selectlanguagearialabel","link":"#selectlanguagearialabel","children":[]},{"level":2,"title":"selectLanguageName","slug":"selectlanguagename","link":"#selectlanguagename","children":[]},{"level":2,"title":"navbarLabel","slug":"navbarlabel","link":"#navbarlabel","children":[]},{"level":2,"title":"pageNavbarLabel","slug":"pagenavbarlabel","link":"#pagenavbarlabel","children":[]},{"level":2,"title":"editLinkText","slug":"editlinktext","link":"#editlinktext","children":[]},{"level":2,"title":"lastUpdatedText","slug":"lastupdatedtext","link":"#lastupdatedtext","children":[]},{"level":2,"title":"contributorsText","slug":"contributorstext","link":"#contributorstext","children":[]},{"level":2,"title":"tip","slug":"tip","link":"#tip","children":[]},{"level":2,"title":"warning","slug":"warning","link":"#warning","children":[]},{"level":2,"title":"danger","slug":"danger","link":"#danger","children":[]},{"level":2,"title":"notFound","slug":"notfound","link":"#notfound","children":[]},{"level":2,"title":"backToHome","slug":"backtohome","link":"#backtohome","children":[]},{"level":2,"title":"openInNewWindow","slug":"openinnewwindow","link":"#openinnewwindow","children":[]},{"level":2,"title":"toggleColorMode","slug":"togglecolormode","link":"#togglecolormode","children":[]},{"level":2,"title":"toggleSidebar","slug":"togglesidebar","link":"#togglesidebar","children":[]}],"git":{"updatedTime":1706960075000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":3}]},"filePathRelative":"zh/themes/default/locale.md"}');export{il as comp,cl as data}; diff --git a/assets/markdown.html-BFz9lbN1.js b/assets/markdown.html-BFz9lbN1.js new file mode 100644 index 0000000000..c30ebe0ff6 --- /dev/null +++ b/assets/markdown.html-BFz9lbN1.js @@ -0,0 +1,50 @@ +import{_ as d,r as e,o as r,c as u,a as n,b as s,w as a,d as l,e as p}from"./app-DI49vYHA.js";const D={},m=s("h1",{id:"markdown",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#markdown"},[s("span",null,"Markdown")])],-1),v=s("h2",{id:"自定义容器",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#自定义容器"},[s("span",null,"自定义容器")])],-1),b=p(`

    使用:

    ::: <type> [title]
    +[content]
    +:::
    +

    type 是必需的, titlecontent 是可选的。

    支持的 type 有:

    `,4),y=s("li",null,[s("code",null,"tip")],-1),g=s("li",null,[s("code",null,"warning")],-1),h=s("li",null,[s("code",null,"danger")],-1),_=s("li",null,[s("code",null,"details")],-1),k=s("ul",null,[s("li",null,[s("code",null,"code-group")]),s("li",null,[s("code",null,"code-group-item")])],-1),C=s("li",null,[s("p",null,"示例 1 (默认标题):")],-1),E=p(`

    输入

    ::: tip
    +这是一个提示
    +:::
    +
    +::: warning
    +这是一个警告
    +:::
    +
    +::: danger
    +这是一个危险警告
    +:::
    +
    +::: details
    +这是一个 details 标签
    +:::
    +

    输出

    提示

    这是一个提示

    注意

    这是一个警告

    警告

    这是一个危险警告

    这是一个 details 标签

    • 示例 2 (自定义标题):

    输入

    ::: danger STOP
    +危险区域,禁止通行
    +:::
    +
    +::: details 点击查看代码
    +
    +\`\`\`ts
    +console.log('你好,VuePress!')
    +\`\`\`
    +
    +:::
    +

    输出

    STOP

    危险区域,禁止通行

    点击查看代码
    console.log('你好,VuePress!')
    +
    • 示例 3 (Code Group 别名):

    输入

    :::: code-group
    +::: code-group-item FOO
    +
    +\`\`\`ts
    +const foo = 'foo'
    +\`\`\`
    +
    +:::
    +
    +::: code-group-item BAR
    +
    +\`\`\`ts
    +const bar = 'bar'
    +\`\`\`
    +
    +:::
    +::::
    +

    输出

    `,17),f=s("div",{class:"language-typescript line-numbers-mode","data-ext":"ts","data-title":"ts"},[s("pre",{class:"shiki dark-plus",style:{"background-color":"#1E1E1E",color:"#D4D4D4"},tabindex:"0"},[s("code",null,[s("span",{class:"line"},[s("span",{style:{color:"#569CD6"}},"const"),s("span",{style:{color:"#4FC1FF"}}," foo"),s("span",{style:{color:"#D4D4D4"}}," = "),s("span",{style:{color:"#CE9178"}},"'foo'")]),l(` +`),s("span",{class:"line"})])]),s("div",{class:"line-numbers","aria-hidden":"true"},[s("div",{class:"line-number"})])],-1),x=s("div",{class:"language-typescript line-numbers-mode","data-ext":"ts","data-title":"ts"},[s("pre",{class:"shiki dark-plus",style:{"background-color":"#1E1E1E",color:"#D4D4D4"},tabindex:"0"},[s("code",null,[s("span",{class:"line"},[s("span",{style:{color:"#569CD6"}},"const"),s("span",{style:{color:"#4FC1FF"}}," bar"),s("span",{style:{color:"#D4D4D4"}}," = "),s("span",{style:{color:"#CE9178"}},"'bar'")]),l(` +`),s("span",{class:"line"})])]),s("div",{class:"line-numbers","aria-hidden":"true"},[s("div",{class:"line-number"})])],-1);function w(F,N){const c=e("NpmBadge"),i=e("RouteLink"),o=e("CodeGroupItem"),t=e("CodeGroup");return r(),u("div",null,[m,n(c,{package:"@vuepress/theme-default"}),v,s("ul",null,[s("li",null,[b,s("ul",null,[y,g,h,_,s("li",null,[n(i,{to:"/zh/themes/default/components.html#codegroup"},{default:a(()=>[l("CodeGroup")]),_:1}),l(" 和 "),n(i,{to:"/zh/themes/default/components.html#codegroupitem"},{default:a(()=>[l("CodeGroupItem")]),_:1}),l(" 的别名: "),k])])]),C]),E,n(t,null,{default:a(()=>[n(o,{title:"FOO"},{default:a(()=>[f]),_:1}),n(o,{title:"BAR"},{default:a(()=>[x]),_:1})]),_:1})])}const G=d(D,[["render",w],["__file","markdown.html.vue"]]),O=JSON.parse('{"path":"/zh/themes/default/markdown.html","title":"Markdown","lang":"zh-CN","frontmatter":{},"headers":[{"level":2,"title":"自定义容器","slug":"自定义容器","link":"#自定义容器","children":[]}],"git":{"updatedTime":1706605723000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":1}]},"filePathRelative":"zh/themes/default/markdown.md"}');export{G as comp,O as data}; diff --git a/assets/markdown.html-QGS-wLl9.js b/assets/markdown.html-QGS-wLl9.js new file mode 100644 index 0000000000..6777963fbe --- /dev/null +++ b/assets/markdown.html-QGS-wLl9.js @@ -0,0 +1,50 @@ +import{_ as d,r as e,o as r,c as u,a,b as s,d as n,w as l,e as c}from"./app-DI49vYHA.js";const D={},m=s("h1",{id:"markdown",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#markdown"},[s("span",null,"Markdown")])],-1),v=s("h2",{id:"custom-containers",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#custom-containers"},[s("span",null,"Custom Containers")])],-1),b=c(`

    Usage:

    ::: <type> [title]
    +[content]
    +:::
    +

    The type is required, and the title and content are optional.

    Supported type :

    `,4),y=s("li",null,[s("code",null,"tip")],-1),g=s("li",null,[s("code",null,"warning")],-1),h=s("li",null,[s("code",null,"danger")],-1),_=s("li",null,[s("code",null,"details")],-1),k=s("ul",null,[s("li",null,[s("code",null,"code-group")]),s("li",null,[s("code",null,"code-group-item")])],-1),C=s("li",null,[s("p",null,"Example 1 (default title):")],-1),E=c(`

    Input

    ::: tip
    +This is a tip
    +:::
    +
    +::: warning
    +This is a warning
    +:::
    +
    +::: danger
    +This is a dangerous warning
    +:::
    +
    +::: details
    +This is a details block
    +:::
    +

    Output

    TIP

    This is a tip

    WARNING

    This is a warning

    DANGER

    This is a dangerous warning

    This is a details block

    • Example 2 (custom title):

    Input

    ::: danger STOP
    +Danger zone, do not proceed
    +:::
    +
    +::: details Click me to view the code
    +
    +\`\`\`ts
    +console.log('Hello, VuePress!')
    +\`\`\`
    +
    +:::
    +

    Output

    STOP

    Danger zone, do not proceed

    Click me to view the code
    console.log('Hello, VuePress!')
    +
    • Example 3 (code group alias):

    Input

    :::: code-group
    +::: code-group-item FOO
    +
    +\`\`\`ts
    +const foo = 'foo'
    +\`\`\`
    +
    +:::
    +
    +::: code-group-item BAR
    +
    +\`\`\`ts
    +const bar = 'bar'
    +\`\`\`
    +
    +:::
    +::::
    +

    Output

    `,17),f=s("div",{class:"language-typescript line-numbers-mode","data-ext":"ts","data-title":"ts"},[s("pre",{class:"shiki dark-plus",style:{"background-color":"#1E1E1E",color:"#D4D4D4"},tabindex:"0"},[s("code",null,[s("span",{class:"line"},[s("span",{style:{color:"#569CD6"}},"const"),s("span",{style:{color:"#4FC1FF"}}," foo"),s("span",{style:{color:"#D4D4D4"}}," = "),s("span",{style:{color:"#CE9178"}},"'foo'")]),n(` +`),s("span",{class:"line"})])]),s("div",{class:"line-numbers","aria-hidden":"true"},[s("div",{class:"line-number"})])],-1),x=s("div",{class:"language-typescript line-numbers-mode","data-ext":"ts","data-title":"ts"},[s("pre",{class:"shiki dark-plus",style:{"background-color":"#1E1E1E",color:"#D4D4D4"},tabindex:"0"},[s("code",null,[s("span",{class:"line"},[s("span",{style:{color:"#569CD6"}},"const"),s("span",{style:{color:"#4FC1FF"}}," bar"),s("span",{style:{color:"#D4D4D4"}}," = "),s("span",{style:{color:"#CE9178"}},"'bar'")]),n(` +`),s("span",{class:"line"})])]),s("div",{class:"line-numbers","aria-hidden":"true"},[s("div",{class:"line-number"})])],-1);function w(F,T){const t=e("NpmBadge"),i=e("RouteLink"),o=e("CodeGroupItem"),p=e("CodeGroup");return r(),u("div",null,[m,a(t,{package:"@vuepress/theme-default"}),v,s("ul",null,[s("li",null,[b,s("ul",null,[y,g,h,_,s("li",null,[n("Alias of "),a(i,{to:"/themes/default/components.html#codegroup"},{default:l(()=>[n("CodeGroup")]),_:1}),n(" and "),a(i,{to:"/themes/default/components.html#codegroupitem"},{default:l(()=>[n("CodeGroupItem")]),_:1}),n(": "),k])])]),C]),E,a(p,null,{default:l(()=>[a(o,{title:"FOO"},{default:l(()=>[f]),_:1}),a(o,{title:"BAR"},{default:l(()=>[x]),_:1})]),_:1})])}const O=d(D,[["render",w],["__file","markdown.html.vue"]]),A=JSON.parse('{"path":"/themes/default/markdown.html","title":"Markdown","lang":"en-US","frontmatter":{},"headers":[{"level":2,"title":"Custom Containers","slug":"custom-containers","link":"#custom-containers","children":[]}],"git":{"updatedTime":1706605723000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":1}]},"filePathRelative":"themes/default/markdown.md"}');export{O as comp,A as data}; diff --git a/assets/medium-zoom.html-BrYLaxXk.js b/assets/medium-zoom.html-BrYLaxXk.js new file mode 100644 index 0000000000..2c0426457a --- /dev/null +++ b/assets/medium-zoom.html-BrYLaxXk.js @@ -0,0 +1,31 @@ +import{_ as p,r as o,o as c,c as t,a as e,b as s,d as n,e as a}from"./app-DI49vYHA.js";const r={},d=s("h1",{id:"medium-zoom",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#medium-zoom"},[s("span",null,"medium-zoom")])],-1),m={href:"https://github.com/francoischalifour/medium-zoom#readme",target:"_blank",rel:"noopener noreferrer"},u=a(`

    该插件已经集成到默认主题中。

    使用方法

    npm i -D @vuepress/plugin-medium-zoom@next
    +
    import { mediumZoomPlugin } from '@vuepress/plugin-medium-zoom'
    +
    +export default {
    +  plugins: [
    +    mediumZoomPlugin({
    +      // 配置项
    +    }),
    +  ],
    +}
    +

    配置项

    selector

    • 类型: string

    • 默认值: ':not(a) > img'

    • 详情:

      可缩放的图片的选择器。

      默认情况下,该插件会使 <a> 标签以外的所有图片都支持缩放。

    delay

    • 类型: number

    • 默认值: 500

    • 详情:

      以毫秒为单位的延迟。

      在切换路由进入一个新页面时,该插件会在一定延迟后才使页面内的图片支持缩放。

    zoomOptions

    `,10),D=s("li",null,[s("p",null,[n("类型: "),s("code",null,"Object")])],-1),h=s("li",null,[s("p",null,"详情:"),s("p",null,"medium-zoom 的配置项。")],-1),v=s("p",null,"参考:",-1),y={href:"https://github.com/francoischalifour/medium-zoom#options",target:"_blank",rel:"noopener noreferrer"},b=a(`

    样式

    你可以通过 zoomOptions 对大部分的缩放样式进行自定义,不过作为补充,该插件同样提供了一些 CSS 变量:

    :root {
    +  --medium-zoom-z-index: 100;
    +  --medium-zoom-bg-color: #ffffff;
    +  --medium-zoom-opacity: 1;
    +}
    +

    Composition API

    useMediumZoom

    `,5),C=s("p",null,"详情:",-1),_=s("code",null,"Zoom",-1),g={href:"https://github.com/francoischalifour/medium-zoom#methods",target:"_blank",rel:"noopener noreferrer"},f=s("p",null,"该插件会在切换路由进入当前页面时使图片支持缩放。但如果你要动态添加新图片,那么你可能就需要这个方法来让这些新图片也支持缩放。",-1),z=s("p",null,[n("该插件在 "),s("code",null,"Zoom"),n(" 实例上额外添加了一个 "),s("code",null,"refresh"),n(" 方法,它将使用 "),s("a",{href:"#selector"},"selector"),n(" 作为默认参数,先调用 "),s("code",null,"zoom.detach()"),n(" 再调用 "),s("code",null,"zoom.attach()"),n(" ,便于你快速刷新当前页面图片的缩放状态。")],-1),k=s("li",null,[s("p",null,"示例:")],-1),E=a(`
    import { nextTick } from 'vue'
    +import { useMediumZoom } from '@vuepress/plugin-medium-zoom/client'
    +
    +export default {
    +  setup() {
    +    const zoom = useMediumZoom()
    +
    +    // ... 进行了一些操作,在当前页面添加了新的图片
    +
    +    // 此时你可能需要手动调用 \`refresh\` 来让这些新图片支持缩放
    +    nextTick(() => {
    +      zoom.refresh()
    +    })
    +  },
    +}
    +
    `,1);function x(A,F){const i=o("NpmBadge"),l=o("ExternalLinkIcon");return c(),t("div",null,[d,e(i,{package:"@vuepress/plugin-medium-zoom"}),s("p",null,[n("将 "),s("a",m,[n("medium-zoom"),e(l)]),n(" 集成到 VuePress 中,为图片提供可缩放的功能。")]),u,s("ul",null,[D,h,s("li",null,[v,s("ul",null,[s("li",null,[s("a",y,[n("medium-zoom > Options"),e(l)])])])])]),b,s("ul",null,[s("li",null,[C,s("p",null,[n("返回该插件使用的 "),_,n(" 实例,便于你直接使用实例上的 "),s("a",g,[n("methods"),e(l)]),n(" 。")]),f,z]),k]),E])}const B=p(r,[["render",x],["__file","medium-zoom.html.vue"]]),N=JSON.parse('{"path":"/zh/plugins/medium-zoom.html","title":"medium-zoom","lang":"zh-CN","frontmatter":{},"headers":[{"level":2,"title":"使用方法","slug":"使用方法","link":"#使用方法","children":[]},{"level":2,"title":"配置项","slug":"配置项","link":"#配置项","children":[{"level":3,"title":"selector","slug":"selector","link":"#selector","children":[]},{"level":3,"title":"delay","slug":"delay","link":"#delay","children":[]},{"level":3,"title":"zoomOptions","slug":"zoomoptions","link":"#zoomoptions","children":[]}]},{"level":2,"title":"样式","slug":"样式","link":"#样式","children":[]},{"level":2,"title":"Composition API","slug":"composition-api","link":"#composition-api","children":[{"level":3,"title":"useMediumZoom","slug":"usemediumzoom","link":"#usemediumzoom","children":[]}]}],"git":{"updatedTime":1706605723000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":1}]},"filePathRelative":"zh/plugins/medium-zoom.md"}');export{B as comp,N as data}; diff --git a/assets/medium-zoom.html-DBOIkrrJ.js b/assets/medium-zoom.html-DBOIkrrJ.js new file mode 100644 index 0000000000..5acac5bb5b --- /dev/null +++ b/assets/medium-zoom.html-DBOIkrrJ.js @@ -0,0 +1,31 @@ +import{_ as t,r as o,o as p,c,a as n,b as s,d as e,e as a}from"./app-DI49vYHA.js";const r={},d=s("h1",{id:"medium-zoom",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#medium-zoom"},[s("span",null,"medium-zoom")])],-1),m={href:"https://github.com/francoischalifour/medium-zoom#readme",target:"_blank",rel:"noopener noreferrer"},u=a(`

    This plugin has been integrated into the default theme.

    Usage

    npm i -D @vuepress/plugin-medium-zoom@next
    +
    import { mediumZoomPlugin } from '@vuepress/plugin-medium-zoom'
    +
    +export default {
    +  plugins: [
    +    mediumZoomPlugin({
    +      // options
    +    }),
    +  ],
    +}
    +

    Options

    selector

    • Type: string

    • Default: ':not(a) > img'

    • Details:

      Selector of zoomable images.

      By default this plugin will make all images zoomable except those inside <a> tags.

    delay

    • Type: number

    • Default: 500

    • Details:

      Delay in milliseconds.

      After navigating to a new page, this plugin will make images zoomable with a delay.

    zoomOptions

    `,10),h=s("li",null,[s("p",null,[e("Type: "),s("code",null,"Object")])],-1),D=s("li",null,[s("p",null,"Details:"),s("p",null,"Options for medium-zoom.")],-1),y=s("p",null,"Also see:",-1),v={href:"https://github.com/francoischalifour/medium-zoom#options",target:"_blank",rel:"noopener noreferrer"},g=a(`

    Styles

    You can customize most of the zoom styles via zoomOptions, while this plugin also provides some CSS variables for additional customization:

    :root {
    +  --medium-zoom-z-index: 100;
    +  --medium-zoom-bg-color: #ffffff;
    +  --medium-zoom-opacity: 1;
    +}
    +

    Composition API

    useMediumZoom

    `,5),b=s("p",null,"Details:",-1),C=s("code",null,"Zoom",-1),f={href:"https://github.com/francoischalifour/medium-zoom#methods",target:"_blank",rel:"noopener noreferrer"},_=s("p",null,"This plugin will make images zoomable after navigating to current page. But if you are going to add new images dynamically, you may need this method to make those new images zoomable, too.",-1),z=s("p",null,[e("This plugin adds an extra "),s("code",null,"refresh"),e(" method on the "),s("code",null,"Zoom"),e(" instance, which will call "),s("code",null,"zoom.detach()"),e(" then "),s("code",null,"zoom.attach()"),e(" with the "),s("a",{href:"#selector"},"selector"),e(" as the default parameter. It will help you to refresh the zoomable images for current page.")],-1),k=s("li",null,[s("p",null,"Example:")],-1),x=a(`
    import { nextTick } from 'vue'
    +import { useMediumZoom } from '@vuepress/plugin-medium-zoom/client'
    +
    +export default {
    +  setup() {
    +    const zoom = useMediumZoom()
    +
    +    // ... do something to add new images in current page
    +
    +    // then you may need to call \`refresh\` manually to make those new images zoomable
    +    nextTick(() => {
    +      zoom.refresh()
    +    })
    +  },
    +}
    +
    `,1);function E(A,w){const i=o("NpmBadge"),l=o("ExternalLinkIcon");return p(),c("div",null,[d,n(i,{package:"@vuepress/plugin-medium-zoom"}),s("p",null,[e("Integrate "),s("a",m,[e("medium-zoom"),n(l)]),e(" into VuePress, which can provide the ability to zoom images.")]),u,s("ul",null,[h,D,s("li",null,[y,s("ul",null,[s("li",null,[s("a",v,[e("medium-zoom > Options"),n(l)])])])])]),g,s("ul",null,[s("li",null,[b,s("p",null,[e("Returns the "),C,e(" instance that used by this plugin, so that you can use the instance "),s("a",f,[e("methods"),n(l)]),e(" directly.")]),_,z]),k]),x])}const B=t(r,[["render",E],["__file","medium-zoom.html.vue"]]),T=JSON.parse('{"path":"/plugins/medium-zoom.html","title":"medium-zoom","lang":"en-US","frontmatter":{},"headers":[{"level":2,"title":"Usage","slug":"usage","link":"#usage","children":[]},{"level":2,"title":"Options","slug":"options","link":"#options","children":[{"level":3,"title":"selector","slug":"selector","link":"#selector","children":[]},{"level":3,"title":"delay","slug":"delay","link":"#delay","children":[]},{"level":3,"title":"zoomOptions","slug":"zoomoptions","link":"#zoomoptions","children":[]}]},{"level":2,"title":"Styles","slug":"styles","link":"#styles","children":[]},{"level":2,"title":"Composition API","slug":"composition-api","link":"#composition-api","children":[{"level":3,"title":"useMediumZoom","slug":"usemediumzoom","link":"#usemediumzoom","children":[]}]}],"git":{"updatedTime":1706605723000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":1}]},"filePathRelative":"plugins/medium-zoom.md"}');export{B as comp,T as data}; diff --git a/assets/nprogress.html-D6c03qSI.js b/assets/nprogress.html-D6c03qSI.js new file mode 100644 index 0000000000..8cdce70523 --- /dev/null +++ b/assets/nprogress.html-D6c03qSI.js @@ -0,0 +1,11 @@ +import{_ as o,r as e,o as p,c as i,a,b as s,d as n,e as t}from"./app-DI49vYHA.js";const c={},d=s("h1",{id:"nprogress-插件",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#nprogress-插件"},[s("span",null,"nprogress 插件")])],-1),D={href:"https://github.com/rstacruz/nprogress",target:"_blank",rel:"noopener noreferrer"},u=t(`

    该插件已经集成到默认主题中。

    使用方法

    npm i -D @vuepress/plugin-nprogress@next
    +
    import { nprogressPlugin } from '@vuepress/plugin-nprogress'
    +
    +export default {
    +  plugins: [nprogressPlugin()],
    +}
    +

    样式

    你可以通过 CSS 变量来自定义进度条的样式:

    :root {
    +  --nprogress-color: #29d;
    +  --nprogress-z-index: 1031;
    +}
    +
    `,7);function m(g,h){const l=e("NpmBadge"),r=e("ExternalLinkIcon");return p(),i("div",null,[d,a(l,{package:"@vuepress/plugin-nprogress"}),s("p",null,[n("将 "),s("a",D,[n("nprogress"),a(r)]),n(" 集成到 VuePress 中,在切换到另一个页面时会展示进度条。")]),u])}const y=o(c,[["render",m],["__file","nprogress.html.vue"]]),b=JSON.parse('{"path":"/zh/plugins/nprogress.html","title":"nprogress","lang":"zh-CN","frontmatter":{"title":"nprogress"},"headers":[{"level":2,"title":"使用方法","slug":"使用方法","link":"#使用方法","children":[]},{"level":2,"title":"样式","slug":"样式","link":"#样式","children":[]}],"git":{"updatedTime":1706605723000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":1}]},"filePathRelative":"zh/plugins/nprogress.md"}');export{y as comp,b as data}; diff --git a/assets/nprogress.html-fM7jj7yn.js b/assets/nprogress.html-fM7jj7yn.js new file mode 100644 index 0000000000..f8f9f8a771 --- /dev/null +++ b/assets/nprogress.html-fM7jj7yn.js @@ -0,0 +1,11 @@ +import{_ as o,r as n,o as p,c as t,a,b as s,d as e,e as i}from"./app-DI49vYHA.js";const c={},d=s("h1",{id:"nprogress-plugin",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#nprogress-plugin"},[s("span",null,"nprogress Plugin")])],-1),u={href:"https://github.com/rstacruz/nprogress",target:"_blank",rel:"noopener noreferrer"},g=i(`

    This plugin has been integrated into the default theme.

    Usage

    npm i -D @vuepress/plugin-nprogress@next
    +
    import { nprogressPlugin } from '@vuepress/plugin-nprogress'
    +
    +export default {
    +  plugins: [nprogressPlugin()],
    +}
    +

    Styles

    You can customize the style of the progress bar via CSS variables:

    :root {
    +  --nprogress-color: #29d;
    +  --nprogress-z-index: 1031;
    +}
    +
    `,7);function D(h,m){const l=n("NpmBadge"),r=n("ExternalLinkIcon");return p(),t("div",null,[d,a(l,{package:"@vuepress/plugin-nprogress"}),s("p",null,[e("Integrate "),s("a",u,[e("nprogress"),a(r)]),e(" into VuePress, which can provide a progress bar when navigating to another page.")]),g])}const y=o(c,[["render",D],["__file","nprogress.html.vue"]]),b=JSON.parse('{"path":"/plugins/nprogress.html","title":"nprogress","lang":"en-US","frontmatter":{"title":"nprogress"},"headers":[{"level":2,"title":"Usage","slug":"usage","link":"#usage","children":[]},{"level":2,"title":"Styles","slug":"styles","link":"#styles","children":[]}],"git":{"updatedTime":1706605723000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":1}]},"filePathRelative":"plugins/nprogress.md"}');export{y as comp,b as data}; diff --git a/assets/page.html-CWQo-Wbb.js b/assets/page.html-CWQo-Wbb.js new file mode 100644 index 0000000000..1574f5804f --- /dev/null +++ b/assets/page.html-CWQo-Wbb.js @@ -0,0 +1,77 @@ +import{_ as s,o as n,c as a,e as l}from"./app-DI49vYHA.js";const e={},p=l(`

    页面相关

    这些函数为你的页面生成常见信息。

    getPageExcerpt

    获取页面摘要。

    export interface PageExcerptOptions {
    +  /**
    +   * 摘要分隔符
    +   *
    +   * @default "<!-- more -->"
    +   */
    +  separator?: string
    +
    +  /**
    +   * 摘要的长度
    +   *
    +   * @description 摘要的长度会尽可能的接近这个值
    +   *
    +   * @default 300
    +   */
    +  length?: number
    +
    +  /**
    +   * 被认为是自定义元素的标签
    +   *
    +   * @description 用于判断一个标签是否是自定义元素,因为在摘要中,所有的未知标签都会被移除。
    +   */
    +  isCustomElement?: (tagName: string) => boolean
    +
    +  /**
    +   * 是否保留页面标题 (第一个 h1)
    +   *
    +   * @default false
    +   */
    +  keepPageTitle?: boolean
    +
    +  /**
    +   * 是否保留代码块的标签,诸如行号和高亮行
    +   *
    +   * @default false
    +   */
    +  keepFenceDom?: boolean
    +}
    +
    +export const getPageExcerpt: (
    +  app: App,
    +  page: Page,
    +  options?: PageExcerptOptions,
    +) => string
    +

    getPageText

    获取页面纯文本。

    export interface PageTextOptions {
    +  /**
    +   * 是否将文字转换成单行内容
    +   *
    +   * @default false
    +   */
    +  singleLine?: boolean
    +
    +  /**
    +   * 文字的长度
    +   *
    +   * @description 文字的长度会尽可能的接近这个值
    +   *
    +   * @default 300
    +   */
    +  length?: number
    +
    +  /**
    +   * 需要移除的标签
    +   *
    +   * @description 默认情况下表格和代码块会被移除
    +   *
    +   * @default ['table', 'pre']
    +   */
    +  removedTags?: string[]
    +}
    +
    +export const getPageText: (
    +  app: App,
    +  page: Page,
    +  options?: PageTextOptions,
    +) => string
    +
    `,8),i=[p];function o(c,r){return n(),a("div",null,i)}const d=s(e,[["render",o],["__file","page.html.vue"]]),v=JSON.parse('{"path":"/zh/tools/helper/node/page.html","title":"页面相关","lang":"zh-CN","frontmatter":{},"headers":[{"level":2,"title":"getPageExcerpt","slug":"getpageexcerpt","link":"#getpageexcerpt","children":[]},{"level":2,"title":"getPageText","slug":"getpagetext","link":"#getpagetext","children":[]}],"git":{"updatedTime":1706960075000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":1}]},"filePathRelative":"zh/tools/helper/node/page.md"}');export{d as comp,v as data}; diff --git a/assets/page.html-DygfZiqD.js b/assets/page.html-DygfZiqD.js new file mode 100644 index 0000000000..130536e1f1 --- /dev/null +++ b/assets/page.html-DygfZiqD.js @@ -0,0 +1,77 @@ +import{_ as s,o as n,c as a,e as l}from"./app-DI49vYHA.js";const e={},p=l(`

    Page Related

    These functions generate common information for your pages.

    getPageExcerpt

    Get the excerpt of the page.

    export interface PageExcerptOptions {
    +  /**
    +   * Excerpt separator
    +   *
    +   * @default "<!-- more -->"
    +   */
    +  separator?: string
    +
    +  /**
    +   * Length of excerpt
    +   *
    +   * @description Excerpt length will be the minimal possible length reaching this value
    +   *
    +   * @default 300
    +   */
    +  length?: number
    +
    +  /**
    +   * Tags which is considered as custom elements
    +   *
    +   * @description This is used to determine whether a tag is a custom element since all unknown tags are removed in excerpt.
    +   */
    +  isCustomElement?: (tagName: string) => boolean
    +
    +  /**
    +   * Whether keep page title (first h1) in excerpt
    +   *
    +   * @default false
    +   */
    +  keepPageTitle?: boolean
    +
    +  /**
    +   * Whether preserve tags like line numbers and highlight lines for code blocks
    +   *
    +   * @default false
    +   */
    +  keepFenceDom?: boolean
    +}
    +
    +export const getPageExcerpt: (
    +  app: App,
    +  page: Page,
    +  options?: PageExcerptOptions,
    +) => string
    +

    getPageText

    Get plain text of the page.

    export interface PageTextOptions {
    +  /**
    +   * Whether convert text to single line content
    +   *
    +   * @default false
    +   */
    +  singleLine?: boolean
    +
    +  /**
    +   * Length of text
    +   *
    +   * @description Text length will be the minimal possible length reaching this value
    +   *
    +   * @default 300
    +   */
    +  length?: number
    +
    +  /**
    +   * Tags to be removed
    +   *
    +   * @description Table and code blocks are removed by default.
    +   *
    +   * @default ['table', 'pre']
    +   */
    +  removedTags?: string[]
    +}
    +
    +export const getPageText: (
    +  app: App,
    +  page: Page,
    +  options?: PageTextOptions,
    +) => string
    +
    `,8),i=[p];function o(c,t){return n(),a("div",null,i)}const d=s(e,[["render",o],["__file","page.html.vue"]]),v=JSON.parse('{"path":"/tools/helper/node/page.html","title":"Page Related","lang":"en-US","frontmatter":{},"headers":[{"level":2,"title":"getPageExcerpt","slug":"getpageexcerpt","link":"#getpageexcerpt","children":[]},{"level":2,"title":"getPageText","slug":"getpagetext","link":"#getpagetext","children":[]}],"git":{"updatedTime":1706960075000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":1}]},"filePathRelative":"tools/helper/node/page.md"}');export{d as comp,v as data}; diff --git a/assets/palette.html-CT6-ohwO.js b/assets/palette.html-CT6-ohwO.js new file mode 100644 index 0000000000..f73a05c803 --- /dev/null +++ b/assets/palette.html-CT6-ohwO.js @@ -0,0 +1,39 @@ +import{_ as p,r as n,o,c as r,a as l,b as s,d as e,e as t}from"./app-DI49vYHA.js";const c={},d=s("h1",{id:"palette",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#palette"},[s("span",null,"palette")])],-1),u=t(`

    Provide palette support for your theme.

    This plugin is mainly used to develop themes, and has been integrated into the default theme. You won't need to use it directly in most cases.

    For theme authors, this plugin will help you to provide styles customization for users.

    Usage

    npm i -D @vuepress/plugin-palette@next
    +
    import { palettePlugin } from '@vuepress/plugin-palette'
    +
    +export default {
    +  plugins: [
    +    palettePlugin({
    +      // options
    +    }),
    +  ],
    +}
    +

    Palette and Style

    This plugin will provide a @vuepress/plugin-palette/palette (palette file) and a @vuepress/plugin-palette/style (style file) to be imported in your theme styles.

    `,8),y={href:"https://developer.mozilla.org/en-US/docs/Web/CSS/Using_CSS_custom_properties",target:"_blank",rel:"noopener noreferrer"},h={href:"https://sass-lang.com/documentation/variables",target:"_blank",rel:"noopener noreferrer"},v={href:"http://lesscss.org/features/#variables-feature",target:"_blank",rel:"noopener noreferrer"},m={href:"https://stylus-lang.com/docs/variables.html",target:"_blank",rel:"noopener noreferrer"},D=t(`

    The style file is used for overriding the default styles or adding extra styles, so it's likely to be imported at the end of your theme styles.

    Usage

    Use this plugin in your theme, assuming you are using SASS:

    export default {
    +  // ...
    +  plugins: [palettePlugin({ preset: 'sass' })],
    +}
    +

    Usage of Palette

    Import the plugin's palette file where your theme needs to use the corresponding variables, such as in the Layout.vue file:

    <template>
    +  <h1 class="palette-title">Hello, Palette!</h1>
    +</template>
    +
    +<style lang="scss">
    +/* import variables from the plugin's palette file */
    +@import '@vuepress/plugin-palette/palette';
    +
    +/* set default value for variables */
    +$color: red !default;
    +
    +/* use variables in your styles */
    +.palette-title {
    +  color: $color;
    +}
    +</style>
    +

    Then users can customize variables in .vuepress/styles/palette.scss:

    $color: green;
    +

    Usage of Style

    Import the plugin's style file after your theme's styles, for example, in the clientConfigFile:

    // import your theme's style file
    +import 'path/to/your/theme/style'
    +// import the plugin's style file
    +import '@vuepress/plugin-palette/style'
    +

    Then users can add extra styles in .vuepress/styles/index.scss and override the default styles of your theme:

    h1 {
    +  font-size: 2.5rem;
    +}
    +

    Options

    preset

    • Type: 'css' | 'sass' | 'less' | 'stylus'

    • Default: 'css'

    • Details:

      Set preset for other options.

      If you don't need advanced customization of the plugin, it's recommended to only set this option and omit other options.

    userPaletteFile

    • Type: string

    • Default:

      • css: '.vuepress/styles/palette.css'
      • sass: '.vuepress/styles/palette.scss'
      • less: '.vuepress/styles/palette.less'
      • stylus: '.vuepress/styles/palette.styl'
    • Details:

      File path of the user palette file, relative to source directory.

      The default value depends on the preset option.

      The file is where users define style variables, and it's recommended to keep the default file path as a convention.

    tempPaletteFile

    • Type: string

    • Default:

      • css: 'styles/palette.css'
      • sass: 'styles/palette.scss'
      • less: 'styles/palette.less'
      • stylus: 'styles/palette.styl'
    • Details:

      File path of the generated palette temp file, relative to temp directory.

      The default value depends on the preset option.

      You should import the palette file via '@vuepress/plugin-palette/palette' alias, so you don't need to change this option in most cases.

    userStyleFile

    • Type: string

    • Default:

      • css: '.vuepress/styles/index.css'
      • sass: '.vuepress/styles/index.scss'
      • less: '.vuepress/styles/index.less'
      • stylus: '.vuepress/styles/index.styl'
    • Details:

      File path of the user style file, relative to source directory.

      The default value depends on the preset option.

      The file is where users override default styles or add extra styles, and it's recommended to keep the default file path as a convention.

    tempStyleFile

    • Type: string

    • Default:

      • css: 'styles/index.css'
      • sass: 'styles/index.scss'
      • less: 'styles/index.less'
      • stylus: 'styles/index.styl'
    • Details:

      File path of the generated style temp file, relative to temp directory.

      The default value depends on the preset option.

      You should import the style file via '@vuepress/plugin-palette/style' alias, so you don't need to change this option in most cases.

    importCode

    • Type: (filePath: string) => string

    • Default:

      • css: (filePath) => \`@import '\${filePath}';\\n\`
      • sass: (filePath) => \`@forward 'file:///\${filePath}';\\n\`
      • less: (filePath) => \`@import '\${filePath}';\\n\`
      • stylus: (filePath) => \`@require '\${filePath}';\\n\`
    • Details:

      Function to generate import code.

      The default value depends on the preset option.

      This option is used for generating tempPaletteFile and tempStyleFile, and you don't need to change this option in most cases.

    `,27);function f(g,b){const i=n("NpmBadge"),a=n("ExternalLinkIcon");return o(),r("div",null,[d,l(i,{package:"@vuepress/plugin-palette"}),u,s("p",null,[e("The palette file is used for defining style variables, so it's likely to be imported at the beginning of your theme styles. For example, users can define "),s("a",y,[e("CSS variables"),l(a)]),e(", "),s("a",h,[e("SASS variables"),l(a)]),e(", "),s("a",v,[e("LESS variables"),l(a)]),e(" or "),s("a",m,[e("Stylus variables"),l(a)]),e(" in the palette, and then you can use those variables in your theme styles.")]),D])}const x=p(c,[["render",f],["__file","palette.html.vue"]]),k=JSON.parse('{"path":"/plugins/palette.html","title":"palette","lang":"en-US","frontmatter":{},"headers":[{"level":2,"title":"Usage","slug":"usage","link":"#usage","children":[]},{"level":2,"title":"Palette and Style","slug":"palette-and-style","link":"#palette-and-style","children":[]},{"level":2,"title":"Usage","slug":"usage-1","link":"#usage-1","children":[{"level":3,"title":"Usage of Palette","slug":"usage-of-palette","link":"#usage-of-palette","children":[]},{"level":3,"title":"Usage of Style","slug":"usage-of-style","link":"#usage-of-style","children":[]}]},{"level":2,"title":"Options","slug":"options","link":"#options","children":[{"level":3,"title":"preset","slug":"preset","link":"#preset","children":[]},{"level":3,"title":"userPaletteFile","slug":"userpalettefile","link":"#userpalettefile","children":[]},{"level":3,"title":"tempPaletteFile","slug":"temppalettefile","link":"#temppalettefile","children":[]},{"level":3,"title":"userStyleFile","slug":"userstylefile","link":"#userstylefile","children":[]},{"level":3,"title":"tempStyleFile","slug":"tempstylefile","link":"#tempstylefile","children":[]},{"level":3,"title":"importCode","slug":"importcode","link":"#importcode","children":[]}]}],"git":{"updatedTime":1706605723000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":1}]},"filePathRelative":"plugins/palette.md"}');export{x as comp,k as data}; diff --git a/assets/palette.html-CrWHHv51.js b/assets/palette.html-CrWHHv51.js new file mode 100644 index 0000000000..9813b3afd2 --- /dev/null +++ b/assets/palette.html-CrWHHv51.js @@ -0,0 +1,39 @@ +import{_ as i,r as n,o,c,a as l,b as e,d as s,e as p}from"./app-DI49vYHA.js";const r={},d=e("h1",{id:"palette",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#palette"},[e("span",null,"palette")])],-1),u=p(`

    为你的主题提供调色板功能。

    该插件主要用于开发主题,并且已经集成到默认主题中。大部分情况下你不需要直接使用它。

    对于主题作者,该插件可以帮助你提供用户自定义样式的能力。

    使用方法

    npm i -D @vuepress/plugin-palette@next
    +
    import { palettePlugin } from '@vuepress/plugin-palette'
    +
    +export default {
    +  plugins: [
    +    palettePlugin({
    +      // 配置项
    +    }),
    +  ],
    +}
    +

    调色板和样式

    该插件会提供一个 @vuepress/plugin-palette/palette (调色板文件)和一个 @vuepress/plugin-palette/style (样式文件),用于在你的主题样式中引入。

    `,8),y={href:"https://developer.mozilla.org/en-US/docs/Web/CSS/Using_CSS_custom_properties",target:"_blank",rel:"noopener noreferrer"},v={href:"https://sass-lang.com/documentation/variables",target:"_blank",rel:"noopener noreferrer"},h={href:"http://lesscss.org/features/#variables-feature",target:"_blank",rel:"noopener noreferrer"},D={href:"https://stylus-lang.com/docs/variables.html",target:"_blank",rel:"noopener noreferrer"},m=p(`

    样式文件用于覆盖默认样式或添加额外样式,因此它一般会在你主题样式的末尾引入。

    使用

    在你的主题中使用该插件,假设你使用 SASS 作为 CSS 预处理器:

    export default {
    +  // ...
    +  plugins: [palettePlugin({ preset: 'sass' })],
    +}
    +

    使用调色板

    在你主题需要使用对应变量的地方引入该插件的调色板文件,比如在 Layout.vue 中:

    <template>
    +  <h1 class="palette-title">你好,调色板!</h1>
    +</template>
    +
    +<style lang="scss">
    +/* 从该插件的调色板中引入变量 */
    +@import '@vuepress/plugin-palette/palette';
    +
    +/* 设置变量的默认值 */
    +$color: red !default;
    +
    +/* 在你的样式中使用变量 */
    +.palette-title {
    +  color: $color;
    +}
    +</style>
    +

    然后,用户就可以在 .vuepress/styles/palette.scss 中自定义变量:

    $color: green;
    +

    使用样式

    在你主题的样式之后引入该插件的样式文件,比如在 clientConfigFile 中:

    // 引入你主题本身的样式文件
    +import 'path/to/your/theme/style'
    +// 引入该插件的样式文件
    +import '@vuepress/plugin-palette/style'
    +

    然后,用户就可以在 .vuepress/styles/index.scss 中添加额外样式,并可以覆盖你主题本身的样式:

    h1 {
    +  font-size: 2.5rem;
    +}
    +

    配置项

    preset

    • 类型: 'css' | 'sass' | 'less' | 'stylus'

    • 默认值: 'css'

    • 详情:

      设置其他选项的预设。

      如果你没有对该插件进行进阶定制化的需要,建议只设置该配置项并忽略其他选项。

    userPaletteFile

    • 类型: string

    • 默认值:

      • css: '.vuepress/styles/palette.css'
      • sass: '.vuepress/styles/palette.scss'
      • less: '.vuepress/styles/palette.less'
      • stylus: '.vuepress/styles/palette.styl'
    • 详情:

      用户调色板文件的路径,是针对源文件目录的相对路径。

      默认值依赖于 preset 配置项。

      该文件用于用户定义样式变量,建议保持默认值作为约定的文件路径。

    tempPaletteFile

    • 类型: string

    • 默认值:

      • css: 'styles/palette.css'
      • sass: 'styles/palette.scss'
      • less: 'styles/palette.less'
      • stylus: 'styles/palette.styl'
    • 详情:

      生成的调色板临时文件的路径,是针对临时文件文件目录的相对路径。

      默认值依赖于 preset 配置项。

      你应该使用 '@vuepress/plugin-palette/palette' 别名来引入调色板文件,因此在绝大多数情况下你不需要修改该配置项。

    userStyleFile

    • 类型: string

    • 默认值:

      • css: '.vuepress/styles/index.css'
      • sass: '.vuepress/styles/index.scss'
      • less: '.vuepress/styles/index.less'
      • stylus: '.vuepress/styles/index.styl'
    • 详情:

      用户样式文件的路径,是针对源文件目录的相对路径。

      默认值依赖于 preset 配置项。

      该文件用于用户覆盖默认样式和添加额外样式,建议保持默认值作为约定的文件路径。

    tempStyleFile

    • 类型: string

    • 默认值:

      • css: 'styles/index.css'
      • sass: 'styles/index.scss'
      • less: 'styles/index.less'
      • stylus: 'styles/index.styl'
    • 详情:

      生成的样式临时文件的路径,是针对临时文件文件目录的相对路径。

      默认值依赖于 preset 配置项。

      你应该使用 '@vuepress/plugin-palette/style' 别名来引入样式文件,因此在绝大多数情况下你不需要修改该配置项。

    importCode

    • 类型: (filePath: string) => string

    • 默认值:

      • css: (filePath) => \`@import '\${filePath}';\\n\`
      • sass: (filePath) => \`@forward 'file:///\${filePath}';\\n\`
      • less: (filePath) => \`@import '\${filePath}';\\n\`
      • stylus: (filePath) => \`@require '\${filePath}';\\n\`
    • 详情:

      用于生成引入代码的函数。

      默认值依赖于 preset 配置项。

      该配置项用于生成 tempPaletteFiletempStyleFile ,在绝大多数情况下你不需要修改该配置项。

    `,27);function b(g,f){const t=n("NpmBadge"),a=n("ExternalLinkIcon");return o(),c("div",null,[d,l(t,{package:"@vuepress/plugin-palette"}),u,e("p",null,[s("调色板文件用于定义样式变量,因此它一般会在你主题样式的开头引入。举例来说,用户可以在调色板中定义 "),e("a",y,[s("CSS 变量"),l(a)]),s(" 、 "),e("a",v,[s("SASS 变量"),l(a)]),s(" 、 "),e("a",h,[s("LESS 变量"),l(a)]),s(" 或 "),e("a",D,[s("Stylus 变量"),l(a)]),s(" ,然后你可以在你的主题样式中使用这些变量。")]),m])}const E=i(r,[["render",b],["__file","palette.html.vue"]]),x=JSON.parse('{"path":"/zh/plugins/palette.html","title":"palette","lang":"zh-CN","frontmatter":{},"headers":[{"level":2,"title":"使用方法","slug":"使用方法","link":"#使用方法","children":[]},{"level":2,"title":"调色板和样式","slug":"调色板和样式","link":"#调色板和样式","children":[]},{"level":2,"title":"使用","slug":"使用","link":"#使用","children":[{"level":3,"title":"使用调色板","slug":"使用调色板","link":"#使用调色板","children":[]},{"level":3,"title":"使用样式","slug":"使用样式","link":"#使用样式","children":[]}]},{"level":2,"title":"配置项","slug":"配置项","link":"#配置项","children":[{"level":3,"title":"preset","slug":"preset","link":"#preset","children":[]},{"level":3,"title":"userPaletteFile","slug":"userpalettefile","link":"#userpalettefile","children":[]},{"level":3,"title":"tempPaletteFile","slug":"temppalettefile","link":"#temppalettefile","children":[]},{"level":3,"title":"userStyleFile","slug":"userstylefile","link":"#userstylefile","children":[]},{"level":3,"title":"tempStyleFile","slug":"tempstylefile","link":"#tempstylefile","children":[]},{"level":3,"title":"importCode","slug":"importcode","link":"#importcode","children":[]}]}],"git":{"updatedTime":1706605723000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":1}]},"filePathRelative":"zh/plugins/palette.md"}');export{E as comp,x as data}; diff --git a/assets/photo-swipe.html-CVrJSAOJ.js b/assets/photo-swipe.html-CVrJSAOJ.js new file mode 100644 index 0000000000..6ff3020cb4 --- /dev/null +++ b/assets/photo-swipe.html-CVrJSAOJ.js @@ -0,0 +1,145 @@ +import{_ as o,r as n,o as i,c as t,a as l,b as s,d as c,e as a}from"./app-DI49vYHA.js";const r={},d=s("h1",{id:"photo-swipe",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#photo-swipe"},[s("span",null,"photo-swipe")])],-1),D=a(`

    This plugin will make the pictures in the body of the page enter the preview mode when clicked.

    Usage

    npm i -D @vuepress/plugin-photo-swipe@next
    +
    import { photoSwipePlugin } from '@vuepress/plugin-photo-swipe'
    +
    +export default {
    +  plugins: [
    +    photoSwipePlugin({
    +      // options
    +    }),
    +  ],
    +}
    +

    In preview mode, you can:

    • Swipe left and right to preview other pictures on the page in order
    • View the description of the picture
    • Zoom in and zoom out the picture
    • View pictures in full screen
    • Download pictures
    • Share pictures

    TIP

    • Besides clicking "×" in the upper right corner to exit the preview mode, scrolling up and down more than a certain distance will also exit preview mode.
    • On mobile devices, or using the PC trackpad, you can use pan and zoom gestures to pan and zoom in the preview mode.

    Options

    selector

    • Type: string | string[]
    • Default: ".theme-default-content :not(a) > img:not([no-view])"
    • Details: Image selector

    scrollToClose

    • Type: boolean
    • Default: true
    • Details: Whether close the current image when scrolling.

    delay

    • Type: number

    • Default: 800

    • Details:

      The delay of operating dom, in ms.

      TIP

      If the theme you are using has a switching animation, it is recommended to configure this option to Switch animation duration + 200.

    locales

    • Type: PhotoSwipePluginLocaleConfig

      interface PhotoSwipePluginLocaleData {
      +  /**
      +   * Close button label text
      +   */
      +  close: string
      +
      +  /**
      +   * Full screen button label text
      +   */
      +  fullscreen: string
      +
      +  /**
      +   * Share button label text
      +   */
      +  share: string
      +
      +  /**
      +   * Zoom button label text
      +   */
      +  zoom: string
      +
      +  /**
      +   * Previous image button label text
      +   */
      +  prev: string
      +
      +  /**
      +   * Next image button label text
      +   */
      +  next: string
      +
      +  /**
      +   * Share button config
      +   */
      +  buttons: PhotoSwipeDefaultUI.ShareButtonData[]
      +}
      +
      +interface PhotoSwipePluginLocaleConfig {
      +  [localePath: string]: PhotoSwipePluginLocaleData
      +}
      +
    • Details: Locales config for photo-swipe plugin.

    • Example:

      import { defineUserConfig } from 'vuepress'
      +import { photoSwipePlugin } from '@vuepress/plugin-photo-swipe'
      +
      +export default defineUserConfig({
      +  locales: {
      +    '/': {
      +      // this is a supported language
      +      lang: 'en-US',
      +    },
      +    '/xx/': {
      +      // the plugin does not support this language
      +      lang: 'mm-NN',
      +    },
      +  },
      +
      +  plugins: [
      +    photoSwipePlugin({
      +      locales: {
      +        '/': {
      +          // Override share label text
      +          share: 'Share with friends',
      +        },
      +
      +        '/xx/': {
      +          // Complete locale config for \`mm-NN\` language here
      +        },
      +      },
      +    }),
      +  ],
      +})
      +
    Built-in Supported Languages
    • Simplified Chinese (zh-CN)
    • Traditional Chinese (zh-TW)
    • English (United States) (en-US)
    • German (de-DE)
    • German (Australia) (de-AT)
    • Russian (ru-RU)
    • Ukrainian (uk-UA)
    • Vietnamese (vi-VN)
    • Portuguese (Brazil) (pt-BR)
    • Polish (pl-PL)
    • French (fr-FR)
    • Spanish (es-ES)
    • Slovak (sk-SK)
    • Japanese (ja-JP)
    • Turkish (tr-TR)
    • Korean (ko-KR)
    • Finnish (fi-FI)
    • Indonesian (id-ID)
    • Dutch (nl-NL)

    Frontmatter

    • Type: string | false
    • Details:

    Image selector for the current page, or false to disable photo-swipe in current page.

    Client Config

    definePhotoSwipeConfig

    `,22),y={href:"http://photoswipe.com/",target:"_blank",rel:"noopener noreferrer"},u=s("code",null,"photo-swipe",-1),v=a(`
    import { definePhotoSwipeConfig } from '@vuepress/plugin-photo-swipe/client'
    +
    +definePhotoSwipeConfig({
    +  // set photoswipe options here
    +})
    +
    +export default {}
    +

    API

    You can also call photoswipe with apis.

    createPhotoSwipe allows you to programmatically view images links with PhotoSwipe:

    <script setup lang="ts">
    +import { onMounted, onUnmounted } from 'vue';
    +import { createPhotoSwipe } from "@vuepress/plugin-photo-swipe/client";
    +
    +let state: PhotoSwipeState | null = null;
    +
    +const openPhotoSwipe = (index: number) => {
    +  state?.open(index - 1);
    +};
    +
    +onMounted(async () => {
    +  // create a new photoswipe instance with image links
    +  state=  await createPhotoSwipe(
    +    [
    +      'https://exmaple.com/image1.png'
    +      'https://exmaple.com/image2.png'
    +      'https://exmaple.com/image3.png'
    +    ],
    +    {
    +      // photoswipe options
    +    }
    +  );
    +});
    +
    +onUnmounted(() => {
    +  state?.destroy()
    +})
    +</script>
    +
    +<template>
    +  <button v-for="i in 3" @click="openPhotoSwipe(i)">open photo {{ i }}</button>
    +</template>
    +

    registerPhotoSwipe allows you to register photoswipe for the given image elements:

    <script setup lang="ts">
    +import { onMounted, onUnmounted } from 'vue'
    +import { registerPhotoSwipe } from '@vuepress/plugin-photo-swipe/client'
    +
    +let destroy: () => void | null = null
    +
    +onMounted(async () => {
    +  await nextTick()
    +
    +  const images = Array.from(document.querySelectorAll('img'))
    +
    +  // create a new photoswipe instance on image elements
    +  state = await registerPhotoSwipe(images, {
    +    // photoswipe options
    +  })
    +})
    +
    +onUnmounted(() => {
    +  destroy?.()
    +})
    +</script>
    +

    Styles

    You can customize the style via CSS variables:

    :root {
    +  --photo-swipe-bullet: #fff;
    +  --photo-swipe-bullet-active: #3eaf7c;
    +}
    +
    `,10);function m(C,h){const e=n("NpmBadge"),p=n("ExternalLinkIcon");return i(),t("div",null,[d,l(e,{package:"@vuepress/plugin-photo-swipe"}),D,s("p",null,[c("Options passed to "),s("a",y,[u,l(p)])]),v])}const g=o(r,[["render",m],["__file","photo-swipe.html.vue"]]),E=JSON.parse('{"path":"/plugins/photo-swipe.html","title":"photo-swipe","lang":"en-US","frontmatter":{},"headers":[{"level":2,"title":"Usage","slug":"usage","link":"#usage","children":[]},{"level":2,"title":"Options","slug":"options","link":"#options","children":[{"level":3,"title":"selector","slug":"selector","link":"#selector","children":[]},{"level":3,"title":"scrollToClose","slug":"scrolltoclose","link":"#scrolltoclose","children":[]},{"level":3,"title":"delay","slug":"delay","link":"#delay","children":[]},{"level":3,"title":"locales","slug":"locales","link":"#locales","children":[]}]},{"level":2,"title":"Frontmatter","slug":"frontmatter","link":"#frontmatter","children":[]},{"level":2,"title":"Client Config","slug":"client-config","link":"#client-config","children":[{"level":3,"title":"definePhotoSwipeConfig","slug":"definephotoswipeconfig","link":"#definephotoswipeconfig","children":[]}]},{"level":2,"title":"API","slug":"api","link":"#api","children":[]},{"level":2,"title":"Styles","slug":"styles","link":"#styles","children":[]}],"git":{"updatedTime":1708274662000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":3}]},"filePathRelative":"plugins/photo-swipe.md"}');export{g as comp,E as data}; diff --git a/assets/photo-swipe.html-Dw7C8ObA.js b/assets/photo-swipe.html-Dw7C8ObA.js new file mode 100644 index 0000000000..4959f2e9e3 --- /dev/null +++ b/assets/photo-swipe.html-Dw7C8ObA.js @@ -0,0 +1,145 @@ +import{_ as i,r as n,o as c,c as t,a as l,b as s,d as a,e}from"./app-DI49vYHA.js";const r={},D=s("h1",{id:"photo-swipe",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#photo-swipe"},[s("span",null,"photo-swipe")])],-1),d=e(`

    此插件会使页面正文内的图片在点击时进入浏览模式浏览。

    使用方法

    npm i -D @vuepress/plugin-photo-swipe@next
    +
    import { photoSwipePlugin } from '@vuepress/plugin-photo-swipe'
    +
    +export default {
    +  plugins: [
    +    photoSwipePlugin({
    +      // 选项
    +    }),
    +  ],
    +}
    +

    在图片预览模式中,你可以:

    • 左右滑动按顺序浏览页面内其他的图片
    • 查看图片的描述
    • 对图片进行缩放
    • 全屏浏览图片
    • 下载图片
    • 分享图片

    提示

    • 除了点击右上角的 "×" 退出浏览模式外,在上下滚动超过一定距离后,会自动退出图片浏览模式。
    • 在移动端,或使用 PC 触控板,你可以使用平移、缩放手势在浏览模式中平移、缩放图片。

    选项

    selector

    • 类型:string | string[]
    • 默认值:".theme-default-content :not(a) > img:not([no-view])"
    • 详情:图片选择器

    scrollToClose

    • 类型:boolean
    • 默认值:true
    • 详情:是否在滚动时关闭当前图片。

    delay

    • 类型:number

    • 默认值:800

    • 详情:

      操作页面 DOM 的延时,单位 ms。

      提示

      如果你使用的主题有切换动画,建议配置此选项为 切换动画时长 + 200

    locales

    • 类型:PhotoSwipePluginLocaleConfig

      interface PhotoSwipePluginLocaleData {
      +  /**
      +   * 关闭按钮标签文字
      +   */
      +  close: string
      +
      +  /**
      +   * 全屏按钮标签文字
      +   */
      +  fullscreen: string
      +
      +  /**
      +   * 分享按钮标签文字
      +   */
      +  share: string
      +
      +  /**
      +   * 缩放按钮标签文字
      +   */
      +  zoom: string
      +
      +  /**
      +   * 上一张图片按钮标签文字
      +   */
      +  prev: string
      +
      +  /**
      +   * 下一张图片按钮标签文字
      +   */
      +  next: string
      +
      +  /**
      +   * 功能按钮配置
      +   */
      +  buttons: PhotoSwipeDefaultUI.ShareButtonData[]
      +}
      +
      +interface PhotoSwipePluginLocaleConfig {
      +  [localePath: string]: PhotoSwipePluginLocaleData
      +}
      +
    • 详情:Photo Swipe 插件的国际化配置。

    • 示例:

      import { defineUserConfig } from 'vuepress'
      +import { photoSwipePlugin } from '@vuepress/plugin-photo-swipe'
      +
      +export default defineUserConfig({
      +  locales: {
      +    '/': {
      +      // 这是一个支持的语言
      +      lang: 'zh-CN',
      +    },
      +    '/xx/': {
      +      // 这是一个没有收到插件支持的语言
      +      lang: 'mm-NN',
      +    },
      +  },
      +
      +  plugins: [
      +    photoSwipePlugin({
      +      locales: {
      +        '/': {
      +          // 覆盖分享标签文字
      +          share: '分享给伙伴',
      +        },
      +
      +        '/xx/': {
      +          // 在这里完整设置 \`mm-NN\` 的多语言配置
      +        },
      +      },
      +    }),
      +  ],
      +})
      +
    内置支持语言
    • 简体中文 (zh-CN)
    • 繁体中文 (zh-TW)
    • 英文(美国) (en-US)
    • 德语 (de-DE)
    • 德语(澳大利亚) (de-AT)
    • 俄语 (ru-RU)
    • 乌克兰语 (uk-UA)
    • 越南语 (vi-VN)
    • 葡萄牙语(巴西) (pt-BR)
    • 波兰语 (pl-PL)
    • 法语 (fr-FR)
    • 西班牙语 (es-ES)
    • 斯洛伐克 (sk-SK)
    • 日语 (ja-JP)
    • 土耳其语 (tr-TR)
    • 韩语 (ko-KR)
    • 芬兰语 (fi-FI)
    • 印尼语 (id-ID)
    • 荷兰语 (nl-NL)

    Frontmatter

    photoswipe

    • 类型: string | false

    • 详情:

      当前页面的图片选择器或 false 以在当前页面中禁用 photo-swipe。

    客户端配置

    definePhotoSwipeConfig

    `,22),y={href:"http://photoswipe.com/",target:"_blank",rel:"noopener noreferrer"},v=s("code",null,"photo-swipe",-1),u=e(`
    import { definePhotoSwipeConfig } from '@vuepress/plugin-photo-swipe/client'
    +
    +definePhotoSwipeConfig({
    +  // 在此设置 photoswipe 选项
    +})
    +
    +export default {}
    +

    API

    你可以通过 API 来调用 photoswipe。

    createPhotoSwipe 允许你以编程的方式查看图片链接:

    <script setup lang="ts">
    +import { onMounted, onUnmounted } from 'vue';
    +import { createPhotoSwipe } from "@vuepress/plugin-photo-swipe/client";
    +
    +let state: PhotoSwipeState | null = null;
    +
    +const openPhotoSwipe = (index: number) => {
    +  state?.open(index - 1);
    +};
    +
    +onMounted(async () => {
    +  // 通过图片链接创建一个新的 photoswipe 实例
    +  state=  await createPhotoSwipe(
    +    [
    +      'https://exmaple.com/image1.png'
    +      'https://exmaple.com/image2.png'
    +      'https://exmaple.com/image3.png'
    +    ],
    +    {
    +      // photoswipe 选项
    +    }
    +  );
    +});
    +
    +onUnmounted(() => {
    +  state?.destroy()
    +})
    +</script>
    +
    +<template>
    +  <button v-for="i in 3" @click="openPhotoSwipe(i)">open photo {{ i }}</button>
    +</template>
    +

    registerPhotoSwipe 允许你为给定的图片元素注册 photoswipe:

    <script setup lang="ts">
    +import { onMounted, onUnmounted } from 'vue'
    +import { registerPhotoSwipe } from '@vuepress/plugin-photo-swipe/client'
    +
    +let destroy: () => void | null = null
    +
    +onMounted(async () => {
    +  await nextTick()
    +
    +  const images = Array.from(document.querySelectorAll('img'))
    +
    +  // 通过图片元素创建一个新的 photoswipe 实例
    +  state = await registerPhotoSwipe(images, {
    +    // photoswipe 选项
    +  })
    +})
    +
    +onUnmounted(() => {
    +  destroy?.()
    +})
    +</script>
    +

    样式

    你可以通过 CSS 变量来自定义部分样式:

    :root {
    +  --photo-swipe-bullet: #fff;
    +  --photo-swipe-bullet-active: #3eaf7c;
    +}
    +
    `,10);function C(m,b){const p=n("NpmBadge"),o=n("ExternalLinkIcon");return c(),t("div",null,[D,l(p,{package:"@vuepress/plugin-photo-swipe"}),d,s("p",null,[a("传递给 "),s("a",y,[v,l(o)]),a(" 的额外选项。")]),u])}const g=i(r,[["render",C],["__file","photo-swipe.html.vue"]]),E=JSON.parse('{"path":"/zh/plugins/photo-swipe.html","title":"photo-swipe","lang":"zh-CN","frontmatter":{},"headers":[{"level":2,"title":"使用方法","slug":"使用方法","link":"#使用方法","children":[]},{"level":2,"title":"选项","slug":"选项","link":"#选项","children":[{"level":3,"title":"selector","slug":"selector","link":"#selector","children":[]},{"level":3,"title":"scrollToClose","slug":"scrolltoclose","link":"#scrolltoclose","children":[]},{"level":3,"title":"delay","slug":"delay","link":"#delay","children":[]},{"level":3,"title":"locales","slug":"locales","link":"#locales","children":[]}]},{"level":2,"title":"Frontmatter","slug":"frontmatter","link":"#frontmatter","children":[{"level":3,"title":"photoswipe","slug":"photoswipe","link":"#photoswipe","children":[]}]},{"level":2,"title":"客户端配置","slug":"客户端配置","link":"#客户端配置","children":[{"level":3,"title":"definePhotoSwipeConfig","slug":"definephotoswipeconfig","link":"#definephotoswipeconfig","children":[]}]},{"level":2,"title":"API","slug":"api","link":"#api","children":[]},{"level":2,"title":"样式","slug":"样式","link":"#样式","children":[]}],"git":{"updatedTime":1708274662000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":3}]},"filePathRelative":"zh/plugins/photo-swipe.md"}');export{g as comp,E as data}; diff --git a/assets/plugin.html-DpcJp1-q.js b/assets/plugin.html-DpcJp1-q.js new file mode 100644 index 0000000000..ad5b72b4a9 --- /dev/null +++ b/assets/plugin.html-DpcJp1-q.js @@ -0,0 +1,10 @@ +import{_ as o,r as u,o as a,c as p,b as e,d as l,a as s,w as t,e as i}from"./app-DI49vYHA.js";const c={},d=i(`

    Plugins Config

    You can configure the plugins that used by default theme with themePlugins.

    Default theme is using some plugins by default. You can disable a plugin if you really do not want to use it. Make sure you understand what the plugin is for before disabling it.

    import { defaultTheme } from '@vuepress/theme-default'
    +
    +export default {
    +  theme: defaultTheme({
    +    themePlugins: {
    +      // customize theme plugins here
    +    },
    +  }),
    +}
    +
    `,5),h=e("li",null,[e("p",null,[l("Type: "),e("code",null,"boolean")])],-1),r=e("li",null,[e("p",null,[l("Default: "),e("code",null,"true")])],-1),m=e("p",null,"Details:",-1),g=e("h2",{id:"themeplugins-backtotop",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#themeplugins-backtotop"},[e("span",null,"themePlugins.backToTop")])],-1),_=e("li",null,[e("p",null,[l("Type: "),e("code",null,"BackToTopPluginOptions | boolean")])],-1),b=e("li",null,[e("p",null,[l("Default: "),e("code",null,"true")])],-1),f=e("p",null,"Details:",-1),v=e("p",null,"Object value is supported as plugin options.",-1),D=e("h2",{id:"themeplugins-container",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#themeplugins-container"},[e("span",null,"themePlugins.container")])],-1),y=e("li",null,[e("p",null,[l("Type: "),e("code",null,"Record")])],-1),k=e("p",null,"Details:",-1),C=i("

    ContainerType type is:

    • tip
    • warning
    • danger
    • details
    • codeGroup
    • codeGroupItem
    ",2),P=e("p",null,"Also see:",-1),x=e("h2",{id:"themeplugins-externallinkicon",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#themeplugins-externallinkicon"},[e("span",null,"themePlugins.externalLinkIcon")])],-1),T=e("li",null,[e("p",null,[l("Type: "),e("code",null,"boolean")])],-1),E=e("li",null,[e("p",null,[l("Default: "),e("code",null,"true")])],-1),w=e("p",null,"Details:",-1),O=e("h2",{id:"themeplugins-copycode",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#themeplugins-copycode"},[e("span",null,"themePlugins.copyCode")])],-1),z=e("li",null,[e("p",null,[l("Type: "),e("code",null,"CopyCodePluginOptions | boolean")])],-1),L=e("li",null,[e("p",null,[l("Default: "),e("code",null,"true")])],-1),N=e("p",null,"Details:",-1),S=e("p",null,"Object value is supported as plugin options.",-1),j=e("h2",{id:"themeplugins-git",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#themeplugins-git"},[e("span",null,"themePlugins.git")])],-1),A=e("li",null,[e("p",null,[l("Type: "),e("code",null,"boolean")])],-1),B=e("li",null,[e("p",null,[l("Default: "),e("code",null,"true")])],-1),R=e("p",null,"Details:",-1),V=e("h2",{id:"themeplugins-mediumzoom",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#themeplugins-mediumzoom"},[e("span",null,"themePlugins.mediumZoom")])],-1),F=e("li",null,[e("p",null,[l("Type: "),e("code",null,"boolean")])],-1),H=e("li",null,[e("p",null,[l("Default: "),e("code",null,"true")])],-1),I=e("p",null,"Details:",-1),M=e("h2",{id:"themeplugins-nprogress",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#themeplugins-nprogress"},[e("span",null,"themePlugins.nprogress")])],-1),G=e("li",null,[e("p",null,[l("Type: "),e("code",null,"boolean")])],-1),Y=e("li",null,[e("p",null,[l("Default: "),e("code",null,"true")])],-1),Z=e("p",null,"Details:",-1),J=e("h2",{id:"themeplugins-seo",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#themeplugins-seo"},[e("span",null,"themePlugins.seo")])],-1),U=e("li",null,[e("p",null,[l("Type: "),e("code",null,"SeoPluginOptions | boolean")])],-1),q=e("li",null,[e("p",null,[l("Default: "),e("code",null,"true")])],-1),K=e("p",null,"Details:",-1),Q=e("p",null,"Object value is supported as plugin options.",-1),W=e("h2",{id:"themeplugins-sitemap",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#themeplugins-sitemap"},[e("span",null,"themePlugins.sitemap")])],-1),X=e("li",null,[e("p",null,[l("Type: "),e("code",null,"SitemapPluginOptions | boolean")])],-1),$=e("li",null,[e("p",null,[l("Default: "),e("code",null,"true")])],-1),ee=e("p",null,"Details:",-1),le=e("p",null,"Object value is supported as plugin options.",-1);function ne(se,te){const n=u("RouteLink");return a(),p("div",null,[d,e("ul",null,[h,r,e("li",null,[m,e("p",null,[l("Enable "),s(n,{to:"/plugins/active-header-links.html"},{default:t(()=>[l("@vuepress/plugin-active-header-links")]),_:1}),l(" or not.")])])]),g,e("ul",null,[_,b,e("li",null,[f,e("p",null,[l("Enable "),s(n,{to:"/plugins/back-to-top.html"},{default:t(()=>[l("@vuepress/plugin-back-to-top")]),_:1}),l(" or not.")]),v])]),D,e("ul",null,[y,e("li",null,[k,e("p",null,[l("Enable custom containers that powered by "),s(n,{to:"/plugins/container.html"},{default:t(()=>[l("@vuepress/plugin-container")]),_:1}),l(" or not.")]),C]),e("li",null,[P,e("ul",null,[e("li",null,[s(n,{to:"/themes/default/markdown.html#custom-containers"},{default:t(()=>[l("Default Theme > Markdown > Custom Containers")]),_:1})])])])]),x,e("ul",null,[T,E,e("li",null,[w,e("p",null,[l("Enable "),s(n,{to:"/plugins/external-link-icon.html"},{default:t(()=>[l("@vuepress/plugin-external-link-icon")]),_:1}),l(" or not.")])])]),O,e("ul",null,[z,L,e("li",null,[N,e("p",null,[l("Enable "),s(n,{to:"/plugins/copy-code.html"},{default:t(()=>[l("@vuepress/plugin-copy-code")]),_:1}),l(" or not.")]),S])]),j,e("ul",null,[A,B,e("li",null,[R,e("p",null,[l("Enable "),s(n,{to:"/plugins/git.html"},{default:t(()=>[l("@vuepress/plugin-git")]),_:1}),l(" or not.")])])]),V,e("ul",null,[F,H,e("li",null,[I,e("p",null,[l("Enable "),s(n,{to:"/plugins/medium-zoom.html"},{default:t(()=>[l("@vuepress/plugin-medium-zoom")]),_:1}),l(" or not.")])])]),M,e("ul",null,[G,Y,e("li",null,[Z,e("p",null,[l("Enable "),s(n,{to:"/plugins/nprogress.html"},{default:t(()=>[l("@vuepress/plugin-nprogress")]),_:1}),l(" or not.")])])]),J,e("ul",null,[U,q,e("li",null,[K,e("p",null,[l("Enable "),s(n,{to:"/plugins/seo/"},{default:t(()=>[l("@vuepress/plugin-seo")]),_:1}),l(" or not.")]),Q])]),W,e("ul",null,[X,$,e("li",null,[ee,e("p",null,[l("Enable "),s(n,{to:"/plugins/sitemap/"},{default:t(()=>[l("@vuepress/plugin-sitemap")]),_:1}),l(" or not.")]),le])])])}const oe=o(c,[["render",ne],["__file","plugin.html.vue"]]),ue=JSON.parse('{"path":"/themes/default/plugin.html","title":"Plugins Config","lang":"en-US","frontmatter":{},"headers":[{"level":2,"title":"themePlugins.activeHeaderLinks","slug":"themeplugins-activeheaderlinks","link":"#themeplugins-activeheaderlinks","children":[]},{"level":2,"title":"themePlugins.backToTop","slug":"themeplugins-backtotop","link":"#themeplugins-backtotop","children":[]},{"level":2,"title":"themePlugins.container","slug":"themeplugins-container","link":"#themeplugins-container","children":[]},{"level":2,"title":"themePlugins.externalLinkIcon","slug":"themeplugins-externallinkicon","link":"#themeplugins-externallinkicon","children":[]},{"level":2,"title":"themePlugins.copyCode","slug":"themeplugins-copycode","link":"#themeplugins-copycode","children":[]},{"level":2,"title":"themePlugins.git","slug":"themeplugins-git","link":"#themeplugins-git","children":[]},{"level":2,"title":"themePlugins.mediumZoom","slug":"themeplugins-mediumzoom","link":"#themeplugins-mediumzoom","children":[]},{"level":2,"title":"themePlugins.nprogress","slug":"themeplugins-nprogress","link":"#themeplugins-nprogress","children":[]},{"level":2,"title":"themePlugins.seo","slug":"themeplugins-seo","link":"#themeplugins-seo","children":[]},{"level":2,"title":"themePlugins.sitemap","slug":"themeplugins-sitemap","link":"#themeplugins-sitemap","children":[]}],"git":{"updatedTime":1706960012000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":2}]},"filePathRelative":"themes/default/plugin.md"}');export{oe as comp,ue as data}; diff --git a/assets/plugin.html-Dsd8bOpV.js b/assets/plugin.html-Dsd8bOpV.js new file mode 100644 index 0000000000..b9807cff26 --- /dev/null +++ b/assets/plugin.html-Dsd8bOpV.js @@ -0,0 +1,10 @@ +import{_ as o,r as u,o as a,c as p,b as l,d as e,a as s,w as t,e as i}from"./app-DI49vYHA.js";const c={},d=i(`

    插件配置

    你可以通过 themePlugins 设置默认主题使用的插件。

    默认主题使用了一些插件,如果你确实不需要该插件,你可以选择禁用它。在禁用插件之前,请确保你已了解它的用途。

    import { defaultTheme } from '@vuepress/theme-default'
    +
    +export default {
    +  theme: defaultTheme({
    +    themePlugins: {
    +      // 在这里自定义主题插件
    +    },
    +  }),
    +}
    +
    `,5),h=l("li",null,[l("p",null,[e("类型: "),l("code",null,"boolean")])],-1),r=l("li",null,[l("p",null,[e("默认值: "),l("code",null,"true")])],-1),m=l("p",null,"详情:",-1),g=l("h2",{id:"themeplugins-backtotop",tabindex:"-1"},[l("a",{class:"header-anchor",href:"#themeplugins-backtotop"},[l("span",null,"themePlugins.backToTop")])],-1),_=l("li",null,[l("p",null,[e("类型: "),l("code",null,"BackToTopPluginOptions | boolean")])],-1),v=l("li",null,[l("p",null,[e("默认值: "),l("code",null,"true")])],-1),k=l("p",null,"详情:",-1),b=l("p",null,"支持对象格式以作为插件选项。",-1),f=l("h2",{id:"themeplugins-container",tabindex:"-1"},[l("a",{class:"header-anchor",href:"#themeplugins-container"},[l("span",null,"themePlugins.container")])],-1),D=l("li",null,[l("p",null,[e("类型: "),l("code",null,"Record")])],-1),y=l("p",null,"详情:",-1),x=i("

    ContainerType 类型为:

    • tip
    • warning
    • danger
    • details
    • codeGroup
    • codeGroupItem
    ",2),P=l("p",null,"参考:",-1),C=l("h2",{id:"themeplugins-copycode",tabindex:"-1"},[l("a",{class:"header-anchor",href:"#themeplugins-copycode"},[l("span",null,"themePlugins.copyCode")])],-1),z=l("li",null,[l("p",null,[e("类型: "),l("code",null,"CopyCodePluginOptions | boolean")])],-1),E=l("li",null,[l("p",null,[e("默认值: "),l("code",null,"true")])],-1),T=l("p",null,"详情:",-1),A=l("p",null,"支持对象格式以作为插件选项。",-1),B=l("h2",{id:"themeplugins-externallinkicon",tabindex:"-1"},[l("a",{class:"header-anchor",href:"#themeplugins-externallinkicon"},[l("span",null,"themePlugins.externalLinkIcon")])],-1),L=l("li",null,[l("p",null,[e("类型: "),l("code",null,"boolean")])],-1),N=l("li",null,[l("p",null,[e("默认值: "),l("code",null,"true")])],-1),w=l("p",null,"详情:",-1),O=l("h2",{id:"themeplugins-git",tabindex:"-1"},[l("a",{class:"header-anchor",href:"#themeplugins-git"},[l("span",null,"themePlugins.git")])],-1),R=l("li",null,[l("p",null,[e("类型: "),l("code",null,"boolean")])],-1),S=l("li",null,[l("p",null,[e("默认值: "),l("code",null,"true")])],-1),V=l("p",null,"详情:",-1),F=l("h2",{id:"themeplugins-mediumzoom",tabindex:"-1"},[l("a",{class:"header-anchor",href:"#themeplugins-mediumzoom"},[l("span",null,"themePlugins.mediumZoom")])],-1),H=l("li",null,[l("p",null,[e("类型: "),l("code",null,"boolean")])],-1),I=l("li",null,[l("p",null,[e("默认值: "),l("code",null,"true")])],-1),G=l("p",null,"详情:",-1),M=l("h2",{id:"themeplugins-nprogress",tabindex:"-1"},[l("a",{class:"header-anchor",href:"#themeplugins-nprogress"},[l("span",null,"themePlugins.nprogress")])],-1),Z=l("li",null,[l("p",null,[e("类型: "),l("code",null,"boolean")])],-1),J=l("li",null,[l("p",null,[e("默认值: "),l("code",null,"true")])],-1),j=l("p",null,"详情:",-1),q=l("h2",{id:"themeplugins-seo",tabindex:"-1"},[l("a",{class:"header-anchor",href:"#themeplugins-seo"},[l("span",null,"themePlugins.seo")])],-1),K=l("li",null,[l("p",null,[e("类型: "),l("code",null,"SeoPluginOptions | boolean")])],-1),Q=l("li",null,[l("p",null,[e("默认值: "),l("code",null,"true")])],-1),U=l("p",null,"详情:",-1),W=l("p",null,"支持对象格式以作为插件选项。",-1),X=l("h2",{id:"themeplugins-sitemap",tabindex:"-1"},[l("a",{class:"header-anchor",href:"#themeplugins-sitemap"},[l("span",null,"themePlugins.sitemap")])],-1),Y=l("li",null,[l("p",null,[e("类型: "),l("code",null,"SitemapPluginOptions | boolean")])],-1),$=l("li",null,[l("p",null,[e("默认值: "),l("code",null,"true")])],-1),ll=l("p",null,"详情:",-1),el=l("p",null,"支持对象格式以作为插件选项。",-1);function nl(sl,tl){const n=u("RouteLink");return a(),p("div",null,[d,l("ul",null,[h,r,l("li",null,[m,l("p",null,[e("是否启用 "),s(n,{to:"/zh/plugins/active-header-links.html"},{default:t(()=>[e("@vuepress/plugin-active-header-links")]),_:1}),e(" 。")])])]),g,l("ul",null,[_,v,l("li",null,[k,l("p",null,[e("是否启用 "),s(n,{to:"/zh/plugins/back-to-top.html"},{default:t(()=>[e("@vuepress/plugin-back-to-top")]),_:1}),e(" 。")]),b])]),f,l("ul",null,[D,l("li",null,[y,l("p",null,[e("是否启用由 "),s(n,{to:"/zh/plugins/container.html"},{default:t(()=>[e("@vuepress/plugin-container")]),_:1}),e(" 支持的自定义容器。")]),x]),l("li",null,[P,l("ul",null,[l("li",null,[s(n,{to:"/zh/themes/default/markdown.html#%E8%87%AA%E5%AE%9A%E4%B9%89%E5%AE%B9%E5%99%A8"},{default:t(()=>[e("默认主题 > Markdown > 自定义容器")]),_:1})])])])]),C,l("ul",null,[z,E,l("li",null,[T,l("p",null,[e("是否启用 "),s(n,{to:"/zh/plugins/copy-code.html"},{default:t(()=>[e("@vuepress/plugin-copy-code")]),_:1}),e(" 。")]),A])]),B,l("ul",null,[L,N,l("li",null,[w,l("p",null,[e("是否启用 "),s(n,{to:"/zh/plugins/external-link-icon.html"},{default:t(()=>[e("@vuepress/plugin-external-link-icon")]),_:1}),e(" 。")])])]),O,l("ul",null,[R,S,l("li",null,[V,l("p",null,[e("是否启用 "),s(n,{to:"/zh/plugins/git.html"},{default:t(()=>[e("@vuepress/plugin-git")]),_:1}),e(" 。")])])]),F,l("ul",null,[H,I,l("li",null,[G,l("p",null,[e("是否启用 "),s(n,{to:"/zh/plugins/medium-zoom.html"},{default:t(()=>[e("@vuepress/plugin-medium-zoom")]),_:1}),e(" 。")])])]),M,l("ul",null,[Z,J,l("li",null,[j,l("p",null,[e("是否启用 "),s(n,{to:"/zh/plugins/nprogress.html"},{default:t(()=>[e("@vuepress/plugin-nprogress")]),_:1}),e(" 。")])])]),q,l("ul",null,[K,Q,l("li",null,[U,l("p",null,[e("是否启用 "),s(n,{to:"/zh/plugins/seo/"},{default:t(()=>[e("@vuepress/plugin-seo")]),_:1}),e(" 。")]),W])]),X,l("ul",null,[Y,$,l("li",null,[ll,l("p",null,[e("是否启用 "),s(n,{to:"/zh/plugins/sitemap/"},{default:t(()=>[e("@vuepress/plugin-sitemap")]),_:1}),e(" 。")]),el])])])}const ol=o(c,[["render",nl],["__file","plugin.html.vue"]]),ul=JSON.parse('{"path":"/zh/themes/default/plugin.html","title":"插件配置","lang":"zh-CN","frontmatter":{},"headers":[{"level":2,"title":"themePlugins.activeHeaderLinks","slug":"themeplugins-activeheaderlinks","link":"#themeplugins-activeheaderlinks","children":[]},{"level":2,"title":"themePlugins.backToTop","slug":"themeplugins-backtotop","link":"#themeplugins-backtotop","children":[]},{"level":2,"title":"themePlugins.container","slug":"themeplugins-container","link":"#themeplugins-container","children":[]},{"level":2,"title":"themePlugins.copyCode","slug":"themeplugins-copycode","link":"#themeplugins-copycode","children":[]},{"level":2,"title":"themePlugins.externalLinkIcon","slug":"themeplugins-externallinkicon","link":"#themeplugins-externallinkicon","children":[]},{"level":2,"title":"themePlugins.git","slug":"themeplugins-git","link":"#themeplugins-git","children":[]},{"level":2,"title":"themePlugins.mediumZoom","slug":"themeplugins-mediumzoom","link":"#themeplugins-mediumzoom","children":[]},{"level":2,"title":"themePlugins.nprogress","slug":"themeplugins-nprogress","link":"#themeplugins-nprogress","children":[]},{"level":2,"title":"themePlugins.seo","slug":"themeplugins-seo","link":"#themeplugins-seo","children":[]},{"level":2,"title":"themePlugins.sitemap","slug":"themeplugins-sitemap","link":"#themeplugins-sitemap","children":[]}],"git":{"updatedTime":1706960075000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":3}]},"filePathRelative":"zh/themes/default/plugin.md"}');export{ol as comp,ul as data}; diff --git a/assets/prismjs.html-CmrkmCnz.js b/assets/prismjs.html-CmrkmCnz.js new file mode 100644 index 0000000000..e5468400e5 --- /dev/null +++ b/assets/prismjs.html-CmrkmCnz.js @@ -0,0 +1,11 @@ +import{_ as r,r as l,o as p,c as o,a as e,b as s,d as n,e as t}from"./app-DI49vYHA.js";const c={},d=s("h1",{id:"prismjs",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#prismjs"},[s("span",null,"prismjs")])],-1),u={href:"https://prismjs.com/",target:"_blank",rel:"noopener noreferrer"},m=t(`

    该插件已经集成到默认主题中。

    需要注意的是,该插件仅会给代码块添加 HTML 标记,而不会添加样式。当你在一个自定义主题中使用它时,可能需要自己选择并引入 Prism.js 样式主题。

    使用方法

    npm i -D @vuepress/plugin-prismjs@next
    +
    import { prismjsPlugin } from '@vuepress/plugin-prismjs'
    +
    +export default {
    +  plugins: [
    +    prismjsPlugin({
    +      // 配置项
    +    }),
    +  ],
    +}
    +

    配置项

    preloadLanguages

    `,7),h=s("li",null,[s("p",null,[n("类型: "),s("code",null,"string[]")])],-1),g=s("li",null,[s("p",null,[n("默认值: "),s("code",null,"['markdown', 'jsdoc', 'yaml']")])],-1),v=s("p",null,"详情:",-1),D=s("p",null,"需要预加载的语言。",-1),_=s("p",null,"默认情况下,语言会在解析 Markdown 文件时按需加载。",-1),b={href:"https://github.com/PrismJS/prism/issues/2716",target:"_blank",rel:"noopener noreferrer"};function y(k,C){const i=l("NpmBadge"),a=l("ExternalLinkIcon");return p(),o("div",null,[d,e(i,{package:"@vuepress/plugin-prismjs"}),s("p",null,[n("该插件使用 "),s("a",u,[n("Prism.js"),e(a)]),n(" 来为 Markdown 代码块启用代码高亮。")]),m,s("ul",null,[h,g,s("li",null,[v,D,_,s("p",null,[n("然而, Prism.js 在动态加载语言时可能会遇到 "),s("a",b,[n("一些潜在的问题"),e(a)]),n(" 。为了避免这些问题,你可以使用该配置项来预加载一些语言。")])])])])}const j=r(c,[["render",y],["__file","prismjs.html.vue"]]),x=JSON.parse('{"path":"/zh/plugins/prismjs.html","title":"prismjs","lang":"zh-CN","frontmatter":{},"headers":[{"level":2,"title":"使用方法","slug":"使用方法","link":"#使用方法","children":[]},{"level":2,"title":"配置项","slug":"配置项","link":"#配置项","children":[{"level":3,"title":"preloadLanguages","slug":"preloadlanguages","link":"#preloadlanguages","children":[]}]}],"git":{"updatedTime":1706605723000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":1}]},"filePathRelative":"zh/plugins/prismjs.md"}');export{j as comp,x as data}; diff --git a/assets/prismjs.html-D5rV4std.js b/assets/prismjs.html-D5rV4std.js new file mode 100644 index 0000000000..8bde93d5f0 --- /dev/null +++ b/assets/prismjs.html-D5rV4std.js @@ -0,0 +1,11 @@ +import{_ as o,r as l,o as t,c as p,a as n,b as s,d as e,e as r}from"./app-DI49vYHA.js";const c={},d=s("h1",{id:"prismjs",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#prismjs"},[s("span",null,"prismjs")])],-1),u={href:"https://prismjs.com/",target:"_blank",rel:"noopener noreferrer"},m=r(`

    This plugin has been integrated into the default theme.

    Notice that this plugin would only tokenize the code fence without adding styles. When using it with a custom theme, you may need to choose and import Prism.js style theme yourself.

    Usage

    npm i -D @vuepress/plugin-prismjs@next
    +
    import { prismjsPlugin } from '@vuepress/plugin-prismjs'
    +
    +export default {
    +  plugins: [
    +    prismjsPlugin({
    +      // options
    +    }),
    +  ],
    +}
    +

    Options

    preloadLanguages

    `,7),h=s("li",null,[s("p",null,[e("Type: "),s("code",null,"string[]")])],-1),g=s("li",null,[s("p",null,[e("Default: "),s("code",null,"['markdown', 'jsdoc', 'yaml']")])],-1),v=s("p",null,"Details:",-1),D=s("p",null,"Languages to preload.",-1),_=s("p",null,"By default, languages will be loaded on demand when parsing markdown files.",-1),y={href:"https://github.com/PrismJS/prism/issues/2716",target:"_blank",rel:"noopener noreferrer"};function b(f,k){const i=l("NpmBadge"),a=l("ExternalLinkIcon");return t(),p("div",null,[d,n(i,{package:"@vuepress/plugin-prismjs"}),s("p",null,[e("This plugin will enable syntax highlighting for markdown code fence with "),s("a",u,[e("Prism.js"),n(a)]),e(".")]),m,s("ul",null,[h,g,s("li",null,[v,D,_,s("p",null,[e("However, Prism.js has "),s("a",y,[e("some potential issues"),n(a)]),e(" about loading languages dynamically. To avoid them, you can preload languages via this option.")])])])])}const j=o(c,[["render",b],["__file","prismjs.html.vue"]]),x=JSON.parse('{"path":"/plugins/prismjs.html","title":"prismjs","lang":"en-US","frontmatter":{},"headers":[{"level":2,"title":"Usage","slug":"usage","link":"#usage","children":[]},{"level":2,"title":"Options","slug":"options","link":"#options","children":[{"level":3,"title":"preloadLanguages","slug":"preloadlanguages","link":"#preloadlanguages","children":[]}]}],"git":{"updatedTime":1706605723000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":1}]},"filePathRelative":"plugins/prismjs.md"}');export{j as comp,x as data}; diff --git a/assets/reading-time.html-CPp8fwlZ.js b/assets/reading-time.html-CPp8fwlZ.js new file mode 100644 index 0000000000..9088e255d1 --- /dev/null +++ b/assets/reading-time.html-CPp8fwlZ.js @@ -0,0 +1,95 @@ +import{_ as a,r as l,o as e,c as p,a as i,b as s,e as o}from"./app-DI49vYHA.js";const c={},r=s("h1",{id:"reading-time",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#reading-time"},[s("span",null,"reading-time")])],-1),t=o(`

    此插件会为每个页面生成字数统计与预计阅读时间。

    使用方法

    npm i -D @vuepress/plugin-reading-time@next
    +
    import { readingTimePlugin } from '@vuepress/plugin-reading-time'
    +
    +export default {
    +  plugins: [
    +    readingTimePlugin({
    +      // 配置项
    +    }),
    +  ],
    +}
    +

    插件会将相关信息注入到页面数据的 readingTime,其中:

    • readingTime.minutes:为预计阅读时间(分钟)number
    • readingTime.words:字数统计,number

    在 Node 侧获取数据

    对于任何页面,你可以从 page.data.readingTime 获取预计阅读时间与字数统计:

    page.data.readingTime // { minutes: 3.2, words: 934 }
    +

    你可以在 extendsPage 以及其他生命周期获取它做进一步处理:

    export default {
    +  // ...
    +  extendsPage: (page) => {
    +    page.data.readingTime // { minutes: 3.2, words: 934 }
    +  },
    +
    +  onInitialized: (app) => {
    +    app.pages.map((page) => {
    +      page.data.readingTime // { minutes: 3.2, words: 934 }
    +    })
    +  },
    +}
    +

    在客户端侧获取数据

    你可以从 @vuepress/plugin-reading-time/client 导入 useReadingTimeDatauseReadingTimeLocale 来获取当前页面的阅读时间数据和语言环境数据:

    <script setup lang="ts">
    +import {
    +  useReadingTimeData,
    +  useReadingTimeLocale,
    +} from '@vuepress/plugin-reading-time/client'
    +
    +const readingTimeData = useReadingTimeData() // { minutes: 1.1, words: 100 }
    +const readingTimeLocale = useReadingTimeLocale() // { time: "1 分钟", words: "100 字" }
    +</script>
    +

    选项

    wordPerMinute

    • 类型:number
    • 默认值:300
    • 详情: 每分钟阅读字数

    locales

    • 类型:ReadingTimePluginLocaleConfig

      interface ReadingTimePluginLocaleData {
      +  /**
      +   * 字数模板,模板中 \`$word\` 会被自动替换为字数
      +   */
      +  word: string
      +
      +  /**
      +   * 小于一分钟文字
      +   */
      +  less1Minute: string
      +
      +  /**
      +   * 时间模板
      +   */
      +  time: string
      +}
      +
      +interface ReadingTimePluginLocaleConfig {
      +  [localePath: string]: ReadingTimePluginLocaleData
      +}
      +
    • 必填:否

    • 详情:

      阅读时间插件的国际化配置。

    内置支持语言
    • 简体中文 (zh-CN)
    • 繁体中文 (zh-TW)
    • 英文(美国) (en-US)
    • 德语 (de-DE)
    • 德语(澳大利亚) (de-AT)
    • 俄语 (ru-RU)
    • 乌克兰语 (uk-UA)
    • 越南语 (vi-VN)
    • 葡萄牙语(巴西) (pt-BR)
    • 波兰语 (pl-PL)
    • 法语 (fr-FR)
    • 西班牙语 (es-ES)
    • 斯洛伐克 (sk-SK)
    • 日语 (ja-JP)
    • 土耳其语 (tr-TR)
    • 韩语 (ko-KR)
    • 芬兰语 (fi-FI)
    • 印尼语 (id-ID)
    • 荷兰语 (nl-NL)

    客户端 API

    你可以从 @vuepress/plugin-reading-time/client 导入并使用这些 API:

    即使插件被禁用,这些 API 也不会抛出错误。

    useReadingTimeData

    interface ReadingTime {
    +  /** 分钟为单位的预计阅读时长 */
    +  minutes: number
    +  /** 内容的字数 */
    +  words: number
    +}
    +
    +const useReadingTimeData: () => ComputedRef<ReadingTime | null>
    +

    当插件被禁用时会返回 null

    useReadingTimeLocale

    interface ReadingTimeLocale {
    +  /** 当前语言的预计阅读时间 */
    +  time: string
    +  /** 当前语言的字数文字 */
    +  words: string
    +}
    +
    +const useReadingTimeLocale: () => ComputedRef<ReadingTimeLocale>
    +

    高级使用

    由于此插件主要面向插件和主题开发者,所以提供了 "使用 API":

    import { useReadingTimePlugin } from '@vuepress/plugin-reading-time'
    +
    +export default (options) => (app) => {
    +  useReadingTimePlugin(app, {
    +    // 你的选项
    +  })
    +
    +  return {
    +    name: 'vuepress-plugin-xxx', // or vuepress-theme-xxx
    +  }
    +}
    +

    为什么你应该使用 "使用 API"

    1. 当你多次注册一个插件时,vuepress 会给你一个警告,告诉你只有第一个插件会生效。useReadingTimePlugin 会自动检测插件是否已经注册,避免多次注册。

    2. 如果你在 extendsPage 生命周期访问阅读时间数据,那么 @vuepress/plugin-reading-time 必须在你的主题或插件之前被调用,否则你会得到未定义的 page.data.readingTimeuseReadingTimePlugin 确保了 @vuepress/plugin-reading-time 在你的主题或插件之前被调用。

    我们也提供了一个 removeReadingTimePlugin api 来移除插件。你可以使用它来确保你的调用生效或清除插件:

    import { useReadingTimePlugin } from '@vuepress/plugin-reading-time'
    +
    +export default (options) => (app) => {
    +  // 这会移除任何当前存在的阅读时间插件
    +  removeReadingTimePlugin(app)
    +
    +  // 所以这会生效,即使之前已经注册了一个阅读时间插件
    +  useReadingTimePlugin(app, {
    +    // 你的选项
    +  })
    +
    +  return {
    +    name: 'vuepress-plugin-xxx', // or vuepress-theme-xxx
    +  }
    +}
    +
    `,34);function d(D,u){const n=l("NpmBadge");return e(),p("div",null,[r,i(n,{package:"@vuepress/plugin-reading-time"}),t])}const y=a(c,[["render",d],["__file","reading-time.html.vue"]]),m=JSON.parse('{"path":"/zh/plugins/reading-time.html","title":"reading-time","lang":"zh-CN","frontmatter":{},"headers":[{"level":2,"title":"使用方法","slug":"使用方法","link":"#使用方法","children":[{"level":3,"title":"在 Node 侧获取数据","slug":"在-node-侧获取数据","link":"#在-node-侧获取数据","children":[]},{"level":3,"title":"在客户端侧获取数据","slug":"在客户端侧获取数据","link":"#在客户端侧获取数据","children":[]}]},{"level":2,"title":"选项","slug":"选项","link":"#选项","children":[{"level":3,"title":"wordPerMinute","slug":"wordperminute","link":"#wordperminute","children":[]},{"level":3,"title":"locales","slug":"locales","link":"#locales","children":[]}]},{"level":2,"title":"客户端 API","slug":"客户端-api","link":"#客户端-api","children":[{"level":3,"title":"useReadingTimeData","slug":"usereadingtimedata","link":"#usereadingtimedata","children":[]},{"level":3,"title":"useReadingTimeLocale","slug":"usereadingtimelocale","link":"#usereadingtimelocale","children":[]}]},{"level":2,"title":"高级使用","slug":"高级使用","link":"#高级使用","children":[]}],"git":{"updatedTime":1708274662000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":2}]},"filePathRelative":"zh/plugins/reading-time.md"}');export{y as comp,m as data}; diff --git a/assets/reading-time.html-DUOmNho8.js b/assets/reading-time.html-DUOmNho8.js new file mode 100644 index 0000000000..0812e1b97b --- /dev/null +++ b/assets/reading-time.html-DUOmNho8.js @@ -0,0 +1,95 @@ +import{_ as a,r as e,o as l,c as i,a as p,b as s,e as o}from"./app-DI49vYHA.js";const t={},r=s("h1",{id:"reading-time",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#reading-time"},[s("span",null,"reading-time")])],-1),c=o(`

    This plugin will generate word count and estimated reading time for each page.

    Usage

    npm i -D @vuepress/plugin-reading-time@next
    +
    import { readingTimePlugin } from '@vuepress/plugin-reading-time'
    +
    +export default {
    +  plugins: [
    +    readingTimePlugin({
    +      // options
    +    }),
    +  ],
    +}
    +

    The plugin will inject reading time information into the readingTime of the page data, where:

    • readingTime.minutes: estimated reading time (minutes) number
    • readingTime.words: word count number

    Getting data on Node Side

    For any page, you can get estimated reading time and word count from page.data.readingTime:

    page.data.readingTime // { minutes: 3.2, words: 934 }
    +

    You can access it for further processing in the extendsPage lifecycle and other lifecycle:

    export default {
    +  // ...
    +  extendsPage: (page) => {
    +    page.data.readingTime // { minutes: 3.2, words: 934 }
    +  },
    +
    +  onInitialized: (app) => {
    +    app.pages.map((page) => {
    +      page.data.readingTime // { minutes: 3.2, words: 934 }
    +    })
    +  },
    +}
    +

    Getting data on Client Side

    You can import useReadingTimeData and useReadingTimeLocale from @vuepress/plugin-reading-time/client to get the reading time data and locale data of the current page:

    <script setup lang="ts">
    +import {
    +  useReadingTimeData,
    +  useReadingTimeLocale,
    +} from '@vuepress/plugin-reading-time/client'
    +
    +const readingTimeData = useReadingTimeData() // { minutes: 1.1, words: 100 }
    +const readingTimeLocale = useReadingTimeLocale() // { time: "1 minute", words: "100 words" }
    +</script>
    +

    Options

    wordPerMinute

    • Type: number
    • Default: 300
    • Details: Reading speed (words per minute)

    locales

    • Type: ReadingTimePluginLocaleConfig

      interface ReadingTimePluginLocaleData {
      +  /**
      +   * Word template, \`$word\` will be automatically replaced by actual words
      +   */
      +  word: string
      +
      +  /**
      +   * Text for less than one minute
      +   */
      +  less1Minute: string
      +
      +  /**
      +   * Time template
      +   */
      +  time: string
      +}
      +
      +interface ReadingTimePluginLocaleConfig {
      +  [localePath: string]: ReadingTimePluginLocaleData
      +}
      +
    • Required: No

    • Details:

      Locales config for reading-time plugin.

    Built-in Supported Languages
    • Simplified Chinese (zh-CN)
    • Traditional Chinese (zh-TW)
    • English (United States) (en-US)
    • German (de-DE)
    • German (Australia) (de-AT)
    • Russian (ru-RU)
    • Ukrainian (uk-UA)
    • Vietnamese (vi-VN)
    • Portuguese (Brazil) (pt-BR)
    • Polish (pl-PL)
    • French (fr-FR)
    • Spanish (es-ES)
    • Slovak (sk-SK)
    • Japanese (ja-JP)
    • Turkish (tr-TR)
    • Korean (ko-KR)
    • Finnish (fi-FI)
    • Indonesian (id-ID)
    • Dutch (nl-NL)

    Client API

    You can import and use these APIs from @vuepress/plugin-reading-time/client:

    These APIs won't throw even you disable the plugin.

    useReadingTimeData

    interface ReadingTime {
    +  /** Expect reading time in minute unit */
    +  minutes: number
    +  /** Words count of content */
    +  words: number
    +}
    +
    +const useReadingTimeData: () => ComputedRef<ReadingTime | null>
    +

    null is returned when the plugin is disabled.

    useReadingTimeLocale

    interface ReadingTimeLocale {
    +  /** Expect reading time text in locale */
    +  time: string
    +  /** Word count text in locale */
    +  words: string
    +}
    +
    +const useReadingTimeLocale: () => ComputedRef<ReadingTimeLocale>
    +

    Advanced Usage

    This plugin is targeting plugin and theme developers mostly, so we provide a "Use API":

    import { useReadingTimePlugin } from '@vuepress/plugin-reading-time'
    +
    +export default (options) => (app) => {
    +  useReadingTimePlugin(app, {
    +    // your options
    +  })
    +
    +  return {
    +    name: 'vuepress-plugin-xxx', // or vuepress-theme-xxx
    +  }
    +}
    +

    Why you should use "Use API"

    1. When you register a plugin multiple times, vuepress will gives you warning about that telling you only the first one takes effect. The useReadingTimePlugin automatically detects if the plugin is registered and avoid registering multiple times.
    2. If you access reading time data in extendsPage lifecycle, then @vuepress/plugin-reading-time must be called before your theme or plugin, otherwise you will get undefined for page.data.readingTime. The useReadingTimePlugin ensures that @vuepress/plugin-reading-time is called before your theme or plugin.

    We also provides a removeReadingTimePlugin api to remove the plugin.You can use this to ensure your call take effect or clear the plugin:

    import { useReadingTimePlugin } from '@vuepress/plugin-reading-time'
    +
    +export default (options) => (app) => {
    +  // this removes any existing reading time plugin at this time
    +  removeReadingTimePlugin(app)
    +
    +  // so this will take effect even if there is a reading time plugin registered before
    +  useReadingTimePlugin(app, {
    +    // your options
    +  })
    +
    +  return {
    +    name: 'vuepress-plugin-xxx', // or vuepress-theme-xxx
    +  }
    +}
    +
    `,34);function d(D,u){const n=e("NpmBadge");return l(),i("div",null,[r,p(n,{package:"@vuepress/plugin-reading-time"}),c])}const g=a(t,[["render",d],["__file","reading-time.html.vue"]]),y=JSON.parse('{"path":"/plugins/reading-time.html","title":"reading-time","lang":"en-US","frontmatter":{},"headers":[{"level":2,"title":"Usage","slug":"usage","link":"#usage","children":[{"level":3,"title":"Getting data on Node Side","slug":"getting-data-on-node-side","link":"#getting-data-on-node-side","children":[]},{"level":3,"title":"Getting data on Client Side","slug":"getting-data-on-client-side","link":"#getting-data-on-client-side","children":[]}]},{"level":2,"title":"Options","slug":"options","link":"#options","children":[{"level":3,"title":"wordPerMinute","slug":"wordperminute","link":"#wordperminute","children":[]},{"level":3,"title":"locales","slug":"locales","link":"#locales","children":[]}]},{"level":2,"title":"Client API","slug":"client-api","link":"#client-api","children":[{"level":3,"title":"useReadingTimeData","slug":"usereadingtimedata","link":"#usereadingtimedata","children":[]},{"level":3,"title":"useReadingTimeLocale","slug":"usereadingtimelocale","link":"#usereadingtimelocale","children":[]}]},{"level":2,"title":"Advanced Usage","slug":"advanced-usage","link":"#advanced-usage","children":[]}],"git":{"updatedTime":1708274662000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":2}]},"filePathRelative":"plugins/reading-time.md"}');export{g as comp,y as data}; diff --git a/assets/redirect.html-CQdnRl7D.js b/assets/redirect.html-CQdnRl7D.js new file mode 100644 index 0000000000..c352b82687 --- /dev/null +++ b/assets/redirect.html-CQdnRl7D.js @@ -0,0 +1,81 @@ +import{_ as a,r as l,o as n,c as o,a as t,b as s,e as i}from"./app-DI49vYHA.js";const c={},p=s("h1",{id:"redirect",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#redirect"},[s("span",null,"redirect")])],-1),r=i(`

    This plugin can automatically handle redirects for your site.

    Usage

    npm i -D @vuepress/plugin-redirect@next
    +
    import { redirectPlugin } from '@vuepress/plugin-redirect'
    +
    +export default {
    +  plugins: [
    +    redirectPlugin({
    +      // options
    +    }),
    +  ],
    +}
    +

    Control Page Redirection

    If you change the address of an existing page, you can use the redirectFrom option in Frontmatter to redirect to the address of this page, which ensures that users are redirected to the new address when they visit the old link.

    If you need to redirect an existing page to a new page, you can use the redirectTo option in Frontmatter to set the address to redirect to. This way the page will redirect to the new address when accessed.

    You can also set config with a redirect map in plugin options, see config for more details.

    Auto Locales

    The plugin can automatically redirect non-multilingual links to the multilingual pages the user needs based on the user's language preference.

    To achieve this, you need to leave the default language directory (/) blank and set autoLocale: true in plugin options. The plugin will automatically redirect to the correct page according to the user's language.

    I.E.: you need to set the following directory structure:

    .
    +├── en
    +│   ├── ...
    +│   ├── page.md
    +│   └── README.md
    +├── zh
    +│   ├── ...
    +│   ├── page.md
    +│   └── README.md
    +└── other_languages
    +    ├── ...
    +    ├── page.md
    +    └── README.md
    +

    And set locales in theme options with:

    export default {
    +  locales: {
    +    '/en/': {
    +      lang: 'en-US',
    +      // ...
    +    },
    +    '/zh/': {
    +      lang: 'zh-CN',
    +      // ...
    +    },
    +    // other languages
    +  },
    +  // ...
    +}
    +

    So when a user accesses / or /page.html, they are automatically redirected to /en/ /en/page.html and /en/ /en/page.html based on current browser language.

    Customizing fallback behavior

    Sometimes, users may add more than one language to the system settings. By default, when a site supports a preferred language, but the page not exists for the preferred language, the plugin attempts to match the alternate language set by the user.

    If you don't need to fall back to the user's alternate language, but directly match the user's preferred language, set localeFallback: false in the plugin options.

    Customizing missing behavior

    Sometimes, when a user visits a page, the document does not yet contain the language version the user needs (a common case is that the current page has not been localized in the relevant language), so the plugin needs to perform a default action, which you can customize by defaultBehavior in the plugin options:

    • "defaultLocale": Redirect to default language or first available language page (default behavior)
    • "homepage": redirect to the home page in the current language (only available if the document contains the user's language)
    • "404": Redirect to page 404 in current language (only available if the document contains the user's language)

    Customizing default locale path

    You can customize the default locale path by setting defaultLocale in the plugin options. By default, the plugin uses the first locale key in locales as the default language.

    Automatically switch languages

    The plugin supports automatically switching the link to the multilingual page that the user needs according to the user's language preference when opening a multilingual document. In order to achieve this, you need to set switchLocale in the plugin options, which can be the following two values:

    • direct: switch directly to the user language preference page without asking
    • modal: When the user's language preference is different from the current page language, show a modal asking whether to switch language

    Customizing Locale Settings

    By default, the plugin generates a locale setting by reading locale path and lang from the site's locales option. Sometimes, you may want multiple languages to hit the same path, in which case you should set localeConfig in plugin options.

    For example, you might want all English users to match to /en/ and Chinese Traditional users to /zh/, then you can set:

    redirect({
    +  localeConfig: {
    +    '/en/': ['en-US', 'en-UK', 'en'],
    +    '/zh/': ['zh-CN', 'zh-TW', 'zh'],
    +  },
    +})
    +

    Redirecting Sites

    Sometimes you may change base or use new domain for your site, so you may want the original site automatically redirects to the new one.

    To solve this, the plugin provide vp-redirect cli.

    Usage:
    +  $ vp-redirect generate [sourceDir]
    +
    +Options:
    +  --hostname <hostname>  Hostname to redirect to (E.g.: https://new.example.com/) (default: /)
    +  -c, --config <config>  Set path to config file
    +  -o, --output <output>  Set the output directory (default: .vuepress/redirect)
    +  --cache <cache>        Set the directory of the cache files
    +  -t, --temp <temp>      Set the directory of the temporary files
    +  --clean-cache          Clean the cache files before generation
    +  --clean-temp           Clean the temporary files before generation
    +  -h, --help             Display this message
    +

    You need to pass in VuePress project source dir and also set the hostname option. The redirect helper cli will initialize your VuePress project to get pages, then generate and output the redirect html files to the output directory.

    By default, the plugin will output to .vuepress/redirect directory under source directory. And you should upload it to your original site to provide redirection.

    Options

    config

    • Type: Record<string, string> | ((app: App) => Record<string, string>)

    • Details: Redirect map.

    • Example:

      When base is set to /base/:

      • redirect /base/foo.html to /base/bar.html
      • /base/baz.html to https://example.com/qux.html.
      redirect({
      +  config: {
      +    '/foo.html': '/bar.html',
      +    '/baz.html': 'https://example.com/qux.html',
      +  },
      +})
      +

      Redirect post folder to posts folder:

      redirect({
      +  hostname: 'https://example.com',
      +  config: (app) =>
      +    Object.fromEntries(
      +      app.pages
      +        .filter(({ path }) => path.startsWith('/posts/'))
      +        .map(({ path }) => [path.replace(/^\\/posts\\//, '/post/'), path]),
      +    ),
      +})
      +

    autoLocale

    • Type: boolean
    • Default: false
    • Details: Whether enable locales redirection.

    switchLocale

    • Type: "direct" | "modal" | false

    • Default: false

    • Details:

      Whether switch to a new locale based on user preference.

      • "direct": redirect to the new locale directly without asking
      • "modal": show a modal to let user choose whether to switch to the new locale

    localeConfig

    • Type: Record<string, string | string[]>

    • Details: Locale language config

    localeFallback

    • Type: boolean
    • Default: true
    • Details: Whether fallback to other locales user defined

    defaultBehavior

    • Type: "defaultLocale" | "homepage" | "404"
    • Default: "defaultLocale"
    • Details: Behavior when a locale version is not available for current link.

    defaultLocale

    • Type: string
    • Default: the first locale
    • Details: Default locale path.

    Frontmatter

    redirectFrom

    • Type: string | string[]
    • Details: The link which this page redirects from.

    redirectTo

    • Type: string
    • Details: The link which this page redirects to.

    Styles

    You can customize the style of the redirect popup via CSS variables:

    :root {
    +  --redirect-z-index: 1499;
    +  --redirect-bg-color: #fff;
    +  --redirect-bg-color-light: #f3f4f5;
    +  --redirect-bg-color-lighter: #eeeeee;
    +  --redirect-text-color: #2c3e50;
    +  --redirect-primary-bg-color: #3eaf7c;
    +  --redirect-primary-hover-bg-color: #4abf8a;
    +  --redirect-primary-text-color: #fff;
    +}
    +
    `,55);function d(u,D){const e=l("NpmBadge");return n(),o("div",null,[p,t(e,{package:"@vuepress/plugin-redirect"}),r])}const y=a(c,[["render",d],["__file","redirect.html.vue"]]),g=JSON.parse('{"path":"/plugins/redirect.html","title":"redirect","lang":"en-US","frontmatter":{},"headers":[{"level":2,"title":"Usage","slug":"usage","link":"#usage","children":[{"level":3,"title":"Control Page Redirection","slug":"control-page-redirection","link":"#control-page-redirection","children":[]},{"level":3,"title":"Auto Locales","slug":"auto-locales","link":"#auto-locales","children":[]},{"level":3,"title":"Automatically switch languages","slug":"automatically-switch-languages","link":"#automatically-switch-languages","children":[]},{"level":3,"title":"Customizing Locale Settings","slug":"customizing-locale-settings","link":"#customizing-locale-settings","children":[]},{"level":3,"title":"Redirecting Sites","slug":"redirecting-sites","link":"#redirecting-sites","children":[]}]},{"level":2,"title":"Options","slug":"options","link":"#options","children":[{"level":3,"title":"config","slug":"config","link":"#config","children":[]},{"level":3,"title":"autoLocale","slug":"autolocale","link":"#autolocale","children":[]},{"level":3,"title":"switchLocale","slug":"switchlocale","link":"#switchlocale","children":[]},{"level":3,"title":"localeConfig","slug":"localeconfig","link":"#localeconfig","children":[]},{"level":3,"title":"localeFallback","slug":"localefallback","link":"#localefallback","children":[]},{"level":3,"title":"defaultBehavior","slug":"defaultbehavior","link":"#defaultbehavior","children":[]},{"level":3,"title":"defaultLocale","slug":"defaultlocale","link":"#defaultlocale","children":[]}]},{"level":2,"title":"Frontmatter","slug":"frontmatter","link":"#frontmatter","children":[{"level":3,"title":"redirectFrom","slug":"redirectfrom","link":"#redirectfrom","children":[]},{"level":3,"title":"redirectTo","slug":"redirectto","link":"#redirectto","children":[]}]},{"level":2,"title":"Styles","slug":"styles","link":"#styles","children":[]}],"git":{"updatedTime":1707223547000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":3}]},"filePathRelative":"plugins/redirect.md"}');export{y as comp,g as data}; diff --git a/assets/redirect.html-wE1TgcR8.js b/assets/redirect.html-wE1TgcR8.js new file mode 100644 index 0000000000..9571f73070 --- /dev/null +++ b/assets/redirect.html-wE1TgcR8.js @@ -0,0 +1,81 @@ +import{_ as c,r as l,o as i,c as r,a as n,b as s,d as a,w as t,e}from"./app-DI49vYHA.js";const d={},D=s("h1",{id:"redirect",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#redirect"},[s("span",null,"redirect")])],-1),u=e(`

    此插件提供页面与整站重定向功能。

    使用方法

    npm i -D @vuepress/plugin-redirect@next
    +
    import { redirectPlugin } from '@vuepress/plugin-redirect'
    +
    +export default {
    +  plugins: [
    +    redirectPlugin({
    +      // 配置项
    +    }),
    +  ],
    +}
    +

    设置重定向

    如果你改动了已有页面的地址,你可以在 Frontmatter 中使用 redirectFrom 选项设置重定向到此页面的地址,这样可以保证用户在访问旧链接时重定向到新的地址。

    如果你需要将已有的页面重定向到新的页面,可以在 Frontmatter 中使用 redirectTo 选项设置需要重定向到的地址。这样该页面会在访问时重定向到新的地址。

    你还可以通过插件选项中的 config 设置一个重定向映射,详见 config

    自动多语言

    插件可以根据用户的语言首选项,自动将无多语言链接重定向到用户需要的多语言页面。为了实现这一点,你需要留空默认的语言目录 (/),并在插件选项中设置 autoLocale: true。插件会自动根据用户语言跳转到对应的语言页面。

    也就是你需要设置以下目录结构:

    .
    +├── en
    +│   ├── ...
    +│   ├── page.md
    +│   └── README.md
    +├── zh
    +│   ├── ...
    +│   ├── page.md
    +│   └── README.md
    +└── other_languages
    +    ├── ...
    +    ├── page.md
    +    └── README.md
    +

    并将主题选项的 locales 设置为:

    export default {
    +  locales: {
    +    '/en/': {
    +      lang: 'en-US',
    +      // ...
    +    },
    +    '/zh/': {
    +      lang: 'zh-CN',
    +      // ...
    +    },
    +    // other languages
    +  },
    +  // ...
    +}
    +

    这样当用户访问 //page.html 时,他们会自动根据当前浏览器语言重定向到 /en/ /en/page.html/zh/ /zh/page.html

    自定义回退行为

    有些时候,用户可能会在系统设置中添加多个语言。默认情况下,在站点支持首选语言,但首选语言不存在相应页面时,插件会尝试匹配用户设置的备用语言。

    如果不需要回退到用户备用语言,而直接匹配用户首选语言,请在插件选项中设置 localeFallback: false

    自定义缺失行为

    有些时候,当用户访问一个页面时,文档尚未包含用户需要的语言版本 (一个普遍的情况是当前页面尚未完成相关语言的本地化),这样插件需要做出默认行为,你可以通过插件选项中的 defaultBehavior 定制它:

    • "defaultLocale": 重定向到默认语言或首个可用语言页面 (默认行为)
    • "homepage": 重定向到当前语言的主页 (仅在文档包含用户语言时可用)
    • "404": 重定向到当前语言的 404 页 (仅在文档包含用户语言时可用)

    自定义默认路径

    你可以通过设置插件选项中的 defaultLocale 来自定义默认路径。默认情况下,插件会使用 locales 中的第一个键名作为默认路径。

    自动切换语言

    插件支持在多语言文档中,自动根据用户语言首选项,将链接切换到用户需要的多语言页面。为了实现这一点,你需要在插件选项中设置 switchLocale,它可以是以下两个值:

    • direct: 直接切换到用户语言首选项页面,而不询问
    • modal: 在用户语言首选项与当前页面语言不同时,弹出一个对话框询问用户是否切换语言

    自定义多语言配置

    默认情况下,插件会从站点的多语言配置 locales 选项中,读取 语言路径lang 生成多语言配置。有些时候,你可能希望多个语言命中同一个路径,这种情况下,你应该设置插件的 localeConfig 选项。

    比如,你可能希望所有英文用户都匹配到 /en/,并将繁体中文用户匹配到 /zh/ 中,那么你可以设置:

    redirect({
    +  localeConfig: {
    +    '/en/': ['en-US', 'en-UK', 'en'],
    +    '/zh/': ['zh-CN', 'zh-TW', 'zh'],
    +  },
    +})
    +

    重定向站点

    有时你可能会更改 base 或为你的站点使用新域名,因此你可能希望原始站点自动重定向到新站点。

    为了解决这个问题,插件提供了 vp-redirect 脚手架。

    使用:
    +  $ vp-redirect generate [源文件夹]
    +
    +Options:
    +  --hostname <hostname>  重定向到的域名 (例如: https://new.example.com/) (默认: /)
    +  -c, --config <config>  设置配置文件路径
    +  -o, --output <output>  设置输出目录 (默认: .vuepress/redirect)
    +  --cache <cache>        设置缓存文件的目录
    +  -t, --temp <temp>      设置临时文件的目录
    +  --clean-cache          生成前清理缓存文件
    +  --clean-temp           生成前清理临时文件
    +  -h, --help             显示此消息
    +

    你需要传入 VuePress 项目源目录并设置 hostname 选项。重定向助手脚手架将初始化你的 VuePress 项目以获取页面,然后在输出目录生成重定向 html 文件。

    默认情况下,插件将输出到源文件夹下的 .vuepress/redirect 目录。你应该将其上传到你的原始站点以提供重定向。

    选项

    config

    • 类型:Record<string, string> | ((app: App) => Record<string, string>)

    • 详情

      页面重定向映射。

      可直接传入对象或传入参数为 App 的函数返回值一个对象。

      每个键名必须是一个绝对路径,代表重定向的源页面地址。

      每个键值是重定向的目标地址,可以是绝对路径或完整路径。

    • 示例:

      当 base 为 /base/时:

      • /base/foo.html 重定向到 /base/bar.html
      • /base/baz.html 重定向到 https://example.com/qux.html
      redirect({
      +  config: {
      +    '/foo.html': '/bar.html',
      +    '/baz.html': 'https://example.com/qux.html',
      +  },
      +})
      +

      将 post 文件夹的路径重定向到 posts 文件夹

      redirect({
      +  hostname: 'https://example.com',
      +  config: (app) =>
      +    Object.fromEntries(
      +      app.pages
      +        .filter(({ path }) => path.startsWith('/posts/'))
      +        .map(({ path }) => [path.replace(/^\\/posts\\//, '/post/'), path]),
      +    ),
      +})
      +

    autoLocale

    `,35),v=s("li",null,[a("类型:"),s("code",null,"boolean")],-1),y=s("li",null,[a("默认值: "),s("code",null,"false")],-1),h=s("li",null,"详情: 是否启用语言重定向",-1),m=e(`

    switchLocale

    • 类型:"direct" | "modal" | false

    • 默认值: false

    • 详情:

      是否根据用户偏好切换到新的语言环境。

      • "direct": 直接重定向到新的语言环境而不询问
      • "modal": 显示一个模式让用户选择是否切换到新的语言环境

    localeConfig

    • 类型:Record<string, string | string[]>
    • 详情:多语言语言配置

    localeFallback

    • 类型:boolean
    • 默认值: true
    • 详情:是否回退到用户定义的其他语言

    defaultBehavior

    • 类型:"defaultLocale" | "homepage" | "404"
    • 默认值: "defaultLocale"
    • 详情:当前链接没有可用的语言版本时的行为

    defaultLocale

    • 类型:string
    • 默认值: 首个语言路径
    • 详情:默认语言路径

    Frontmatter

    redirectFrom

    • 类型:string | string[]
    • 详情:重定向到该页面的地址。

    redirectTo

    • 类型:string
    • 详情:该页面重定向到的地址。

    样式

    你可以通过 CSS 变量来自定义重定向弹窗的样式:

    :root {
    +  --redirect-z-index: 1499;
    +  --redirect-bg-color: #fff;
    +  --redirect-bg-color-light: #f3f4f5;
    +  --redirect-bg-color-lighter: #eeeeee;
    +  --redirect-text-color: #2c3e50;
    +  --redirect-primary-bg-color: #3eaf7c;
    +  --redirect-primary-hover-bg-color: #4abf8a;
    +  --redirect-primary-text-color: #fff;
    +}
    +
    `,18);function C(b,g){const o=l("NpmBadge"),p=l("RouteLink");return i(),r("div",null,[D,n(o,{package:"@vuepress/plugin-rtl"}),u,s("ul",null,[v,y,h,s("li",null,[a("参考: "),s("ul",null,[s("li",null,[n(p,{to:"/zh/plugins/guide.html#%E9%87%8D%E5%AE%9A%E5%90%91%E8%AF%AD%E8%A8%80"},{default:t(()=>[a("指南 → 重定向语言")]),_:1})])])])]),m])}const f=c(d,[["render",C],["__file","redirect.html.vue"]]),A=JSON.parse('{"path":"/zh/plugins/redirect.html","title":"redirect","lang":"zh-CN","frontmatter":{},"headers":[{"level":2,"title":"使用方法","slug":"使用方法","link":"#使用方法","children":[{"level":3,"title":"设置重定向","slug":"设置重定向","link":"#设置重定向","children":[]},{"level":3,"title":"自动多语言","slug":"自动多语言","link":"#自动多语言","children":[]},{"level":3,"title":"自动切换语言","slug":"自动切换语言","link":"#自动切换语言","children":[]},{"level":3,"title":"自定义多语言配置","slug":"自定义多语言配置","link":"#自定义多语言配置","children":[]},{"level":3,"title":"重定向站点","slug":"重定向站点","link":"#重定向站点","children":[]}]},{"level":2,"title":"选项","slug":"选项","link":"#选项","children":[{"level":3,"title":"config","slug":"config","link":"#config","children":[]},{"level":3,"title":"autoLocale","slug":"autolocale","link":"#autolocale","children":[]},{"level":3,"title":"switchLocale","slug":"switchlocale","link":"#switchlocale","children":[]},{"level":3,"title":"localeConfig","slug":"localeconfig","link":"#localeconfig","children":[]},{"level":3,"title":"localeFallback","slug":"localefallback","link":"#localefallback","children":[]},{"level":3,"title":"defaultBehavior","slug":"defaultbehavior","link":"#defaultbehavior","children":[]},{"level":3,"title":"defaultLocale","slug":"defaultlocale","link":"#defaultlocale","children":[]}]},{"level":2,"title":"Frontmatter","slug":"frontmatter","link":"#frontmatter","children":[{"level":3,"title":"redirectFrom","slug":"redirectfrom","link":"#redirectfrom","children":[]},{"level":3,"title":"redirectTo","slug":"redirectto","link":"#redirectto","children":[]}]},{"level":2,"title":"样式","slug":"样式","link":"#样式","children":[]}],"git":{"updatedTime":1707152654000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":2}]},"filePathRelative":"zh/plugins/redirect.md"}');export{f as comp,A as data}; diff --git a/assets/register-components.html-CSH-WB-N.js b/assets/register-components.html-CSH-WB-N.js new file mode 100644 index 0000000000..bcd3c2b636 --- /dev/null +++ b/assets/register-components.html-CSH-WB-N.js @@ -0,0 +1,49 @@ +import{_ as t,r as a,o as i,c,a as e,b as s,d as n,e as l}from"./app-DI49vYHA.js";const r={},D=s("h1",{id:"register-components",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#register-components"},[s("span",null,"register-components")])],-1),d=l(`

    根据组件文件或目录自动注册 Vue 组件。

    使用方法

    npm i -D @vuepress/plugin-register-components@next
    +
    import { registerComponentsPlugin } from '@vuepress/plugin-register-components'
    +
    +export default {
    +  plugins: [
    +    registerComponentsPlugin({
    +      // 配置项
    +    }),
    +  ],
    +}
    +

    配置项

    components

    • 类型: Record<string, string>

    • 默认值: {}

    • 详情:

      一个定义了组件名称和其对应文件路径的对象。

      键会被用作组件名称,值是组件文件的绝对路径。

      如果该配置项中的组件名称和 componentsDir 配置项发生冲突,那么该配置项会有更高的优先级。

    • 示例:

    import { getDirname, path } from 'vuepress/utils'
    +
    +const __dirname = getDirname(import.meta.url)
    +
    +export default {
    +  plugins: [
    +    registerComponentsPlugin({
    +      components: {
    +        FooBar: path.resolve(__dirname, './components/FooBar.vue'),
    +      },
    +    }),
    +  ],
    +}
    +

    componentsDir

    • 类型: string | null

    • 默认值: null

    • 详情:

      组件目录的绝对路径。

      该目录下匹配 componentsPatterns 的文件会被自动注册为 Vue 组件。

    • 示例:

    import { getDirname, path } from 'vuepress/utils'
    +
    +const __dirname = getDirname(import.meta.url)
    +
    +export default {
    +  plugins: [
    +    registerComponentsPlugin({
    +      componentsDir: path.resolve(__dirname, './components'),
    +    }),
    +  ],
    +}
    +

    组件目录:

    components
    +├─ FooBar.vue
    +└─ Baz.vue
    +

    组件会像这样被注册:

    import { defineAsyncComponent } from 'vue'
    +
    +app.component(
    +  'FooBar',
    +  defineAsyncComponent(() => import('/path/to/components/FooBar.vue')),
    +)
    +
    +app.component(
    +  'Baz',
    +  defineAsyncComponent(() => import('/path/to/components/Baz.vue')),
    +)
    +

    componentsPatterns

    `,16),m=s("li",null,[s("p",null,[n("类型: "),s("code",null,"string[]")])],-1),u=s("li",null,[s("p",null,[n("默认值: "),s("code",null,"['**/*.vue']")])],-1),y=s("p",null,"详情:",-1),v={href:"https://github.com/sindresorhus/globby",target:"_blank",rel:"noopener noreferrer"},C=s("p",null,[n("该 Patterns 是相对于 "),s("a",{href:"#componentsdir"},"componentsDir"),n(" 目录的。")],-1),h=l('

    getComponentName

    • 类型: (filename: string) => string

    • 默认值: (filename) => path.trimExt(filename.replace(/\\/|\\\\/g, '-'))

    • 详情:

      用于从文件名获取对应组件名称的函数。

      它只会对 componentsDir 目录下匹配了 componentsPatterns 的文件生效。

      注意,这里的 filename 是相对于 componentsPatterns 目录的文件路径。

    ',2);function b(g,E){const p=a("NpmBadge"),o=a("ExternalLinkIcon");return i(),c("div",null,[D,e(p,{package:"@vuepress/plugin-register-components"}),d,s("ul",null,[m,u,s("li",null,[y,s("p",null,[n("使用 "),s("a",v,[n("globby"),e(o)]),n(" 来匹配组件文件的 Patterns 。")]),C])]),h])}const f=t(r,[["render",b],["__file","register-components.html.vue"]]),A=JSON.parse('{"path":"/zh/plugins/register-components.html","title":"register-components","lang":"zh-CN","frontmatter":{},"headers":[{"level":2,"title":"使用方法","slug":"使用方法","link":"#使用方法","children":[]},{"level":2,"title":"配置项","slug":"配置项","link":"#配置项","children":[{"level":3,"title":"components","slug":"components","link":"#components","children":[]},{"level":3,"title":"componentsDir","slug":"componentsdir","link":"#componentsdir","children":[]},{"level":3,"title":"componentsPatterns","slug":"componentspatterns","link":"#componentspatterns","children":[]},{"level":3,"title":"getComponentName","slug":"getcomponentname","link":"#getcomponentname","children":[]}]}],"git":{"updatedTime":1706605723000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":1}]},"filePathRelative":"zh/plugins/register-components.md"}');export{f as comp,A as data}; diff --git a/assets/register-components.html-nZSf746Y.js b/assets/register-components.html-nZSf746Y.js new file mode 100644 index 0000000000..4bad1ab7ac --- /dev/null +++ b/assets/register-components.html-nZSf746Y.js @@ -0,0 +1,49 @@ +import{_ as t,r as e,o as i,c,a,b as s,d as n,e as l}from"./app-DI49vYHA.js";const r={},D=s("h1",{id:"register-components",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#register-components"},[s("span",null,"register-components")])],-1),d=l(`

    Register Vue components from component files or directory automatically.

    Usage

    npm i -D @vuepress/plugin-register-components@next
    +
    import { registerComponentsPlugin } from '@vuepress/plugin-register-components'
    +
    +export default {
    +  plugins: [
    +    registerComponentsPlugin({
    +      // options
    +    }),
    +  ],
    +}
    +

    Options

    components

    • Type: Record<string, string>

    • Default: {}

    • Details:

      An object that defines name of components and their corresponding file path.

      The key will be used as the component name, and the value is an absolute path of the component file.

      If the component name from this option conflicts with componentsDir option, this option will have a higher priority.

    • Example:

    import { getDirname, path } from 'vuepress/utils'
    +
    +const __dirname = getDirname(import.meta.url)
    +
    +export default {
    +  plugins: [
    +    registerComponentsPlugin({
    +      components: {
    +        FooBar: path.resolve(__dirname, './components/FooBar.vue'),
    +      },
    +    }),
    +  ],
    +}
    +

    componentsDir

    • Type: string | null

    • Default: null

    • Details:

      Absolute path to the components directory.

      Files in this directory which are matched with componentsPatterns will be registered as Vue components automatically.

    • Example:

    import { getDirname, path } from 'vuepress/utils'
    +
    +const __dirname = getDirname(import.meta.url)
    +
    +export default {
    +  plugins: [
    +    registerComponentsPlugin({
    +      componentsDir: path.resolve(__dirname, './components'),
    +    }),
    +  ],
    +}
    +

    Components directory:

    components
    +├─ FooBar.vue
    +└─ Baz.vue
    +

    Components will be registered like this:

    import { defineAsyncComponent } from 'vue'
    +
    +app.component(
    +  'FooBar',
    +  defineAsyncComponent(() => import('/path/to/components/FooBar.vue')),
    +)
    +
    +app.component(
    +  'Baz',
    +  defineAsyncComponent(() => import('/path/to/components/Baz.vue')),
    +)
    +

    componentsPatterns

    `,16),m=s("li",null,[s("p",null,[n("Type: "),s("code",null,"string[]")])],-1),u=s("li",null,[s("p",null,[n("Default: "),s("code",null,"['**/*.vue']")])],-1),y=s("p",null,"Details:",-1),v={href:"https://github.com/sindresorhus/globby",target:"_blank",rel:"noopener noreferrer"},C=s("p",null,[n("The patterns are relative to "),s("a",{href:"#componentsdir"},"componentsDir"),n(".")],-1),h=l('

    getComponentName

    • Type: (filename: string) => string

    • Default: (filename) => path.trimExt(filename.replace(/\\/|\\\\/g, '-'))

    • Details:

      A function to get component name from the filename.

      It will only take effect on the files in the componentsDir which are matched with the componentsPatterns.

      Notice that the filename is a filepath relative to componentsDir.

    ',2);function b(g,f){const o=e("NpmBadge"),p=e("ExternalLinkIcon");return i(),c("div",null,[D,a(o,{package:"@vuepress/plugin-register-components"}),d,s("ul",null,[m,u,s("li",null,[y,s("p",null,[n("Patterns to match component files using "),s("a",v,[n("globby"),a(p)]),n(".")]),C])]),h])}const _=t(r,[["render",b],["__file","register-components.html.vue"]]),A=JSON.parse('{"path":"/plugins/register-components.html","title":"register-components","lang":"en-US","frontmatter":{},"headers":[{"level":2,"title":"Usage","slug":"usage","link":"#usage","children":[]},{"level":2,"title":"Options","slug":"options","link":"#options","children":[{"level":3,"title":"components","slug":"components","link":"#components","children":[]},{"level":3,"title":"componentsDir","slug":"componentsdir","link":"#componentsdir","children":[]},{"level":3,"title":"componentsPatterns","slug":"componentspatterns","link":"#componentspatterns","children":[]},{"level":3,"title":"getComponentName","slug":"getcomponentname","link":"#getcomponentname","children":[]}]}],"git":{"updatedTime":1706605723000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":1}]},"filePathRelative":"plugins/register-components.md"}');export{_ as comp,A as data}; diff --git a/assets/remove-pwa.html-DyC9esSj.js b/assets/remove-pwa.html-DyC9esSj.js new file mode 100644 index 0000000000..87558b4002 --- /dev/null +++ b/assets/remove-pwa.html-DyC9esSj.js @@ -0,0 +1,11 @@ +import{_ as o,r as s,o as t,c as r,a,b as e,d as n,w as c,e as p}from"./app-DI49vYHA.js";const d={},u=e("h1",{id:"remove-pwa",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#remove-pwa"},[e("span",null,"remove-pwa")])],-1),h=e("p",null,"This plugin removes any related service worker from your VuePress site, so that users can still get updates if you removed any PWA plugin after enabling it.",-1),v={class:"custom-container tip"},m=e("p",{class:"custom-container-title"},"Why this plugin is needed if you used PWA plugin once?",-1),g=e("code",null,"@vuepress/plugin-pwa",-1),w=e("p",null,"However, if you remove pwa plugin, the old service worker will still be there, but they can never get an update because they can never found a new service worker to update to. So users will stay with the old version of your site.",-1),D=e("p",null,"To solve this problem:",-1),y=e("ol",null,[e("li",null,"A new service worker with empty contents shall be generated in the original place."),e("li",null,"The new service worker shall attempt to remove contents that old service worker cached, then it should unregister itself.")],-1),f=p(`

    Usage

    npm i -D @vuepress/plugin-remove-pwa@next
    +
    import { removePwaPlugin } from '@vuepress/plugin-remove-pwa'
    +
    +export default {
    +  plugins: [
    +    removePwaPlugin({
    +      // options
    +    }),
    +  ],
    +}
    +

    Options

    cachePrefix

    • Type: string
    • Default: 'workbox'
    • Details: The cache prefix for the service worker.

    swLocation

    • Type: string
    • Default: 'service-worker.js'
    • Details: The location of the old service worker.
    `,8);function _(b,k){const l=s("NpmBadge"),i=s("RouteLink");return t(),r("div",null,[u,a(l,{package:"@vuepress/plugin-remove-pwa"}),h,e("div",v,[m,e("p",null,[n("PWA plugins like "),a(i,{to:"/plugins/pwa.html"},{default:c(()=>[g]),_:1}),n(" register service worker to your site, which will cache your site and make it available offline.")]),w,D,y]),f])}const C=o(d,[["render",_],["__file","remove-pwa.html.vue"]]),P=JSON.parse('{"path":"/plugins/remove-pwa.html","title":"remove-pwa","lang":"en-US","frontmatter":{},"headers":[{"level":2,"title":"Usage","slug":"usage","link":"#usage","children":[]},{"level":2,"title":"Options","slug":"options","link":"#options","children":[{"level":3,"title":"cachePrefix","slug":"cacheprefix","link":"#cacheprefix","children":[]},{"level":3,"title":"swLocation","slug":"swlocation","link":"#swlocation","children":[]}]}],"git":{"updatedTime":1707223547000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":2}]},"filePathRelative":"plugins/remove-pwa.md"}');export{C as comp,P as data}; diff --git a/assets/remove-pwa.html-obHjlz2h.js b/assets/remove-pwa.html-obHjlz2h.js new file mode 100644 index 0000000000..fd45ded215 --- /dev/null +++ b/assets/remove-pwa.html-obHjlz2h.js @@ -0,0 +1,11 @@ +import{_ as o,r as s,o as r,c,a,b as e,d as n,w as t,e as p}from"./app-DI49vYHA.js";const d={},u=e("h1",{id:"remove-pwa",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#remove-pwa"},[e("span",null,"remove-pwa")])],-1),h=e("p",null,"此插件从你的 VuePress 站点中删除任何相关的 Service Worker,因此如果你在启用后任何 PWA 插件后移除它们,用户仍然可以获得更新。",-1),v={class:"custom-container tip"},m=e("p",{class:"custom-container-title"},"如果你启用过 PWA,为什么需要这个插件?",-1),D=e("code",null,"@vuepress/plugin-pwa",-1),_=e("p",null,"但是,如果你删除 PWA 插件,先前的 Service Worker 仍将在那里,但它们永远无法获得更新,因为他们永远无法找到要更新的新 Service Worker。 因此,用户将继续使用你网站的旧版本。",-1),b=e("p",null,"要解决这个问题:",-1),g=e("ol",null,[e("li",null,[e("p",null,"一个新的内容为空的 Service Worker 需要生成在原位置。")]),e("li",null,[e("p",null,"新的 Service Worker 应该尝试删除旧 Service Worker 缓存的内容,然后它应该注销自己。")])],-1),k=p(`

    使用方法

    npm i -D @vuepress/plugin-remove-pwa@next
    +
    import { removePwaPlugin } from '@vuepress/plugin-remove-pwa'
    +
    +export default {
    +  plugins: [
    +    removePwaPlugin({
    +      // options
    +    }),
    +  ],
    +}
    +

    选项

    cachePrefix

    • 类型:string
    • 默认值:'workbox'
    • 详情:Service worker 的缓存前缀。

    swLocation

    • 类型: string
    • 默认值:'service-worker.js'
    • 详情:旧 Service Worker 的位置。
    `,8);function w(x,f){const l=s("NpmBadge"),i=s("RouteLink");return r(),c("div",null,[u,a(l,{package:"@vuepress/plugin-remove-pwa"}),h,e("div",v,[m,e("p",null,[n("PWA 插件,如 "),a(i,{to:"/zh/plugins/pwa.html"},{default:t(()=>[D]),_:1}),n(" 将 Service Worker 注册到你的站点,这将缓存你的站点并使其离线可用。")]),_,b,g]),k])}const C=o(d,[["render",w],["__file","remove-pwa.html.vue"]]),P=JSON.parse('{"path":"/zh/plugins/remove-pwa.html","title":"remove-pwa","lang":"zh-CN","frontmatter":{},"headers":[{"level":2,"title":"使用方法","slug":"使用方法","link":"#使用方法","children":[]},{"level":2,"title":"选项","slug":"选项","link":"#选项","children":[{"level":3,"title":"cachePrefix","slug":"cacheprefix","link":"#cacheprefix","children":[]},{"level":3,"title":"swLocation","slug":"swlocation","link":"#swlocation","children":[]}]}],"git":{"updatedTime":1707223547000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":2}]},"filePathRelative":"zh/plugins/remove-pwa.md"}');export{C as comp,P as data}; diff --git a/assets/rtl.html-COWcRLLy.js b/assets/rtl.html-COWcRLLy.js new file mode 100644 index 0000000000..4ee8841824 --- /dev/null +++ b/assets/rtl.html-COWcRLLy.js @@ -0,0 +1,17 @@ +import{_ as e,r as a,o as n,c as p,a as o,b as s,e as i}from"./app-DI49vYHA.js";const c={},r=s("h1",{id:"rtl",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#rtl"},[s("span",null,"rtl")])],-1),t=i(`

    此插件会在配置的语言上设置 rtl 方向。

    使用方法

    npm i -D @vuepress/plugin-rtl@next
    +
    import { rtlPlugin } from '@vuepress/plugin-rtl'
    +
    +export default {
    +  plugins: [
    +    rtlPlugin({
    +      // 配置项
    +      locales: ['/ar/'],
    +    }),
    +  ],
    +}
    +

    选项

    locales

    • 类型:string[]
    • 默认值:['/']
    • 详情: 开启 RTL 布局的多语言路径。

    selector

    • 类型:SelectorOptions

      interface SelectorOptions {
      +  [element: string]: {
      +    [attrs: string]: string
      +  }
      +}
      +
    • 默认值:{ 'html': { dir: 'rtl' } }

    • 详情:

      开启 RTL 的选择器。

      默认设置意味着在 RTL 多语言中,html 元素的 dir 属性将被设置为 rtl

    `,9);function d(D,u){const l=a("NpmBadge");return n(),p("div",null,[r,o(l,{package:"@vuepress/plugin-rtl"}),t])}const h=e(c,[["render",d],["__file","rtl.html.vue"]]),m=JSON.parse('{"path":"/zh/plugins/rtl.html","title":"rtl","lang":"zh-CN","frontmatter":{},"headers":[{"level":2,"title":"使用方法","slug":"使用方法","link":"#使用方法","children":[]},{"level":2,"title":"选项","slug":"选项","link":"#选项","children":[{"level":3,"title":"locales","slug":"locales","link":"#locales","children":[]},{"level":3,"title":"selector","slug":"selector","link":"#selector","children":[]}]}],"git":{"updatedTime":1706801200000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":1}]},"filePathRelative":"zh/plugins/rtl.md"}');export{h as comp,m as data}; diff --git a/assets/rtl.html-DbExLeFw.js b/assets/rtl.html-DbExLeFw.js new file mode 100644 index 0000000000..a4e1b81dad --- /dev/null +++ b/assets/rtl.html-DbExLeFw.js @@ -0,0 +1,17 @@ +import{_ as e,r as a,o as n,c as o,a as t,b as s,e as i}from"./app-DI49vYHA.js";const p={},c=s("h1",{id:"rtl",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#rtl"},[s("span",null,"rtl")])],-1),r=i(`

    This plugin will set direction to rtl on configured locales.

    Usage

    npm i -D @vuepress/plugin-rtl@next
    +
    import { rtlPlugin } from '@vuepress/plugin-rtl'
    +
    +export default {
    +  plugins: [
    +    rtlPlugin({
    +      // options
    +      locales: ['/ar/'],
    +    }),
    +  ],
    +}
    +

    Options

    locales

    • Type: string[]
    • Default: ['/']
    • Details: Locale path to enable rtl.

    selector

    • Type: SelectorOptions

      interface SelectorOptions {
      +  [element: string]: {
      +    [attrs: string]: string
      +  }
      +}
      +
    • Default: { 'html': { dir: 'rtl' } }

    • Details:

      Selector to enable rtl.

      The default settings mean that the dir attribute of the html element will be set to rtl in rtl locales.

    `,9);function d(D,u){const l=a("NpmBadge");return n(),o("div",null,[c,t(l,{package:"@vuepress/plugin-rtl"}),r])}const v=e(p,[["render",d],["__file","rtl.html.vue"]]),m=JSON.parse('{"path":"/plugins/rtl.html","title":"rtl","lang":"en-US","frontmatter":{},"headers":[{"level":2,"title":"Usage","slug":"usage","link":"#usage","children":[]},{"level":2,"title":"Options","slug":"options","link":"#options","children":[{"level":3,"title":"locales","slug":"locales","link":"#locales","children":[]},{"level":3,"title":"selector","slug":"selector","link":"#selector","children":[]}]}],"git":{"updatedTime":1706801200000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":1}]},"filePathRelative":"plugins/rtl.md"}');export{v as comp,m as data}; diff --git a/assets/search.html-BNGeFq23.js b/assets/search.html-BNGeFq23.js new file mode 100644 index 0000000000..666d410314 --- /dev/null +++ b/assets/search.html-BNGeFq23.js @@ -0,0 +1,75 @@ +import{_ as t,r as l,o as r,c as d,a as e,b as s,d as n,w as o,e as a}from"./app-DI49vYHA.js";const D={},u=s("h1",{id:"search",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#search"},[s("span",null,"search")])],-1),h=a(`

    Provide local search to your documentation site.

    TIP

    Default theme will add search box to the navbar once you configure this plugin correctly.

    This plugin may not be used directly in other themes, so you'd better refer to the documentation of your theme for more details.

    Usage

    npm i -D @vuepress/plugin-search@next
    +
    import { searchPlugin } from '@vuepress/plugin-search'
    +
    +export default {
    +  plugins: [
    +    searchPlugin({
    +      // options
    +    }),
    +  ],
    +}
    +

    Local Search Index

    This plugin will generate search index from your pages locally, and load the search index file when users enter your site. In other words, this is a lightweight built-in search which does not require any external requests.

    `,7),y=a(`

    Options

    locales

    • Type: Record<string, { placeholder: string }>

    • Details:

      The text of the search box in different locales.

      If this option is not specified, it will fallback to default text.

    • Example:

    export default {
    +  plugins: [
    +    searchPlugin({
    +      locales: {
    +        '/': {
    +          placeholder: 'Search',
    +        },
    +        '/zh/': {
    +          placeholder: '搜索',
    +        },
    +      },
    +    }),
    +  ],
    +}
    +
    `,4),v=a(`

    hotKeys

    • Type: (string | HotKeyOptions)[]
    export interface HotKeyOptions {
    +    /**
    +     * Value of \`event.key\` to trigger the hot key
    +     */
    +    key: string;
    +    /**
    +     * Whether to press \`event.altKey\` at the same time
    +     *
    +     * @default false
    +     */
    +    alt?: boolean;
    +    /**
    +     * Whether to press \`event.ctrlKey\` at the same time
    +     *
    +     * @default false
    +     */
    +    ctrl?: boolean;
    +    /**
    +     * Whether to press \`event.shiftKey\` at the same time
    +     *
    +     * @default false
    +     */
    +    shift?: boolean;
    +}
    +
    `,3),m=s("li",null,[s("p",null,[n("Default: "),s("code",null,"['s', '/']")])],-1),b=s("p",null,"Details:",-1),C={href:"http://keycode.info/",target:"_blank",rel:"noopener noreferrer"},g=s("p",null,"When hotkeys are pressed, the search box input will be focused.",-1),f=s("p",null,"Set to an empty array to disable hotkeys.",-1),x=a(`

    maxSuggestions

    • Type: number

    • Default: 5

    • Details:

      Specify the maximum number of search results.

    isSearchable

    • Type: (page: Page) => boolean

    • Default: () => true

    • Details:

      A function to determine whether a page should be included in the search index.

      • Return true to include the page.
      • Return false to exclude the page.
    • Example:

    export default {
    +  plugins: [
    +    searchPlugin({
    +      // exclude the homepage
    +      isSearchable: (page) => page.path !== '/',
    +    }),
    +  ],
    +}
    +

    getExtraFields

    • Type: (page: Page) => string[]

    • Default: () => []

    • Details:

      A function to add extra fields to the search index of a page.

      By default, this plugin will use page title and headers as the search index. This option could help you to add more searchable fields.

    • Example:

    export default {
    +  plugins: [
    +    searchPlugin({
    +      // allow searching the \`tags\` frontmatter
    +      getExtraFields: (page) => page.frontmatter.tags ?? [],
    +    }),
    +  ],
    +}
    +

    Styles

    You can customize the style of the search box via CSS variables:

    :root {
    +  --search-bg-color: #ffffff;
    +  --search-accent-color: #3eaf7c;
    +  --search-text-color: #2c3e50;
    +  --search-border-color: #eaecef;
    +  --search-item-text-color: #5d81a5;
    +  --search-item-focus-bg-color: #f3f4f5;
    +  --search-input-width: 8rem;
    +  --search-result-width: 20rem;
    +}
    +

    Components

    • Details:

      This plugin will register a <SearchBox /> component globally, and you can use it without any props.

      Put this component to where you want to place the search box. For example, default theme puts this component to the end of the navbar.

    TIP

    This component is mainly used for theme development. You don't need to use it directly in most cases.

    `,15);function E(k,A){const i=l("NpmBadge"),p=l("RouteLink"),c=l("ExternalLinkIcon");return r(),d("div",null,[u,e(i,{package:"@vuepress/plugin-search"}),h,s("p",null,[n("However, when your site has a large number of pages, the size of search index file would be very large, which could slow down the page loading speed. In this case, we recommend you to use a more professional solution - "),e(p,{to:"/plugins/docsearch.html"},{default:o(()=>[n("docsearch")]),_:1}),n(".")]),y,s("ul",null,[s("li",null,[n("Also see: "),s("ul",null,[s("li",null,[e(p,{to:"/guide/i18n.html"},{default:o(()=>[n("Guide > I18n")]),_:1})])])])]),v,s("ul",null,[m,s("li",null,[b,s("p",null,[n("Specify the "),s("a",C,[n("event.key"),e(c)]),n(" of the hotkeys.")]),g,f])]),x])}const F=t(D,[["render",E],["__file","search.html.vue"]]),w=JSON.parse('{"path":"/plugins/search.html","title":"search","lang":"en-US","frontmatter":{},"headers":[{"level":2,"title":"Usage","slug":"usage","link":"#usage","children":[]},{"level":2,"title":"Local Search Index","slug":"local-search-index","link":"#local-search-index","children":[]},{"level":2,"title":"Options","slug":"options","link":"#options","children":[{"level":3,"title":"locales","slug":"locales","link":"#locales","children":[]},{"level":3,"title":"hotKeys","slug":"hotkeys","link":"#hotkeys","children":[]},{"level":3,"title":"maxSuggestions","slug":"maxsuggestions","link":"#maxsuggestions","children":[]},{"level":3,"title":"isSearchable","slug":"issearchable","link":"#issearchable","children":[]},{"level":3,"title":"getExtraFields","slug":"getextrafields","link":"#getextrafields","children":[]}]},{"level":2,"title":"Styles","slug":"styles","link":"#styles","children":[]},{"level":2,"title":"Components","slug":"components","link":"#components","children":[{"level":3,"title":"SearchBox","slug":"searchbox","link":"#searchbox","children":[]}]}],"git":{"updatedTime":1706605723000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":1}]},"filePathRelative":"plugins/search.md"}');export{F as comp,w as data}; diff --git a/assets/search.html-DIFheM8W.js b/assets/search.html-DIFheM8W.js new file mode 100644 index 0000000000..e30d551e8e --- /dev/null +++ b/assets/search.html-DIFheM8W.js @@ -0,0 +1,75 @@ +import{_ as r,r as e,o as t,c as d,a,b as s,d as n,w as i,e as l}from"./app-DI49vYHA.js";const D={},u=s("h1",{id:"search",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#search"},[s("span",null,"search")])],-1),v=l(`

    为你的文档网站提供本地搜索能力。

    提示

    当你正确配置该插件后,默认主题会把搜索框添加到导航栏。

    该插件不一定能在其他主题中直接使用,因此你应参考主题本身的文档来获取更多信息。

    使用方法

    npm i -D @vuepress/plugin-search@next
    +
    import { searchPlugin } from '@vuepress/plugin-search'
    +
    +export default {
    +  plugins: [
    +    searchPlugin({
    +      // 配置项
    +    }),
    +  ],
    +}
    +

    本地搜索索引

    该插件会根据你的页面,在本地生成搜索索引,然后在用户访问站点时加载搜索索引文件。换句话说,这是一个轻量级的内置搜索能力,不会进行任何外部请求。

    `,7),y=l(`

    配置项

    locales

    • 类型: Record<string, { placeholder: string }>

    • 详情:

      搜索框在不同 locales 下的文字。

      如果没有指定该配置项,它会降级使用默认文字。

    • 示例:

    export default {
    +  plugins: [
    +    searchPlugin({
    +      locales: {
    +        '/': {
    +          placeholder: 'Search',
    +        },
    +        '/zh/': {
    +          placeholder: '搜索',
    +        },
    +      },
    +    }),
    +  ],
    +}
    +
    `,4),h=l(`

    hotKeys

    • 类型: (string | HotKeyOptions)[]
    export interface HotKeyOptions {
    +    /**
    +     * Value of \`event.key\` to trigger the hot key
    +     */
    +    key: string;
    +    /**
    +     * Whether to press \`event.altKey\` at the same time
    +     *
    +     * @default false
    +     */
    +    alt?: boolean;
    +    /**
    +     * Whether to press \`event.ctrlKey\` at the same time
    +     *
    +     * @default false
    +     */
    +    ctrl?: boolean;
    +    /**
    +     * Whether to press \`event.shiftKey\` at the same time
    +     *
    +     * @default false
    +     */
    +    shift?: boolean;
    +}
    +
    `,3),m=s("li",null,[s("p",null,[n("默认值: "),s("code",null,"['s', '/']")])],-1),b=s("p",null,"详情:",-1),C={href:"http://keycode.info/",target:"_blank",rel:"noopener noreferrer"},g=s("p",null,"当按下热键时,搜索框会被聚焦。",-1),E=s("p",null,"将该配置项设为空数组可以禁用热键功能。",-1),x=l(`

    maxSuggestions

    • 类型: number

    • 默认值: 5

    • 详情:

      指定搜索结果的最大条数。

    isSearchable

    • 类型: (page: Page) => boolean

    • 默认值: () => true

    • 详情:

      一个函数,用于判断一个页面是否应该被包含在搜索索引中。

      • 返回 true 来包含该页面。
      • 返回 false 来排除该页面。
    • 示例:

    export default {
    +  plugins: [
    +    searchPlugin({
    +      // 排除首页
    +      isSearchable: (page) => page.path !== '/',
    +    }),
    +  ],
    +}
    +

    getExtraFields

    • 类型: (page: Page) => string[]

    • 默认值: () => []

    • 详情:

      一个函数,用于在页面的搜索索引中添加额外字段。

      默认情况下,该插件会将页面标题和小标题作为搜索索引。该配置项可以帮助你添加更多的可搜索字段。

    • 示例:

    export default {
    +  plugins: [
    +    searchPlugin({
    +      // 允许搜索 Frontmatter 中的 \`tags\`
    +      getExtraFields: (page) => page.frontmatter.tags ?? [],
    +    }),
    +  ],
    +}
    +

    样式

    你可以通过 CSS 变量来自定义搜索框的样式:

    :root {
    +  --search-bg-color: #ffffff;
    +  --search-accent-color: #3eaf7c;
    +  --search-text-color: #2c3e50;
    +  --search-border-color: #eaecef;
    +  --search-item-text-color: #5d81a5;
    +  --search-item-focus-bg-color: #f3f4f5;
    +  --search-input-width: 8rem;
    +  --search-result-width: 20rem;
    +}
    +

    组件

    • 详情:

      该插件会全局注册一个 <SearchBox /> 组件,你可以不传入任何 Props 来使用它。

      将该组件放置在你想要显示搜索框的地方。例如,默认主题将这个组件放在了导航栏的末尾。

    提示

    该组件主要用于主题开发。在大多数情况下你不需要直接使用该组件。

    `,15);function f(k,_){const o=e("NpmBadge"),p=e("RouteLink"),c=e("ExternalLinkIcon");return t(),d("div",null,[u,a(o,{package:"@vuepress/plugin-search"}),v,s("p",null,[n("然而,当你的站点包含大量页面时,搜索索引文件也会变得非常大,它可能会拖慢你的页面加载速度。在这种情况下,我们建议你使用更成熟的解决方案 - "),a(p,{to:"/zh/plugins/docsearch.html"},{default:i(()=>[n("docsearch")]),_:1}),n(" 。")]),y,s("ul",null,[s("li",null,[n("参考: "),s("ul",null,[s("li",null,[a(p,{to:"/guide/i18n.html"},{default:i(()=>[n("指南 > 多语言支持")]),_:1})])])])]),h,s("ul",null,[m,s("li",null,[b,s("p",null,[n("指定热键的 "),s("a",C,[n("event.key"),a(c)]),n(" 。")]),g,E])]),x])}const F=r(D,[["render",f],["__file","search.html.vue"]]),B=JSON.parse('{"path":"/zh/plugins/search.html","title":"search","lang":"zh-CN","frontmatter":{},"headers":[{"level":2,"title":"使用方法","slug":"使用方法","link":"#使用方法","children":[]},{"level":2,"title":"本地搜索索引","slug":"本地搜索索引","link":"#本地搜索索引","children":[]},{"level":2,"title":"配置项","slug":"配置项","link":"#配置项","children":[{"level":3,"title":"locales","slug":"locales","link":"#locales","children":[]},{"level":3,"title":"hotKeys","slug":"hotkeys","link":"#hotkeys","children":[]},{"level":3,"title":"maxSuggestions","slug":"maxsuggestions","link":"#maxsuggestions","children":[]},{"level":3,"title":"isSearchable","slug":"issearchable","link":"#issearchable","children":[]},{"level":3,"title":"getExtraFields","slug":"getextrafields","link":"#getextrafields","children":[]}]},{"level":2,"title":"样式","slug":"样式","link":"#样式","children":[]},{"level":2,"title":"组件","slug":"组件","link":"#组件","children":[{"level":3,"title":"SearchBox","slug":"searchbox","link":"#searchbox","children":[]}]}],"git":{"updatedTime":1706605723000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":1}]},"filePathRelative":"zh/plugins/search.md"}');export{F as comp,B as data}; diff --git a/assets/shared.html-BZlae7Rh.js b/assets/shared.html-BZlae7Rh.js new file mode 100644 index 0000000000..efe8376201 --- /dev/null +++ b/assets/shared.html-BZlae7Rh.js @@ -0,0 +1,110 @@ +import{_ as s,o as n,c as a,e}from"./app-DI49vYHA.js";const l={},o=e(`

    Shared Methods

    The following functions are available on both Node.js and Client.

    Encode/decode and zip/unzip data.

    This is useful in markdown plugins when you want to encode string content and pass it to the component through props.

    You may simply achieve this with encodeURIComponent and decodeURIComponent, but it can be very large if the content contains lots of special characters.

    So we provide encodeData and decodeData to zip and encode content.

    export const encodeData: (
    +  data: string,
    +  level: DeflateOptions['level'] = 6,
    +) => string
    +
    +export const decodeData: (compressed: string) => string
    +
    const content = \`
    +{
    +  "type": "bar",
    +  "data": {
    +    "labels": ["Red", "Blue", "Yellow", "Green", "Purple", "Orange"],
    +    "datasets": [
    +      {
    +        "label": "# of Votes",
    +        "data": [12, 19, 3, 5, 2, 3],
    +        "backgroundColor": [
    +          "rgba(255, 99, 132, 0.2)",
    +          "rgba(54, 162, 235, 0.2)",
    +          "rgba(255, 206, 86, 0.2)",
    +          "rgba(75, 192, 192, 0.2)",
    +          "rgba(153, 102, 255, 0.2)",
    +          "rgba(255, 159, 64, 0.2)"
    +        ],
    +        "borderColor": [
    +          "rgba(255, 99, 132, 1)",
    +          "rgba(54, 162, 235, 1)",
    +          "rgba(255, 206, 86, 1)",
    +          "rgba(75, 192, 192, 1)",
    +          "rgba(153, 102, 255, 1)",
    +          "rgba(255, 159, 64, 1)"
    +        ],
    +        "borderWidth": 1
    +      }
    +    ]
    +  },
    +  "options": {
    +    "scales": {
    +      "y": {
    +        "beginAtZero": true
    +      }
    +    }
    +  }
    +}
    +\`
    +
    +const prop = encodeData(content) // "eJyNUsFOwzAMve8rrHABKZqWlg5WxAE4cARxAMHEIV1NmQhNlaaCCe3fcdKtW0sLWGpjxy/v+UV512mlcIyfhTa2hHP4GgHYVYExsEQaxqlMpZWxbwAomaAqY5izO0wZB3apKnTrIyqlP1x2bRBzl9xWplC+eWNkniF7dmw1X4nWsfgaNtwNP2kfgH6Be22x9CPUUQ8yFwEHMeMQcog4UBFuiF0kcvGWGV3l6ZVW2uw0XDCTJfIwiOjYjAhESIcn4+BoT2MLio6pP6V+EBJ6AOSZgsmUwyl9A6ATwoiZn3lYTkTkRkycnuP8TU9ENPqUxuuA9i9BmxTNPy9A/G2/F9I23wtpW++FdIwPKzW2W5Afph+WqX2NQWz313XicT7XhV3qnB5f/ejKhVTYVACrXUqUmC3zC/uERsdgTYUdVr/Qb302+gZxe7S/"
    +
    +decodeData(prop) // will be the original content
    +
    +// if you use \`encodeURIComponent\`, it will be much longer
    +encodeURIComponent(content) // '%0A%7B%0A%20%20%22type%22%3A%20%22bar%22%2C%0A%20%20%22data%22%3A%20%7B%0A%20%20%20%20%22labels%22%3A%20%5B%22Red%22%2C%20%22Blue%22%2C%20%22Yellow%22%2C%20%22Green%22%2C%20%22Purple%22%2C%20%22Orange%22%5D%2C%0A%20%20%20%20%22datasets%22%3A%20%5B%0A%20%20%20%20%20%20%7B%0A%20%20%20%20%20%20%20%20%22label%22%3A%20%22%23%20of%20Votes%22%2C%0A%20%20%20%20%20%20%20%20%22data%22%3A%20%5B12%2C%2019%2C%203%2C%205%2C%202%2C%203%5D%2C%0A%20%20%20%20%20%20%20%20%22backgroundColor%22%3A%20%5B%0A%20%20%20%20%20%20%20%20%20%20%22rgba(255%2C%2099%2C%20132%2C%200.2)%22%2C%0A%20%20%20%20%20%20%20%20%20%20%22rgba(54%2C%20162%2C%20235%2C%200.2)%22%2C%0A%20%20%20%20%20%20%20%20%20%20%22rgba(255%2C%20206%2C%2086%2C%200.2)%22%2C%0A%20%20%20%20%20%20%20%20%20%20%22rgba(75%2C%20192%2C%20192%2C%200.2)%22%2C%0A%20%20%20%20%20%20%20%20%20%20%22rgba(153%2C%20102%2C%20255%2C%200.2)%22%2C%0A%20%20%20%20%20%20%20%20%20%20%22rgba(255%2C%20159%2C%2064%2C%200.2)%22%0A%20%20%20%20%20%20%20%20%5D%2C%0A%20%20%20%20%20%20%20%20%22borderColor%22%3A%20%5B%0A%20%20%20%20%20%20%20%20%20%20%22rgba(255%2C%2099%2C%20132%2C%201)%22%2C%0A%20%20%20%20%20%20%20%20%20%20%22rgba(54%2C%20162%2C%20235%2C%201)%22%2C%0A%20%20%20%20%20%20%20%20%20%20%22rgba(255%2C%20206%2C%2086%2C%201)%22%2C%0A%20%20%20%20%20%20%20%20%20%20%22rgba(75%2C%20192%2C%20192%2C%201)%22%2C%0A%20%20%20%20%20%20%20%20%20%20%22rgba(153%2C%20102%2C%20255%2C%201)%22%2C%0A%20%20%20%20%20%20%20%20%20%20%22rgba(255%2C%20159%2C%2064%2C%201)%22%0A%20%20%20%20%20%20%20%20%5D%2C%0A%20%20%20%20%20%20%20%20%22borderWidth%22%3A%201%0A%20%20%20%20%20%20%7D%0A%20%20%20%20%5D%0A%20%20%7D%2C%0A%20%20%22options%22%3A%20%7B%0A%20%20%20%20%22scales%22%3A%20%7B%0A%20%20%20%20%20%20%22y%22%3A%20%7B%0A%20%20%20%20%20%20%20%20%22beginAtZero%22%3A%20true%0A%20%20%20%20%20%20%7D%0A%20%20%20%20%7D%0A%20%20%7D%0A%7D%0A'
    +

    Type Helper

    • isDef(x): Check if x is defined.
    • isBoolean(x): Check if x is a boolean.
    • isString(x): Check if x is a string.
    • isNumber(x): Check if x is a number.
    • isPlainObject(x): Check if x is a plain object.
    • isArray(x): Check if x is an array.
    • isFunction(x): Check if x is a function.
    • isRegExp(x): Check if x is a regular expression.
    • startsWith(a, b): Check if string a starts with string b.
    • endsWith(a, b): Check if string a ends with string b.

    Return false if a is not a string.

    对象相关

    • keys(x): Return an array of keys of object x.

    • values(x): Return an array of values of object x.

    • entries(x): Convert object x to an array of key-value pairs.

    • fromEntries(x): Convert an array of key-value pairs x to an object.

    • deepAssign(x, y, ...): A deep version of Object.assign.

      Example
      // or @vuepress/helper/client
      +import { deepAssign } from '@vuepress/helper'
      +
      +const defaultOptions = {
      +  optionA: {
      +    optionA1: 'defaultOptionA1',
      +    optionA2: 'defaultOptionA2',
      +    optionA3: 'defaultOptionA3',
      +  },
      +  optionB: true,
      +  optionC: 'optionC',
      +}
      +
      +const userOptions = {
      +  optionA: {
      +    optionA1: 'optionA1',
      +    optionA2: 'optionA2',
      +  },
      +  optionB: false,
      +}
      +
      +deepAssign(defaultOptions, userOptions)
      +// {
      +//   optionA: {
      +//     optionA1: "optionA1",
      +//     optionA2: "optionA2",
      +//     optionA3: "defaultOptionA3",
      +//   },
      +//   optionB: false,
      +//   optionC: "optionC",
      +// }
      +
    • getDate(x): Convert input x to a date. It can support Date, timestamp, and date string. The support degree of date string depends on the Date.parse support degree of the environment. Return null when it cannot be converted to a date.

      Example
      getDate('2021-01-01') // a Date object represents 2021-01-01
      +getDate(1609459200000) // a Date object represents 2021-01-01
      +getDate('2021-01-01T00:00:00.000Z') // a Date object represents 2021-01-01
      +getDate('2021/01/01') // a Date object represents 2021-01-01 (might be null in some browsers)
      +getDate('invalid date') // null
      +getDate(undefined) // null
      +getDate(-32) // null
      +
    • dateSorter: Sort the values that can be converted to dates from new to old, and the values that cannot be converted to dates will be at the end.

      Example
      const arr = [
      +  '2020-01-01',
      +  1609459200000,
      +  '2022-01-01T00:00:00.000Z',
      +  '2023/01/01',
      +  'invalid date',
      +  undefined,
      +  -32,
      +]
      +
      +arr.sort(dateSorter)
      +// [
      +//   '2023/01/01',
      +//   '2022-01-01T00:00:00.000Z',
      +//   1609459200000,
      +//   '2020-01-01',
      +//   'invalid date',
      +//   undefined,
      +//   -32,
      +// ]
      +
    • isLinkHttp(x): Check if x is a valid HTTP URL.
    • isLinkWithProtocol(x): Check if x is a valid URL with protocol.
    • isLinkExternal(x): Check if x is a valid external URL.
    • isLinkAbsolute(x): Check if x is a valid absolute URL.
    • ensureEndingSlash(x): Ensure x ends with a slash.
    • ensureLeadingSlash(x): Ensure x starts with a slash.
    • removeEndingSlash(x): Ensure x does not end with a slash.
    • removeLeadingSlash(x): Ensure x does not start with a slash.
    `,20),p=[o];function i(c,t){return n(),a("div",null,p)}const d=s(l,[["render",i],["__file","shared.html.vue"]]),u=JSON.parse('{"path":"/tools/helper/shared.html","title":"Shared Methods","lang":"en-US","frontmatter":{},"headers":[{"level":2,"title":"Data Related","slug":"data-related","link":"#data-related","children":[]},{"level":2,"title":"Type Helper","slug":"type-helper","link":"#type-helper","children":[]},{"level":2,"title":"String Related","slug":"string-related","link":"#string-related","children":[]},{"level":2,"title":"对象相关","slug":"对象相关","link":"#对象相关","children":[]},{"level":2,"title":"Date Related","slug":"date-related","link":"#date-related","children":[]},{"level":2,"title":"Link Related","slug":"link-related","link":"#link-related","children":[]}],"git":{"updatedTime":1706960075000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":1}]},"filePathRelative":"tools/helper/shared.md"}');export{d as comp,u as data}; diff --git a/assets/shared.html-CRDbFVNx.js b/assets/shared.html-CRDbFVNx.js new file mode 100644 index 0000000000..d29a10506f --- /dev/null +++ b/assets/shared.html-CRDbFVNx.js @@ -0,0 +1,110 @@ +import{_ as s,o as n,c as a,e as l}from"./app-DI49vYHA.js";const e={},o=l(`

    共享方法

    以下函数在 Node.js 和客户端上均可用。

    数据相关

    此方法在 MarkdownIt 插件中很有用。有些时候你可能需要在 Markdown 插件中生成组件,并将复杂的数据写入到组件属性中,一个通常做法是使用 JSON.stringify + encodeURIComponent,并在客户端 decodeURIComponent + JSON.parse。但如果内容包含很多特殊字符,转换结果会很长。

    所以我们提供 encodeDatadecodeData 来压缩和编码内容。

    export const encodeData: (
    +  data: string,
    +  level: DeflateOptions['level'] = 6,
    +) => string
    +
    +export const decodeData: (compressed: string) => string
    +
    const content = \`
    +{
    +  "type": "bar",
    +  "data": {
    +    "labels": ["Red", "Blue", "Yellow", "Green", "Purple", "Orange"],
    +    "datasets": [
    +      {
    +        "label": "# of Votes",
    +        "data": [12, 19, 3, 5, 2, 3],
    +        "backgroundColor": [
    +          "rgba(255, 99, 132, 0.2)",
    +          "rgba(54, 162, 235, 0.2)",
    +          "rgba(255, 206, 86, 0.2)",
    +          "rgba(75, 192, 192, 0.2)",
    +          "rgba(153, 102, 255, 0.2)",
    +          "rgba(255, 159, 64, 0.2)"
    +        ],
    +        "borderColor": [
    +          "rgba(255, 99, 132, 1)",
    +          "rgba(54, 162, 235, 1)",
    +          "rgba(255, 206, 86, 1)",
    +          "rgba(75, 192, 192, 1)",
    +          "rgba(153, 102, 255, 1)",
    +          "rgba(255, 159, 64, 1)"
    +        ],
    +        "borderWidth": 1
    +      }
    +    ]
    +  },
    +  "options": {
    +    "scales": {
    +      "y": {
    +        "beginAtZero": true
    +      }
    +    }
    +  }
    +}
    +\`
    +
    +const prop = encodeData(content) // "eJyNUsFOwzAMve8rrHABKZqWlg5WxAE4cARxAMHEIV1NmQhNlaaCCe3fcdKtW0sLWGpjxy/v+UV512mlcIyfhTa2hHP4GgHYVYExsEQaxqlMpZWxbwAomaAqY5izO0wZB3apKnTrIyqlP1x2bRBzl9xWplC+eWNkniF7dmw1X4nWsfgaNtwNP2kfgH6Be22x9CPUUQ8yFwEHMeMQcog4UBFuiF0kcvGWGV3l6ZVW2uw0XDCTJfIwiOjYjAhESIcn4+BoT2MLio6pP6V+EBJ6AOSZgsmUwyl9A6ATwoiZn3lYTkTkRkycnuP8TU9ENPqUxuuA9i9BmxTNPy9A/G2/F9I23wtpW++FdIwPKzW2W5Afph+WqX2NQWz313XicT7XhV3qnB5f/ejKhVTYVACrXUqUmC3zC/uERsdgTYUdVr/Qb302+gZxe7S/"
    +
    +decodeData(prop) // will be the original content
    +
    +// if you use \`encodeURIComponent\`, it will be much longer
    +encodeURIComponent(content) // '%0A%7B%0A%20%20%22type%22%3A%20%22bar%22%2C%0A%20%20%22data%22%3A%20%7B%0A%20%20%20%20%22labels%22%3A%20%5B%22Red%22%2C%20%22Blue%22%2C%20%22Yellow%22%2C%20%22Green%22%2C%20%22Purple%22%2C%20%22Orange%22%5D%2C%0A%20%20%20%20%22datasets%22%3A%20%5B%0A%20%20%20%20%20%20%7B%0A%20%20%20%20%20%20%20%20%22label%22%3A%20%22%23%20of%20Votes%22%2C%0A%20%20%20%20%20%20%20%20%22data%22%3A%20%5B12%2C%2019%2C%203%2C%205%2C%202%2C%203%5D%2C%0A%20%20%20%20%20%20%20%20%22backgroundColor%22%3A%20%5B%0A%20%20%20%20%20%20%20%20%20%20%22rgba(255%2C%2099%2C%20132%2C%200.2)%22%2C%0A%20%20%20%20%20%20%20%20%20%20%22rgba(54%2C%20162%2C%20235%2C%200.2)%22%2C%0A%20%20%20%20%20%20%20%20%20%20%22rgba(255%2C%20206%2C%2086%2C%200.2)%22%2C%0A%20%20%20%20%20%20%20%20%20%20%22rgba(75%2C%20192%2C%20192%2C%200.2)%22%2C%0A%20%20%20%20%20%20%20%20%20%20%22rgba(153%2C%20102%2C%20255%2C%200.2)%22%2C%0A%20%20%20%20%20%20%20%20%20%20%22rgba(255%2C%20159%2C%2064%2C%200.2)%22%0A%20%20%20%20%20%20%20%20%5D%2C%0A%20%20%20%20%20%20%20%20%22borderColor%22%3A%20%5B%0A%20%20%20%20%20%20%20%20%20%20%22rgba(255%2C%2099%2C%20132%2C%201)%22%2C%0A%20%20%20%20%20%20%20%20%20%20%22rgba(54%2C%20162%2C%20235%2C%201)%22%2C%0A%20%20%20%20%20%20%20%20%20%20%22rgba(255%2C%20206%2C%2086%2C%201)%22%2C%0A%20%20%20%20%20%20%20%20%20%20%22rgba(75%2C%20192%2C%20192%2C%201)%22%2C%0A%20%20%20%20%20%20%20%20%20%20%22rgba(153%2C%20102%2C%20255%2C%201)%22%2C%0A%20%20%20%20%20%20%20%20%20%20%22rgba(255%2C%20159%2C%2064%2C%201)%22%0A%20%20%20%20%20%20%20%20%5D%2C%0A%20%20%20%20%20%20%20%20%22borderWidth%22%3A%201%0A%20%20%20%20%20%20%7D%0A%20%20%20%20%5D%0A%20%20%7D%2C%0A%20%20%22options%22%3A%20%7B%0A%20%20%20%20%22scales%22%3A%20%7B%0A%20%20%20%20%20%20%22y%22%3A%20%7B%0A%20%20%20%20%20%20%20%20%22beginAtZero%22%3A%20true%0A%20%20%20%20%20%20%7D%0A%20%20%20%20%7D%0A%20%20%7D%0A%7D%0A'
    +

    类型助手

    • isDef(x): 判断 x 是否定义。
    • isBoolean(x): 判断 x 是否为布尔值。
    • isString(x): 判断 x 是否为字符串。
    • isNumber(x): 判断 x 是否为数字。
    • isPlainObject(x): 判断值是否为纯对象。
    • isArray(x): 判断 x 是否为数组
    • isFunction(x): 判断 x 是否为函数。
    • isRegExp(x): 判断 x 是否为正则表达式

    字符串相关

    • startsWith(a, b): 判断字符串 a 是否以指定字符串 b 开头
    • endsWith(a, b): 判断字符串 a 是否以指定字符串 b 结尾

    当 a 不是字符串时返回 false

    对象相关

    • keys(x): 以数组形式返回对象 x 的键

    • values(x): 以数组形式返回对象 x 的值

    • entries(x): 将对象 x 转换为键值对数组。

    • fromEntries(x): 将键值对数组 x 转换为对象。

    • deepAssign(x, y, ...): Object.assign 的深度版本。

      示例
      // or @vuepress/helper/client
      +import { deepAssign } from '@vuepress/helper'
      +
      +const defaultOptions = {
      +  optionA: {
      +    optionA1: 'defaultOptionA1',
      +    optionA2: 'defaultOptionA2',
      +    optionA3: 'defaultOptionA3',
      +  },
      +  optionB: true,
      +  optionC: 'optionC',
      +}
      +
      +const userOptions = {
      +  optionA: {
      +    optionA1: 'optionA1',
      +    optionA2: 'optionA2',
      +  },
      +  optionB: false,
      +}
      +
      +deepAssign(defaultOptions, userOptions)
      +// {
      +//   optionA: {
      +//     optionA1: "optionA1",
      +//     optionA2: "optionA2",
      +//     optionA3: "defaultOptionA3",
      +//   },
      +//   optionB: false,
      +//   optionC: "optionC",
      +// }
      +

    日期相关

    • getDate(x): 将输入 x 转换为日期,可以支持 Date,时间戳,日期字符串。日期字符串的支持度以环境的 Date.parse 支持度为准。当不能转换为日期时返回 null

      示例
      getDate('2021-01-01') // a Date object represents 2021-01-01
      +getDate(1609459200000) // a Date object represents 2021-01-01
      +getDate('2021-01-01T00:00:00.000Z') // a Date object represents 2021-01-01
      +getDate('2021/01/01') // a Date object represents 2021-01-01 (might be null in some browsers)
      +getDate('invalid date') // null
      +getDate(undefined) // null
      +getDate(-32) // null
      +
    • dateSorter: 将可转换为日期的值从新到旧排序,不能转换为日期的值会在最后。

      示例
      const arr = [
      +  '2020-01-01',
      +  1609459200000,
      +  '2022-01-01T00:00:00.000Z',
      +  '2023/01/01',
      +  'invalid date',
      +  undefined,
      +  -32,
      +]
      +
      +arr.sort(dateSorter)
      +// [
      +//   '2023/01/01',
      +//   '2022-01-01T00:00:00.000Z',
      +//   1609459200000,
      +//   '2020-01-01',
      +//   'invalid date',
      +//   undefined,
      +//   -32,
      +// ]
      +

    链接相关

    • isLinkHttp(x): x 是否是有效的 HTTP URL。
    • isLinkWithProtocol(x): x 是否是有效的带有协议的 URL。
    • isLinkExternal(x): x 是否是有效的外部 URL。
    • isLinkAbsolute(x): x 是否是有效的绝对 URL。
    • ensureEndingSlash(x): 确保 x 以斜杠结尾。
    • ensureLeadingSlash(x): 确保 x 以斜杠开头。
    • removeEndingSlash(x): 确保 x 不以斜杠结尾。
    • removeLeadingSlash(x): 确保 x 不以斜杠开头。
    `,18),p=[o];function i(c,t){return n(),a("div",null,p)}const d=s(e,[["render",i],["__file","shared.html.vue"]]),D=JSON.parse('{"path":"/zh/tools/helper/shared.html","title":"共享方法","lang":"zh-CN","frontmatter":{},"headers":[{"level":2,"title":"数据相关","slug":"数据相关","link":"#数据相关","children":[]},{"level":2,"title":"类型助手","slug":"类型助手","link":"#类型助手","children":[]},{"level":2,"title":"字符串相关","slug":"字符串相关","link":"#字符串相关","children":[]},{"level":2,"title":"对象相关","slug":"对象相关","link":"#对象相关","children":[]},{"level":2,"title":"日期相关","slug":"日期相关","link":"#日期相关","children":[]},{"level":2,"title":"链接相关","slug":"链接相关","link":"#链接相关","children":[]}],"git":{"updatedTime":1706960075000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":1}]},"filePathRelative":"zh/tools/helper/shared.md"}');export{d as comp,D as data}; diff --git a/assets/shiki.html-Br5bIad7.js b/assets/shiki.html-Br5bIad7.js new file mode 100644 index 0000000000..b663db11aa --- /dev/null +++ b/assets/shiki.html-Br5bIad7.js @@ -0,0 +1,12 @@ +import{_ as t,r as a,o as p,c as r,a as l,b as e,d as s,e as i}from"./app-DI49vYHA.js";const c={},h=e("h1",{id:"shiki",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#shiki"},[e("span",null,"shiki")])],-1),d={href:"https://shiki.style/",target:"_blank",rel:"noopener noreferrer"},u={class:"custom-container tip"},m=e("p",{class:"custom-container-title"},"TIP",-1),g={href:"https://shiki.matsu.io/",target:"_blank",rel:"noopener noreferrer"},D={href:"https://prismjs.com/",target:"_blank",rel:"noopener noreferrer"},_=e("p",null,[s("You could consider disabling this plugin in "),e("code",null,"dev"),s(" mode to get better development experience.")],-1),k=i(`

    Usage

    npm i -D @vuepress/plugin-shiki@next
    +
    import { shikiPlugin } from '@vuepress/plugin-shiki'
    +
    +export default {
    +  plugins: [
    +    shikiPlugin({
    +      // options
    +      langs: ['ts', 'json', 'vue', 'md', 'bash', 'diff'],
    +    }),
    +  ],
    +}
    +

    Options

    langs

    `,5),y=e("li",null,[e("p",null,[s("Type: "),e("code",null,"ShikiLang[]")])],-1),b=e("li",null,[e("p",null,"Details:"),e("p",null,"Languages of code blocks to be parsed by Shiki."),e("p",null,[s("This option will be forwarded to "),e("code",null,"getHighlighter()"),s(" method of Shiki.")]),e("p",null,"You'd better provide the languages list you are using explicitly, otherwise Shiki will load all languages and can affect performance.")],-1),v=e("p",null,"Also see:",-1),f={href:"https://shiki.style/languages",target:"_blank",rel:"noopener noreferrer"},C=e("h3",{id:"theme",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#theme"},[e("span",null,"theme")])],-1),x=i("
  • Type: ShikiTheme

  • Default: 'nord'

  • Details:

    Theme of Shiki.

    This option will be forwarded to codeToHtml() method of Shiki.

  • ",3),E=e("p",null,"Also see:",-1),S={href:"https://shiki.style/themes",target:"_blank",rel:"noopener noreferrer"},T=e("h3",{id:"themes",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#themes"},[e("span",null,"themes")])],-1),w=e("li",null,[e("p",null,[s("Type: "),e("code",null,"Record<'dark' | 'light', ShikiTheme>")])],-1),A=e("li",null,[e("p",null,"Details:"),e("p",null,"Dark / Light Dual themes of Shiki."),e("p",null,[s("This option will be forwarded to "),e("code",null,"codeToHtml()"),s(" method of Shiki.")])],-1),N=e("p",null,"Also see:",-1),L={href:"https://shiki.style/guide/dual-themes",target:"_blank",rel:"noopener noreferrer"};function B(P,V){const o=a("NpmBadge"),n=a("ExternalLinkIcon");return p(),r("div",null,[h,l(o,{package:"@vuepress/plugin-shiki"}),e("p",null,[s("This plugin will enable syntax highlighting for markdown code fence with "),e("a",d,[s("Shiki"),l(n)]),s(".")]),e("div",u,[m,e("p",null,[e("a",g,[s("Shiki"),l(n)]),s(" is the syntax highlighter being used by VSCode. It has higher fidelity, but it could be slower than "),e("a",D,[s("Prism.js"),l(n)]),s(", especially when you have a lot of code blocks.")]),_]),k,e("ul",null,[y,b,e("li",null,[v,e("ul",null,[e("li",null,[e("a",f,[s("Shiki > Languages"),l(n)])])])])]),C,e("ul",null,[x,e("li",null,[E,e("ul",null,[e("li",null,[e("a",S,[s("Shiki > Themes"),l(n)])])])])]),T,e("ul",null,[w,A,e("li",null,[N,e("ul",null,[e("li",null,[e("a",L,[s("Shiki > Dual Themes"),l(n)])])])])])])}const I=t(c,[["render",B],["__file","shiki.html.vue"]]),j=JSON.parse('{"path":"/plugins/shiki.html","title":"shiki","lang":"en-US","frontmatter":{},"headers":[{"level":2,"title":"Usage","slug":"usage","link":"#usage","children":[]},{"level":2,"title":"Options","slug":"options","link":"#options","children":[{"level":3,"title":"langs","slug":"langs","link":"#langs","children":[]},{"level":3,"title":"theme","slug":"theme","link":"#theme","children":[]},{"level":3,"title":"themes","slug":"themes","link":"#themes","children":[]}]}],"git":{"updatedTime":1707215812000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":3}]},"filePathRelative":"plugins/shiki.md"}');export{I as comp,j as data}; diff --git a/assets/shiki.html-CIFvEyyj.js b/assets/shiki.html-CIFvEyyj.js new file mode 100644 index 0000000000..ba7b906297 --- /dev/null +++ b/assets/shiki.html-CIFvEyyj.js @@ -0,0 +1,12 @@ +import{_ as t,r as a,o as r,c as p,a as l,b as s,d as e,e as i}from"./app-DI49vYHA.js";const c={},h=s("h1",{id:"shiki",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#shiki"},[s("span",null,"shiki")])],-1),d={href:"https://shiki.matsu.io/",target:"_blank",rel:"noopener noreferrer"},u={class:"custom-container tip"},m=s("p",{class:"custom-container-title"},"提示",-1),_={href:"https://shiki.matsu.io/",target:"_blank",rel:"noopener noreferrer"},D={href:"https://prismjs.com/",target:"_blank",rel:"noopener noreferrer"},k=s("p",null,[e("你可以考虑在 "),s("code",null,"dev"),e(" 模式下禁用该插件来获取更好的开发体验。")],-1),g=i(`

    使用方法

    npm i -D @vuepress/plugin-shiki@next
    +
    import { shikiPlugin } from '@vuepress/plugin-shiki'
    +
    +export default {
    +  plugins: [
    +    shikiPlugin({
    +      // 配置项
    +      langs: ['ts', 'json', 'vue', 'md', 'bash', 'diff'],
    +    }),
    +  ],
    +}
    +

    配置项

    langs

    `,5),v=s("li",null,[s("p",null,[e("类型: "),s("code",null,"ShikiLang[]")])],-1),y=s("li",null,[s("p",null,"详情:"),s("p",null,"Shiki 要解析的代码块的语言。"),s("p",null,[e("该配置项会被传递到 Shiki 的 "),s("code",null,"getHighlighter()"),e(" 方法中。")]),s("p",null,"你最好明确传入所有你使用的语言列表,否则 Shiki 会加载所有语言,并可能影响性能。")],-1),b=s("p",null,"参考:",-1),f={href:"https://shiki.style/languages",target:"_blank",rel:"noopener noreferrer"},C=s("h3",{id:"theme",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#theme"},[s("span",null,"theme")])],-1),E=i("
  • 类型: ShikiTheme

  • 默认值: 'nord'

  • 详情:

    Shiki 的主题。

    该配置项会被传递到 Shiki 的 codeToHtml() 方法中。

  • ",3),x=s("p",null,"参考:",-1),S={href:"https://shiki.style/themes",target:"_blank",rel:"noopener noreferrer"},N=s("h3",{id:"themes",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#themes"},[s("span",null,"themes")])],-1),T=s("li",null,[s("p",null,[e("类型:"),s("code",null,"Record<'dark' | 'light', ShikiTheme>")])],-1),A=s("li",null,[s("p",null,"详情:"),s("p",null,"Shiki 的暗黑和明亮模式双主题。"),s("p",null,[e("该配置项会被传递到 Shiki 的 "),s("code",null,"codeToHtml()"),e(" 方法中。")])],-1),B=s("p",null,"参考:",-1),V={href:"https://shiki.style/guide/dual-themes",target:"_blank",rel:"noopener noreferrer"};function H(L,P){const o=a("NpmBadge"),n=a("ExternalLinkIcon");return r(),p("div",null,[h,l(o,{package:"@vuepress/plugin-shiki"}),s("p",null,[e("该插件使用 "),s("a",d,[e("Shiki"),l(n)]),e(" 来为 Markdown 代码块启用代码高亮。")]),s("div",u,[m,s("p",null,[s("a",_,[e("Shiki"),l(n)]),e(" 是 VSCode 正在使用的代码高亮器。它具有更高的保真度,但可能会比 "),s("a",D,[e("Prism.js"),l(n)]),e(" 要慢一些,特别是在有大量代码块需要处理的时候。")]),k]),g,s("ul",null,[v,y,s("li",null,[b,s("ul",null,[s("li",null,[s("a",f,[e("shiki > Languages"),l(n)])])])])]),C,s("ul",null,[E,s("li",null,[x,s("ul",null,[s("li",null,[s("a",S,[e("Shiki > Themes"),l(n)])])])])]),N,s("ul",null,[T,A,s("li",null,[B,s("ul",null,[s("li",null,[s("a",V,[e("Shiki > Dual Themes"),l(n)])])])])])])}const z=t(c,[["render",H],["__file","shiki.html.vue"]]),F=JSON.parse('{"path":"/zh/plugins/shiki.html","title":"shiki","lang":"zh-CN","frontmatter":{},"headers":[{"level":2,"title":"使用方法","slug":"使用方法","link":"#使用方法","children":[]},{"level":2,"title":"配置项","slug":"配置项","link":"#配置项","children":[{"level":3,"title":"langs","slug":"langs","link":"#langs","children":[]},{"level":3,"title":"theme","slug":"theme","link":"#theme","children":[]},{"level":3,"title":"themes","slug":"themes","link":"#themes","children":[]}]}],"git":{"updatedTime":1707215812000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":3}]},"filePathRelative":"zh/plugins/shiki.md"}');export{z as comp,F as data}; diff --git a/assets/style-BPe9ZiPR.css b/assets/style-BPe9ZiPR.css new file mode 100644 index 0000000000..fe1f594128 --- /dev/null +++ b/assets/style-BPe9ZiPR.css @@ -0,0 +1 @@ +@charset "UTF-8";.vp-back-to-top-button{position:fixed!important;bottom:4rem;inset-inline-end:1rem;z-index:100;width:3rem;height:3rem;padding:.5rem;border-width:0;border-radius:50%;background:var(--back-to-top-bg-color);color:var(--back-to-top-color);box-shadow:2px 2px 10px 4px var(--back-to-top-shadow);cursor:pointer}@media (max-width: 959px){.vp-back-to-top-button{width:2.5rem;height:2.5rem}}@media print{.vp-back-to-top-button{display:none}}.vp-back-to-top-button:hover{color:var(--back-to-top-color-hover)}.vp-back-to-top-button .back-to-top-icon{overflow:hidden;width:100%;height:100%;background:currentcolor;border-radius:50%;-webkit-mask-image:var(--back-to-top-icon);mask-image:var(--back-to-top-icon);-webkit-mask-position:50%;mask-position:50%;-webkit-mask-size:cover;mask-size:cover}.vp-scroll-progress{position:absolute;right:-2px;bottom:-2px;width:calc(100% + 4px);height:calc(100% + 4px)}.vp-scroll-progress svg{width:100%;height:100%}.vp-scroll-progress circle{opacity:.9;fill:none;stroke:currentColor;transform:rotate(-90deg);transform-origin:50% 50%;r:22;stroke-dasharray:0% 314.1593%;stroke-width:3px}@media (max-width: 959px){.vp-scroll-progress circle{r:18}}.back-to-top-enter-active,.back-to-top-leave-active{transition:opacity .3s}.back-to-top-enter-from,.back-to-top-leave-to{opacity:0}:root{--back-to-top-z-index: 5;--back-to-top-icon: url("data:image/svg+xml,%3csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%201024%201024'%3e%3cpath%20d='M512%20843.2c-36.2%200-66.4-13.6-85.8-21.8-10.8-4.6-22.6%203.6-21.8%2015.2l7%20102c.4%206.2%207.6%209.4%2012.6%205.6l29-22c3.6-2.8%209-1.8%2011.4%202l41%2064.2c3%204.8%2010.2%204.8%2013.2%200l41-64.2c2.4-3.8%207.8-4.8%2011.4-2l29%2022c5%203.8%2012.2.6%2012.6-5.6l7-102c.8-11.6-11-20-21.8-15.2-19.6%208.2-49.6%2021.8-85.8%2021.8'/%3e%3cpath%20d='m795.4%20586.2-96-98.2C699.4%20172%20513%2032%20513%2032S324.8%20172%20324.8%20488l-96%2098.2c-3.6%203.6-5.2%209-4.4%2014.2L261.2%20824c1.8%2011.4%2014.2%2017%2023.6%2010.8L419%20744s41.4%2040%2094.2%2040%2092.2-40%2092.2-40l134.2%2090.8c9.2%206.2%2021.6.6%2023.6-10.8l37-223.8c.4-5.2-1.2-10.4-4.8-14M513%20384c-34%200-61.4-28.6-61.4-64s27.6-64%2061.4-64c34%200%2061.4%2028.6%2061.4%2064S547%20384%20513%20384'/%3e%3c/svg%3e");--back-to-top-bg-color: #fff;--back-to-top-color: #3eaf7c;--back-to-top-color-hover: #71cda3;--back-to-top-shadow: rgb(0 0 0 / 20%)}div[class*=language-]:hover:before{display:none}div[class*=language-]:hover .vp-copy-code-button{opacity:1}.vp-copy-code-button{position:absolute;top:.5em;right:.5em;z-index:5;width:2.5rem;height:2.5rem;padding:0;border-width:0;border-radius:.5rem;background:transparent;outline:none;opacity:0;cursor:pointer;transition:opacity .4s}@media print{.vp-copy-code-button{display:none}}.vp-copy-code-button:focus,.vp-copy-code-button.copied{opacity:1}.vp-copy-code-button:hover,.vp-copy-code-button.copied{background:var(--copy-code-hover)}.vp-copy-code-button.copied .vp-copy-icon{-webkit-mask-image:var(--code-copied-icon);mask-image:var(--code-copied-icon)}.vp-copy-code-button.copied:after{content:attr(data-copied);position:absolute;top:0;right:calc(100% + .25rem);display:block;height:1.25rem;padding:.625rem;border-radius:.5rem;background:var(--copy-code-hover);color:var(--copy-code-color);font-weight:500;line-height:1.25rem;white-space:nowrap}.vp-copy-icon{width:1.25rem;height:1.25rem;padding:.625rem;background:currentcolor;color:var(--copy-code-color);font-size:1.25rem;-webkit-mask-image:var(--code-copy-icon);mask-image:var(--code-copy-icon);-webkit-mask-position:50%;mask-position:50%;-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:1em;mask-size:1em}:root{--code-copy-icon: url("data:image/svg+xml,%3csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%2024%2024'%20fill='none'%20height='20'%20width='20'%20stroke='rgba(128,128,128,1)'%20stroke-width='2'%3e%3cpath%20stroke-linecap='round'%20stroke-linejoin='round'%20d='M9%205H7a2%202%200%200%200-2%202v12a2%202%200%200%200%202%202h10a2%202%200%200%200%202-2V7a2%202%200%200%200-2-2h-2M9%205a2%202%200%200%200%202%202h2a2%202%200%200%200%202-2M9%205a2%202%200%200%201%202-2h2a2%202%200%200%201%202%202'%20/%3e%3c/svg%3e");--code-copied-icon: url("data:image/svg+xml,%3csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%2024%2024'%20fill='none'%20height='20'%20width='20'%20stroke='rgba(128,128,128,1)'%20stroke-width='2'%3e%3cpath%20stroke-linecap='round'%20stroke-linejoin='round'%20d='M9%205H7a2%202%200%200%200-2%202v12a2%202%200%200%200%202%202h10a2%202%200%200%200%202-2V7a2%202%200%200%200-2-2h-2M9%205a2%202%200%200%200%202%202h2a2%202%200%200%200%202-2M9%205a2%202%200%200%201%202-2h2a2%202%200%200%201%202%202m-6%209%202%202%204-4'%20/%3e%3c/svg%3e");--copy-code-color: #9e9e9e;--copy-code-hover: rgb(0 0 0 / 50%)}:root{--external-link-icon-color: #aaa}.external-link-icon{position:relative;display:inline-block;color:var(--external-link-icon-color);vertical-align:middle;top:-1px}@media print{.external-link-icon{display:none}}.external-link-icon-sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border-width:0;-webkit-user-select:none;-moz-user-select:none;user-select:none}:root{--medium-zoom-z-index: 100;--medium-zoom-bg-color: #ffffff;--medium-zoom-opacity: 1}.medium-zoom-overlay{background-color:var(--medium-zoom-bg-color)!important;z-index:var(--medium-zoom-z-index)}.medium-zoom-overlay~img{z-index:calc(var(--medium-zoom-z-index) + 1)}.medium-zoom--opened .medium-zoom-overlay{opacity:var(--medium-zoom-opacity)}:root{--nprogress-color: #29d;--nprogress-z-index: 1031}#nprogress{pointer-events:none}#nprogress .bar{background:var(--nprogress-color);position:fixed;z-index:var(--nprogress-z-index);top:0;left:0;width:100%;height:2px}:root{--c-brand: #3eaf7c;--c-brand-light: #4abf8a;--c-bg: #ffffff;--c-bg-light: #f3f4f5;--c-bg-lighter: #eeeeee;--c-bg-dark: #ebebec;--c-bg-darker: #e6e6e6;--c-bg-navbar: var(--c-bg);--c-bg-sidebar: var(--c-bg);--c-bg-arrow: #cccccc;--c-text: #2c3e50;--c-text-accent: var(--c-brand);--c-text-light: #3a5169;--c-text-lighter: #4e6e8e;--c-text-lightest: #6a8bad;--c-text-quote: #999999;--c-border: #eaecef;--c-border-dark: #dfe2e5;--c-tip: #42b983;--c-tip-bg: var(--c-bg-light);--c-tip-title: var(--c-text);--c-tip-text: var(--c-text);--c-tip-text-accent: var(--c-text-accent);--c-warning: #ffc310;--c-warning-bg: #fffae3;--c-warning-bg-light: #fff3ba;--c-warning-bg-lighter: #fff0b0;--c-warning-border-dark: #f7dc91;--c-warning-details-bg: #fff5ca;--c-warning-title: #f1b300;--c-warning-text: #746000;--c-warning-text-accent: #edb100;--c-warning-text-light: #c1971c;--c-warning-text-quote: #ccab49;--c-danger: #f11e37;--c-danger-bg: #ffe0e0;--c-danger-bg-light: #ffcfde;--c-danger-bg-lighter: #ffc9c9;--c-danger-border-dark: #f1abab;--c-danger-details-bg: #ffd4d4;--c-danger-title: #ed1e2c;--c-danger-text: #660000;--c-danger-text-accent: #bd1a1a;--c-danger-text-light: #b5474d;--c-danger-text-quote: #c15b5b;--c-details-bg: #eeeeee;--c-badge-tip: var(--c-tip);--c-badge-warning: #ecc808;--c-badge-warning-text: var(--c-bg);--c-badge-danger: #dc2626;--c-badge-danger-text: var(--c-bg);--c-code-group-tab-title: rgba(255, 255, 255, .9);--c-code-group-tab-bg: var(--code-bg-color);--c-code-group-tab-outline: var(var(--c-code-group-tab-title));--c-code-group-tab-active-border: var(--c-brand);--t-color: .3s ease;--t-transform: .3s ease;--code-bg-color: #282c34;--code-hl-bg-color: rgba(0, 0, 0, .66);--code-ln-color: #9e9e9e;--code-ln-wrapper-width: 3.5rem;--font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif;--font-family-code: Consolas, Monaco, "Andale Mono", "Ubuntu Mono", monospace;--navbar-height: 3.6rem;--navbar-padding-v: .7rem;--navbar-padding-h: 1.5rem;--sidebar-width: 20rem;--sidebar-width-mobile: calc(var(--sidebar-width) * .82);--content-width: 740px;--homepage-width: 960px}.vp-back-to-top-button{--back-to-top-color: var(--c-brand);--back-to-top-color-hover: var(--c-brand-light);--back-to-top-bg-color: var(--c-bg)}.vp-catalog-wrapper{--catalog-bg-color: var(--c-bg);--catalog-bg-secondary-color: var(--c-bg-dark);--catalog-border-color: var(--c-border);--catalog-active-color: var(--c-brand);--catalog-hover-color: var(--c-brand-light)}.DocSearch{--docsearch-primary-color: var(--c-brand);--docsearch-text-color: var(--c-text);--docsearch-highlight-color: var(--c-brand);--docsearch-muted-color: var(--c-text-quote);--docsearch-container-background: rgba(9, 10, 17, .8);--docsearch-modal-background: var(--c-bg-light);--docsearch-searchbox-background: var(--c-bg-lighter);--docsearch-searchbox-focus-background: var(--c-bg);--docsearch-searchbox-shadow: inset 0 0 0 2px var(--c-brand);--docsearch-hit-color: var(--c-text-light);--docsearch-hit-active-color: var(--c-bg);--docsearch-hit-background: var(--c-bg);--docsearch-hit-shadow: 0 1px 3px 0 var(--c-border-dark);--docsearch-footer-background: var(--c-bg)}.external-link-icon{--external-link-icon-color: var(--c-text-quote)}.medium-zoom-overlay{--medium-zoom-bg-color: var(--c-bg)}#nprogress{--nprogress-color: var(--c-brand)}body{--photo-swipe-bullet: var(--c-bg);--photo-swipe-bullet-active: var(--c-brand)}html{--pwa-text-color: var(--c-text);--pwa-bg-color: var(--c-bg);--pwa-border-color: var(--c-brand);--pwa-btn-text-color: var(--c-bg);--pwa-btn-bg-color: var(--c-brand);--pwa-btn-hover-bg-color: var(--c-brand-light)}html.dark{--pwa-shadow-color: rgb(0 0 0 / 30%);--pwa-content-color: #ccc;--pwa-content-light-color: #999}.language-modal-mask{--redirect-bg-color: var(--c-bg);--redirect-bg-color-light: var(--c-bg-light);--redirect-bg-color-lighter: var(--c-bg-lighter);--redirect-text-color: var(--c-text);--redirect-primary-bg-color: var(--c-brand);--redirect-primary-hover-bg-color: var(--c-brand-light);--redirect-primary-text-color: var(--c-bg)}.search-box{--search-bg-color: var(--c-bg);--search-accent-color: var(--c-brand);--search-text-color: var(--c-text);--search-border-color: var(--c-border);--search-item-text-color: var(--c-text-lighter);--search-item-focus-bg-color: var(--c-bg-light)}html.dark{--c-brand: #3aa675;--c-brand-light: #349469;--c-bg: #22272e;--c-bg-light: #2b313a;--c-bg-lighter: #262c34;--c-bg-dark: #343b44;--c-bg-darker: #37404c;--c-text: #adbac7;--c-text-light: #96a7b7;--c-text-lighter: #8b9eb0;--c-text-lightest: #8094a8;--c-border: #3e4c5a;--c-border-dark: #34404c;--c-tip: #318a62;--c-warning: #e0ad15;--c-warning-bg: #2d2f2d;--c-warning-bg-light: #423e2a;--c-warning-bg-lighter: #44442f;--c-warning-border-dark: #957c35;--c-warning-details-bg: #39392d;--c-warning-title: #fdca31;--c-warning-text: #d8d96d;--c-warning-text-accent: #ffbf00;--c-warning-text-light: #ddb84b;--c-warning-text-quote: #ccab49;--c-danger: #fc1e38;--c-danger-bg: #39232c;--c-danger-bg-light: #4b2b35;--c-danger-bg-lighter: #553040;--c-danger-border-dark: #a25151;--c-danger-details-bg: #482936;--c-danger-title: #fc2d3b;--c-danger-text: #ea9ca0;--c-danger-text-accent: #fd3636;--c-danger-text-light: #d9777c;--c-danger-text-quote: #d56b6b;--c-details-bg: #323843;--c-badge-warning: var(--c-warning);--c-badge-warning-text: #3c2e05;--c-badge-danger: var(--c-danger);--c-badge-danger-text: #401416;--code-hl-bg-color: #363b46}html.dark .DocSearch{--docsearch-logo-color: var(--c-text);--docsearch-modal-shadow: inset 1px 1px 0 0 #2c2e40, 0 3px 8px 0 #000309;--docsearch-key-shadow: inset 0 -2px 0 0 #282d55, inset 0 0 1px 1px #51577d, 0 2px 2px 0 rgba(3, 4, 9, .3);--docsearch-key-gradient: linear-gradient(-225deg, #444950, #1c1e21);--docsearch-footer-shadow: inset 0 1px 0 0 rgba(73, 76, 106, .5), 0 -4px 8px 0 rgba(0, 0, 0, .2)}html,body{padding:0;margin:0;background-color:var(--c-bg);transition:background-color var(--t-color)}html.dark{color-scheme:dark}html{font-size:16px}body{font-family:var(--font-family);-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;font-size:1rem;color:var(--c-text)}a{font-weight:500;color:var(--c-text-accent);text-decoration:none;overflow-wrap:break-word}p a code{font-weight:400;color:var(--c-text-accent)}kbd{font-family:var(--font-family-code);color:var(--c-text);background:var(--c-bg-lighter);border:solid .15rem var(--c-border-dark);border-bottom:solid .25rem var(--c-border-dark);border-radius:.15rem;padding:0 .15em}code{font-family:var(--font-family-code);color:var(--c-text-lighter);padding:.25rem .5rem;margin:0;font-size:.85em;background-color:var(--c-bg-light);border-radius:3px;overflow-wrap:break-word;transition:background-color var(--t-color)}blockquote{font-size:1rem;color:var(--c-text-quote);border-left:.2rem solid var(--c-border-dark);margin:1rem 0;padding:.25rem 0 .25rem 1rem;overflow-wrap:break-word}blockquote>p{margin:0}ul,ol{padding-left:1.2em}strong{font-weight:600}h1,h2,h3,h4,h5,h6{font-weight:600;line-height:1.25;overflow-wrap:break-word}h1:focus-visible,h2:focus-visible,h3:focus-visible,h4:focus-visible,h5:focus-visible,h6:focus-visible{outline:none}h1 .header-anchor,h2 .header-anchor,h3 .header-anchor,h4 .header-anchor,h5 .header-anchor,h6 .header-anchor{color:inherit;text-decoration:none;position:relative}h1 .header-anchor:hover:before,h2 .header-anchor:hover:before,h3 .header-anchor:hover:before,h4 .header-anchor:hover:before,h5 .header-anchor:hover:before,h6 .header-anchor:hover:before{font-size:.8em;content:"¶";position:absolute;left:-.75em;color:var(--c-brand)}h1 .header-anchor:focus-visible,h2 .header-anchor:focus-visible,h3 .header-anchor:focus-visible,h4 .header-anchor:focus-visible,h5 .header-anchor:focus-visible,h6 .header-anchor:focus-visible{outline:none}h1 .header-anchor:focus-visible:before,h2 .header-anchor:focus-visible:before,h3 .header-anchor:focus-visible:before,h4 .header-anchor:focus-visible:before,h5 .header-anchor:focus-visible:before,h6 .header-anchor:focus-visible:before{content:"¶";position:absolute;left:-.75em;color:var(--c-brand);outline:auto}h1{font-size:2.2rem}h2{font-size:1.65rem;padding-bottom:.3rem;border-bottom:1px solid var(--c-border);transition:border-color var(--t-color)}h3{font-size:1.35rem}h4{font-size:1.15rem}h5{font-size:1.05rem}h6{font-size:1rem}@media print{a[href^="http://"]:after,a[href^="https://"]:after{content:" (" attr(href) ") "}}p,ul,ol{line-height:1.7;overflow-wrap:break-word}hr{border:0;border-top:1px solid var(--c-border)}table{border-collapse:collapse;margin:1rem 0;display:block;overflow-x:auto;transition:border-color var(--t-color)}tr{border-top:1px solid var(--c-border-dark);transition:border-color var(--t-color)}tr:nth-child(2n){background-color:var(--c-bg-light);transition:background-color var(--t-color)}tr:nth-child(2n) code{background-color:var(--c-bg-dark)}th,td{padding:.6em 1em;border:1px solid var(--c-border-dark);transition:border-color var(--t-color)}.arrow{display:inline-block;width:0;height:0}.arrow.up{border-left:4px solid transparent;border-right:4px solid transparent;border-bottom:6px solid var(--c-bg-arrow)}.arrow.down{border-left:4px solid transparent;border-right:4px solid transparent;border-top:6px solid var(--c-bg-arrow)}.arrow.right{border-top:4px solid transparent;border-bottom:4px solid transparent;border-left:6px solid var(--c-bg-arrow)}.arrow.left{border-top:4px solid transparent;border-bottom:4px solid transparent;border-right:6px solid var(--c-bg-arrow)}.badge{display:inline-block;font-size:14px;font-weight:600;height:18px;line-height:18px;border-radius:3px;padding:0 6px;color:var(--c-bg);vertical-align:top;transition:color var(--t-color),background-color var(--t-color)}.badge.tip{background-color:var(--c-badge-tip)}.badge.warning{background-color:var(--c-badge-warning);color:var(--c-badge-warning-text)}.badge.danger{background-color:var(--c-badge-danger);color:var(--c-badge-danger-text)}.badge+.badge{margin-left:5px}code[class*=language-],pre[class*=language-]{color:#ccc;background:none;font-family:var(--font-family-code);font-size:1em;text-align:left;white-space:pre;word-spacing:normal;word-break:normal;word-wrap:normal;line-height:1.5;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-hyphens:none;hyphens:none}pre[class*=language-]{padding:1em;margin:.5em 0;overflow:auto}:not(pre)>code[class*=language-],pre[class*=language-]{background:#2d2d2d}:not(pre)>code[class*=language-]{padding:.1em;border-radius:.3em;white-space:normal}.token.comment,.token.block-comment,.token.prolog,.token.doctype,.token.cdata{color:#999}.token.punctuation{color:#ccc}.token.tag,.token.attr-name,.token.namespace,.token.deleted{color:#ec5975}.token.function-name{color:#6196cc}.token.boolean,.token.number,.token.function{color:#f08d49}.token.property,.token.class-name,.token.constant,.token.symbol{color:#f8c555}.token.selector,.token.important,.token.atrule,.token.keyword,.token.builtin{color:#cc99cd}.token.string,.token.char,.token.attr-value,.token.regex,.token.variable{color:#7ec699}.token.operator,.token.entity,.token.url{color:#67cdcc}.token.important,.token.bold{font-weight:700}.token.italic{font-style:italic}.token.entity{cursor:help}.token.inserted{color:#3eaf7c}.theme-default-content pre,.theme-default-content pre[class*=language-]{line-height:1.375;padding:1.3rem 1.5rem;margin:.85rem 0;border-radius:6px;overflow:auto}.theme-default-content pre code,.theme-default-content pre[class*=language-] code{color:#fff;padding:0;background-color:transparent!important;border-radius:0;overflow-wrap:unset;-webkit-font-smoothing:auto;-moz-osx-font-smoothing:auto}.theme-default-content .line-number{font-family:var(--font-family-code)}div[class*=language-]{position:relative;background-color:var(--code-bg-color);border-radius:6px}div[class*=language-]:before{content:attr(data-title);position:absolute;z-index:3;top:.8em;right:1em;font-size:.75rem;color:var(--code-ln-color)}div[class*=language-] pre,div[class*=language-] pre[class*=language-]{background:transparent!important;position:relative;z-index:1}div[class*=language-] .highlight-lines{-webkit-user-select:none;-moz-user-select:none;user-select:none;padding-top:1.3rem;position:absolute;top:0;left:0;width:100%;line-height:1.375}div[class*=language-] .highlight-lines .highlight-line{background-color:var(--code-hl-bg-color)}div[class*=language-]:not(.line-numbers-mode) .line-numbers{display:none}div[class*=language-].line-numbers-mode .highlight-lines .highlight-line{position:relative}div[class*=language-].line-numbers-mode .highlight-lines .highlight-line:before{content:" ";position:absolute;z-index:2;left:0;top:0;display:block;width:var(--code-ln-wrapper-width);height:100%}div[class*=language-].line-numbers-mode pre{margin-left:var(--code-ln-wrapper-width);padding-left:1rem;vertical-align:middle}div[class*=language-].line-numbers-mode .line-numbers{position:absolute;top:0;width:var(--code-ln-wrapper-width);text-align:center;color:var(--code-ln-color);padding-top:1.25rem;line-height:1.375;counter-reset:line-number}div[class*=language-].line-numbers-mode .line-numbers .line-number{position:relative;z-index:3;-webkit-user-select:none;-moz-user-select:none;user-select:none;height:1.375em}div[class*=language-].line-numbers-mode .line-numbers .line-number:before{counter-increment:line-number;content:counter(line-number);font-size:.85em}div[class*=language-].line-numbers-mode:after{content:"";position:absolute;top:0;left:0;width:var(--code-ln-wrapper-width);height:100%;border-radius:6px 0 0 6px;border-right:1px solid var(--code-hl-bg-color)}@media (max-width: 419px){.theme-default-content div[class*=language-]{margin:.85rem -1.5rem;border-radius:0}}.code-group__nav{margin-top:.85rem;margin-bottom:calc(-1.7rem - 6px);padding-bottom:calc(1.7rem - 6px);padding-left:10px;padding-top:10px;border-top-left-radius:6px;border-top-right-radius:6px;background-color:var(--c-code-group-tab-bg)}.code-group__nav-tab{border:0;padding:5px;cursor:pointer;background-color:transparent;font-size:.85em;line-height:1.4;color:var(--c-code-group-tab-title);font-weight:600}.code-group__nav-tab:focus{outline:none}.code-group__nav-tab:focus-visible{outline:1px solid var(--c-code-group-tab-outline)}.code-group__nav-tab-active{border-bottom:var(--c-code-group-tab-active-border) 1px solid}@media (max-width: 419px){.code-group__nav{margin-left:-1.5rem;margin-right:-1.5rem;border-radius:0}}.code-group-item{display:none}.code-group-item__active{display:block}.code-group-item>pre{background-color:orange}.custom-container{transition:color var(--t-color),border-color var(--t-color),background-color var(--t-color)}.custom-container .custom-container-title{font-weight:600}.custom-container .custom-container-title:not(:only-child){margin-bottom:-.4rem}.custom-container.tip,.custom-container.warning,.custom-container.danger{padding:.1rem 1.5rem;border-left-width:.5rem;border-left-style:solid;margin:1rem 0}.custom-container.tip{border-color:var(--c-tip);background-color:var(--c-tip-bg);color:var(--c-tip-text)}.custom-container.tip .custom-container-title{color:var(--c-tip-title)}.custom-container.tip a{color:var(--c-tip-text-accent)}.custom-container.tip code{background-color:var(--c-bg-dark)}.custom-container.warning{border-color:var(--c-warning);background-color:var(--c-warning-bg);color:var(--c-warning-text)}.custom-container.warning .custom-container-title{color:var(--c-warning-title)}.custom-container.warning a{color:var(--c-warning-text-accent)}.custom-container.warning blockquote{border-left-color:var(--c-warning-border-dark);color:var(--c-warning-text-quote)}.custom-container.warning code{color:var(--c-warning-text-light);background-color:var(--c-warning-bg-light)}.custom-container.warning details{background-color:var(--c-warning-details-bg)}.custom-container.warning details code{background-color:var(--c-warning-bg-lighter)}.custom-container.warning .external-link-icon{--external-link-icon-color: var(--c-warning-text-quote)}.custom-container.danger{border-color:var(--c-danger);background-color:var(--c-danger-bg);color:var(--c-danger-text)}.custom-container.danger .custom-container-title{color:var(--c-danger-title)}.custom-container.danger a{color:var(--c-danger-text-accent)}.custom-container.danger blockquote{border-left-color:var(--c-danger-border-dark);color:var(--c-danger-text-quote)}.custom-container.danger code{color:var(--c-danger-text-light);background-color:var(--c-danger-bg-light)}.custom-container.danger details{background-color:var(--c-danger-details-bg)}.custom-container.danger details code{background-color:var(--c-danger-bg-lighter)}.custom-container.danger .external-link-icon{--external-link-icon-color: var(--c-danger-text-quote)}.custom-container.details{display:block;position:relative;border-radius:2px;margin:1.6em 0;padding:1.6em;background-color:var(--c-details-bg)}.custom-container.details code{background-color:var(--c-bg-darker)}.custom-container.details h4{margin-top:0}.custom-container.details figure:last-child,.custom-container.details p:last-child{margin-bottom:0;padding-bottom:0}.custom-container.details summary{outline:none;cursor:pointer}.home{padding:var(--navbar-height) 2rem 0;max-width:var(--homepage-width);margin:0 auto;display:block}.home .hero{text-align:center}.home .hero img{max-width:100%;max-height:280px;display:block;margin:3rem auto 1.5rem}.home .hero h1{font-size:3rem}.home .hero h1,.home .hero .description,.home .hero .actions{margin:1.8rem auto}.home .hero .actions{display:flex;flex-wrap:wrap;gap:1rem;justify-content:center}.home .hero .description{max-width:35rem;font-size:1.6rem;line-height:1.3;color:var(--c-text-lightest)}.home .hero .action-button{display:inline-block;font-size:1.2rem;padding:.8rem 1.6rem;border-width:2px;border-style:solid;border-radius:4px;transition:background-color var(--t-color);box-sizing:border-box}.home .hero .action-button.primary{color:var(--c-bg);background-color:var(--c-brand);border-color:var(--c-brand)}.home .hero .action-button.primary:hover{background-color:var(--c-brand-light)}.home .hero .action-button.secondary{color:var(--c-brand);background-color:var(--c-bg);border-color:var(--c-brand)}.home .hero .action-button.secondary:hover{color:var(--c-bg);background-color:var(--c-brand-light)}.home .features{border-top:1px solid var(--c-border);transition:border-color var(--t-color);padding:1.2rem 0;margin-top:2.5rem;display:flex;flex-wrap:wrap;align-items:flex-start;align-content:stretch;justify-content:space-between}.home .feature{flex-grow:1;flex-basis:30%;max-width:30%}.home .feature h2{font-size:1.4rem;font-weight:500;border-bottom:none;padding-bottom:0;color:var(--c-text-light)}.home .feature p{color:var(--c-text-lighter)}.home .theme-default-content{padding:0;margin:0}.home .footer{padding:2.5rem;border-top:1px solid var(--c-border);text-align:center;color:var(--c-text-lighter);transition:border-color var(--t-color)}@media (max-width: 719px){.home .features{flex-direction:column}.home .feature{max-width:100%;padding:0 2.5rem}}@media (max-width: 419px){.home{padding-left:1.5rem;padding-right:1.5rem}.home .hero img{max-height:210px;margin:2rem auto 1.2rem}.home .hero h1{font-size:2rem}.home .hero h1,.home .hero .description,.home .hero .actions{margin:1.2rem auto}.home .hero .description{font-size:1.2rem}.home .hero .action-button{font-size:1rem;padding:.6rem 1.2rem}.home .feature h2{font-size:1.25rem}}.page{padding-top:var(--navbar-height);padding-left:var(--sidebar-width)}.navbar{position:fixed;z-index:20;top:0;left:0;right:0;height:var(--navbar-height);box-sizing:border-box;border-bottom:1px solid var(--c-border);background-color:var(--c-bg-navbar);transition:background-color var(--t-color),border-color var(--t-color)}.sidebar{font-size:16px;width:var(--sidebar-width);position:fixed;z-index:10;margin:0;top:var(--navbar-height);left:0;bottom:0;box-sizing:border-box;border-right:1px solid var(--c-border);overflow-y:auto;scrollbar-width:thin;scrollbar-color:var(--c-brand) var(--c-border);background-color:var(--c-bg-sidebar);transition:transform var(--t-transform),background-color var(--t-color),border-color var(--t-color)}.sidebar::-webkit-scrollbar{width:7px}.sidebar::-webkit-scrollbar-track{background-color:var(--c-border)}.sidebar::-webkit-scrollbar-thumb{background-color:var(--c-brand)}.sidebar-mask{position:fixed;z-index:9;top:0;left:0;width:100vw;height:100vh;display:none}.theme-container.sidebar-open .sidebar-mask{display:block}.theme-container.sidebar-open .navbar>.toggle-sidebar-button .icon span:nth-child(1){transform:rotate(45deg) translate3d(5.5px,5.5px,0)}.theme-container.sidebar-open .navbar>.toggle-sidebar-button .icon span:nth-child(2){transform:scale3d(0,1,1)}.theme-container.sidebar-open .navbar>.toggle-sidebar-button .icon span:nth-child(3){transform:rotate(-45deg) translate3d(6px,-6px,0)}.theme-container.sidebar-open .navbar>.toggle-sidebar-button .icon span:nth-child(1),.theme-container.sidebar-open .navbar>.toggle-sidebar-button .icon span:nth-child(3){transform-origin:center}.theme-container.no-navbar .theme-default-content h1,.theme-container.no-navbar .theme-default-content h2,.theme-container.no-navbar .theme-default-content h3,.theme-container.no-navbar .theme-default-content h4,.theme-container.no-navbar .theme-default-content h5,.theme-container.no-navbar .theme-default-content h6{margin-top:1.5rem;padding-top:0}.theme-container.no-navbar .page{padding-top:0}.theme-container.no-navbar .sidebar{top:0}.theme-container.no-sidebar .sidebar{display:none}@media (max-width: 719px){.theme-container.no-sidebar .sidebar{display:block}}.theme-container.no-sidebar .page{padding-left:0}.theme-default-content a:not(.header-anchor):hover{text-decoration:underline}.theme-default-content img{max-width:100%}.theme-default-content h1,.theme-default-content h2,.theme-default-content h3,.theme-default-content h4,.theme-default-content h5,.theme-default-content h6{margin-top:calc(.5rem - var(--navbar-height));padding-top:calc(1rem + var(--navbar-height));margin-bottom:0}.theme-default-content h1:first-child,.theme-default-content h2:first-child,.theme-default-content h3:first-child,.theme-default-content h4:first-child,.theme-default-content h5:first-child,.theme-default-content h6:first-child{margin-bottom:1rem}.theme-default-content h1:first-child+p,.theme-default-content h1:first-child+pre,.theme-default-content h1:first-child+.custom-container,.theme-default-content h2:first-child+p,.theme-default-content h2:first-child+pre,.theme-default-content h2:first-child+.custom-container,.theme-default-content h3:first-child+p,.theme-default-content h3:first-child+pre,.theme-default-content h3:first-child+.custom-container,.theme-default-content h4:first-child+p,.theme-default-content h4:first-child+pre,.theme-default-content h4:first-child+.custom-container,.theme-default-content h5:first-child+p,.theme-default-content h5:first-child+pre,.theme-default-content h5:first-child+.custom-container,.theme-default-content h6:first-child+p,.theme-default-content h6:first-child+pre,.theme-default-content h6:first-child+.custom-container{margin-top:2rem}@media (max-width: 959px){.sidebar{font-size:15px;width:var(--sidebar-width-mobile)}.page{padding-left:var(--sidebar-width-mobile)}}@media (max-width: 719px){.sidebar{top:0;padding-top:var(--navbar-height);transform:translate(-100%)}.page{padding-left:0}.theme-container.sidebar-open .sidebar{transform:translate(0)}.theme-container.no-navbar .sidebar{padding-top:0}}@media (max-width: 419px){h1{font-size:1.9rem}}.navbar{--navbar-line-height: calc( var(--navbar-height) - 2 * var(--navbar-padding-v) );padding:var(--navbar-padding-v) var(--navbar-padding-h);line-height:var(--navbar-line-height)}.navbar .logo{height:var(--navbar-line-height);margin-right:var(--navbar-padding-v);vertical-align:top}.navbar .site-name{font-size:1.3rem;font-weight:600;color:var(--c-text);position:relative}.navbar .navbar-items-wrapper{display:flex;position:absolute;box-sizing:border-box;top:var(--navbar-padding-v);right:var(--navbar-padding-h);height:var(--navbar-line-height);padding-left:var(--navbar-padding-h);white-space:nowrap;font-size:.9rem}.navbar .navbar-items-wrapper .search-box{flex:0 0 auto;vertical-align:top}@media screen and (max-width: 719px){.navbar{padding-left:4rem}.navbar .site-name{display:block;width:calc(100vw - 11rem);overflow:hidden;white-space:nowrap;text-overflow:ellipsis}.navbar .can-hide{display:none}}.navbar-items{display:inline-block}@media print{.navbar-items{display:none}}.navbar-items a{display:inline-block;line-height:1.4rem;color:inherit}.navbar-items a:hover,.navbar-items a.route-link-active{color:var(--c-text)}.navbar-items .navbar-item{position:relative;display:inline-block;margin-left:1.5rem;line-height:var(--navbar-line-height)}.navbar-items .navbar-item:first-child{margin-left:0}.navbar-items .navbar-item>a:hover,.navbar-items .navbar-item>a.route-link-active{margin-bottom:-2px;border-bottom:2px solid var(--c-text-accent)}@media (max-width: 719px){.navbar-items .navbar-item{margin-left:0}.navbar-items .navbar-item>a:hover,.navbar-items .navbar-item>a.route-link-active{margin-bottom:0;border-bottom:none}.navbar-items a:hover,.navbar-items a.route-link-active{color:var(--c-text-accent)}}.toggle-sidebar-button{position:absolute;top:.6rem;left:1rem;display:none;padding:.6rem;cursor:pointer}.toggle-sidebar-button .icon{display:flex;flex-direction:column;justify-content:center;align-items:center;width:1.25rem;height:1.25rem;cursor:inherit}.toggle-sidebar-button .icon span{display:inline-block;width:100%;height:2px;border-radius:2px;background-color:var(--c-text);transition:transform var(--t-transform)}.toggle-sidebar-button .icon span:nth-child(2){margin:6px 0}@media screen and (max-width: 719px){.toggle-sidebar-button{display:block}}.toggle-color-mode-button{display:flex;margin:auto;margin-left:1rem;border:0;background:none;color:var(--c-text);opacity:.8;cursor:pointer}@media print{.toggle-color-mode-button{display:none}}.toggle-color-mode-button:hover{opacity:1}.toggle-color-mode-button .icon{width:1.25rem;height:1.25rem}.DocSearch{transition:background-color var(--t-color)}.navbar-dropdown-wrapper{cursor:pointer}.navbar-dropdown-wrapper .navbar-dropdown-title,.navbar-dropdown-wrapper .navbar-dropdown-title-mobile{display:block;font-size:.9rem;font-family:inherit;cursor:inherit;padding:inherit;line-height:1.4rem;background:transparent;border:none;font-weight:500;color:var(--c-text)}.navbar-dropdown-wrapper .navbar-dropdown-title:hover,.navbar-dropdown-wrapper .navbar-dropdown-title-mobile:hover{border-color:transparent}.navbar-dropdown-wrapper .navbar-dropdown-title .arrow,.navbar-dropdown-wrapper .navbar-dropdown-title-mobile .arrow{vertical-align:middle;margin-top:-1px;margin-left:.4rem}.navbar-dropdown-wrapper .navbar-dropdown-title-mobile{display:none;font-weight:600;font-size:inherit}.navbar-dropdown-wrapper .navbar-dropdown-title-mobile:hover{color:var(--c-text-accent)}.navbar-dropdown-wrapper .navbar-dropdown .navbar-dropdown-item{color:inherit;line-height:1.7rem}.navbar-dropdown-wrapper .navbar-dropdown .navbar-dropdown-item .navbar-dropdown-subtitle{margin:.45rem 0 0;border-top:1px solid var(--c-border);padding:1rem 0 .45rem;font-size:.9rem}.navbar-dropdown-wrapper .navbar-dropdown .navbar-dropdown-item .navbar-dropdown-subtitle>span{padding:0 1.5rem 0 1.25rem}.navbar-dropdown-wrapper .navbar-dropdown .navbar-dropdown-item .navbar-dropdown-subtitle>a{font-weight:inherit}.navbar-dropdown-wrapper .navbar-dropdown .navbar-dropdown-item .navbar-dropdown-subtitle>a.route-link-active:after{display:none}.navbar-dropdown-wrapper .navbar-dropdown .navbar-dropdown-item .navbar-dropdown-subitem-wrapper{padding:0;list-style:none}.navbar-dropdown-wrapper .navbar-dropdown .navbar-dropdown-item .navbar-dropdown-subitem-wrapper .navbar-dropdown-subitem{font-size:.9em}.navbar-dropdown-wrapper .navbar-dropdown .navbar-dropdown-item a{display:block;line-height:1.7rem;position:relative;border-bottom:none;font-weight:400;margin-bottom:0;padding:0 1.5rem 0 1.25rem}.navbar-dropdown-wrapper .navbar-dropdown .navbar-dropdown-item a:hover,.navbar-dropdown-wrapper .navbar-dropdown .navbar-dropdown-item a.route-link-active{color:var(--c-text-accent)}.navbar-dropdown-wrapper .navbar-dropdown .navbar-dropdown-item a.route-link-active:after{content:"";width:0;height:0;border-left:5px solid var(--c-text-accent);border-top:3px solid transparent;border-bottom:3px solid transparent;position:absolute;top:calc(50% - 2px);left:9px}.navbar-dropdown-wrapper .navbar-dropdown .navbar-dropdown-item:first-child .navbar-dropdown-subtitle{margin-top:0;padding-top:0;border-top:0}.navbar-dropdown-wrapper.mobile.open .navbar-dropdown-title,.navbar-dropdown-wrapper.mobile.open .navbar-dropdown-title-mobile{margin-bottom:.5rem}.navbar-dropdown-wrapper.mobile .navbar-dropdown-title,.navbar-dropdown-wrapper.mobile .navbar-dropdown-title-mobile{display:none}.navbar-dropdown-wrapper.mobile .navbar-dropdown-title-mobile{display:block}.navbar-dropdown-wrapper.mobile .navbar-dropdown{transition:height .1s ease-out;overflow:hidden}.navbar-dropdown-wrapper.mobile .navbar-dropdown .navbar-dropdown-item .navbar-dropdown-subtitle{border-top:0;margin-top:0;padding-top:0;padding-bottom:0}.navbar-dropdown-wrapper.mobile .navbar-dropdown .navbar-dropdown-item .navbar-dropdown-subtitle,.navbar-dropdown-wrapper.mobile .navbar-dropdown .navbar-dropdown-item>a{font-size:15px;line-height:2rem}.navbar-dropdown-wrapper.mobile .navbar-dropdown .navbar-dropdown-item .navbar-dropdown-subitem{font-size:14px;padding-left:1rem}.navbar-dropdown-wrapper:not(.mobile){height:1.8rem}.navbar-dropdown-wrapper:not(.mobile):hover .navbar-dropdown,.navbar-dropdown-wrapper:not(.mobile).open .navbar-dropdown{display:block!important}.navbar-dropdown-wrapper:not(.mobile).open:blur{display:none}.navbar-dropdown-wrapper:not(.mobile) .navbar-dropdown{display:none;height:auto!important;box-sizing:border-box;max-height:calc(100vh - 2.7rem);overflow-y:auto;position:absolute;top:100%;right:0;background-color:var(--c-bg-navbar);padding:.6rem 0;border:1px solid var(--c-border);border-bottom-color:var(--c-border-dark);text-align:left;border-radius:.25rem;white-space:nowrap;margin:0}.page{padding-bottom:2rem;display:block}.page .theme-default-content{max-width:var(--content-width);margin:0 auto;padding:2rem 2.5rem;padding-top:0}@media (max-width: 959px){.page .theme-default-content{padding:2rem}}@media (max-width: 419px){.page .theme-default-content{padding:1.5rem}}.page-meta{max-width:var(--content-width);margin:0 auto;padding:1rem 2.5rem;overflow:auto}@media (max-width: 959px){.page-meta{padding:2rem}}@media (max-width: 419px){.page-meta{padding:1.5rem}}.page-meta .meta-item{cursor:default;margin-top:.8rem}.page-meta .meta-item .meta-item-label{font-weight:500;color:var(--c-text-lighter)}.page-meta .meta-item .meta-item-info{font-weight:400;color:var(--c-text-quote)}.page-meta .edit-link{display:inline-block;margin-right:.25rem}@media print{.page-meta .edit-link{display:none}}.page-meta .last-updated{float:right}@media (max-width: 719px){.page-meta .last-updated{font-size:.8em;float:none}.page-meta .contributors{font-size:.8em}}.page-nav{max-width:var(--content-width);margin:0 auto;padding:1rem 2.5rem 2rem;padding-bottom:0}@media (max-width: 959px){.page-nav{padding:2rem}}@media (max-width: 419px){.page-nav{padding:1.5rem}}.page-nav .inner{min-height:2rem;margin-top:0;border-top:1px solid var(--c-border);transition:border-color var(--t-color);padding-top:1rem;overflow:auto}.page-nav .prev a:before{content:"←"}.page-nav .next{float:right}.page-nav .next a:after{content:"→"}.sidebar ul{padding:0;margin:0;list-style-type:none}.sidebar a{display:inline-block}.sidebar .navbar-items{display:none;border-bottom:1px solid var(--c-border);transition:border-color var(--t-color);padding:.5rem 0 .75rem}.sidebar .navbar-items a{font-weight:600}.sidebar .navbar-items .navbar-item{display:block;line-height:1.25rem;font-size:1.1em;padding:.5rem 0 .5rem 1.5rem}.sidebar .sidebar-items{padding:1.5rem 0}@media (max-width: 719px){.sidebar .navbar-items{display:block}.sidebar .navbar-items .navbar-dropdown-wrapper .navbar-dropdown .navbar-dropdown-item a.route-link-active:after{top:calc(1rem - 2px)}.sidebar .sidebar-items{padding:1rem 0}}.sidebar-item{cursor:default;border-left:.25rem solid transparent;color:var(--c-text)}.sidebar-item:focus-visible{outline-width:1px;outline-offset:-1px}.sidebar-item.active:not(p.sidebar-heading){font-weight:600;color:var(--c-text-accent);border-left-color:var(--c-text-accent)}.sidebar-item.sidebar-heading{transition:color .15s ease;font-size:1.1em;font-weight:700;padding:.35rem 1.5rem .35rem 1.25rem;width:100%;box-sizing:border-box;margin:0}.sidebar-item.sidebar-heading+.sidebar-item-children{transition:height .1s ease-out;overflow:hidden;margin-bottom:.75rem}.sidebar-item.collapsible{cursor:pointer}.sidebar-item.collapsible .arrow{position:relative;top:-.12em;left:.5em}.sidebar-item:not(.sidebar-heading){font-size:1em;font-weight:400;display:inline-block;margin:0;padding:.35rem 1rem .35rem 2rem;line-height:1.4;width:100%;box-sizing:border-box}.sidebar-item:not(.sidebar-heading)+.sidebar-item-children{padding-left:1rem;font-size:.95em}.sidebar-item-children .sidebar-item-children .sidebar-item:not(.sidebar-heading){padding:.25rem 1rem .25rem 1.75rem}.sidebar-item-children .sidebar-item-children .sidebar-item:not(.sidebar-heading).active{font-weight:500;border-left-color:transparent}a.sidebar-heading+.sidebar-item-children .sidebar-item:not(.sidebar-heading).active{border-left-color:transparent}a.sidebar-item{cursor:pointer}a.sidebar-item:hover{color:var(--c-text-accent)}.table-of-contents .badge{vertical-align:middle}.dropdown-enter-from,.dropdown-leave-to{height:0!important}.fade-slide-y-enter-active{transition:all .2s ease}.fade-slide-y-leave-active{transition:all .2s cubic-bezier(1,.5,.8,1)}.fade-slide-y-enter-from,.fade-slide-y-leave-to{transform:translateY(10px);opacity:0}.footnote-item{margin-top:-3.6rem;padding-top:4.1rem}.footnote-item>p{margin-bottom:0}.footnote-ref{position:relative}.footnote-anchor{position:absolute;top:-4.1rem}.vp-catalog-wrapper{margin-top:8px;margin-bottom:8px}.vp-catalog-wrapper.index ol{padding-inline-start:0}.vp-catalog-wrapper.index li{list-style-type:none}.vp-catalog-wrapper.index .vp-catalogs{padding-inline-start:0}.vp-catalog-wrapper.index .vp-catalog{list-style-type:none}.vp-catalog-wrapper.index .vp-catalog-title:before{content:"§" counter(catalog-item,upper-roman) " "}.vp-catalog-wrapper.index .vp-child-catalogs{counter-reset:child-catalog}.vp-catalog-wrapper.index .vp-child-catalog{counter-increment:child-catalog}.vp-catalog-wrapper.index .vp-child-catalog .vp-catalog-title:before{content:counter(catalog-item) "." counter(child-catalog) " "}.vp-catalog-wrapper.index .vp-sub-catalogs{padding-inline-start:.5rem}.vp-catalogs{margin:0;counter-reset:catalog-item}.vp-catalogs.deep{padding-inline-start:0}.vp-catalogs.deep .vp-catalog{list-style-type:none}.vp-catalogs .font-icon{vertical-align:baseline;margin-inline-end:.25rem}.vp-catalog{counter-increment:catalog-item}.vp-catalog-main-title{margin-top:calc(.5rem - var(--navbar-height, 3.6rem));margin-bottom:.5rem;padding-top:var(--navbar-height, 3.6rem);font-weight:500;font-size:1.75rem}.vp-catalog-main-title:first-child{margin-bottom:.5rem!important}.vp-catalog-main-title:only-child{margin-bottom:0!important}.vp-catalog-main-title .vp-link{text-decoration:none!important}.vp-catalog-child-title{margin-bottom:.5rem!important}.vp-catalog-child-title.has-children{margin-top:calc(.5rem - var(--navbar-height, 3.6rem));padding-top:var(--navbar-height, 3.6rem);border-bottom:1px solid var(--catalog-border-color);font-weight:500;font-size:1.3rem;transition:border-color .3s}.vp-catalog-child-title.has-children:only-child{margin-bottom:0!important}.vp-catalog-child-title .vp-link{text-decoration:none!important}.vp-catalog-sub-title{font-weight:500;font-size:1.1rem}.vp-catalog-sub-title:only-child{margin-bottom:0!important}.vp-catalog-title{color:inherit;text-decoration:none}.vp-catalog-title:hover{color:var(--catalog-active-color)}.vp-child-catalogs{margin:0}.vp-child-catalog{list-style-type:disc}.vp-sub-catalogs{counter-reset:sub-catalog}.vp-sub-catalog{counter-increment:sub-catalog}.vp-sub-catalog .vp-link:before{content:counter(catalog-item) "." counter(child-catalog) "." counter(sub-catalog) " "}.vp-sub-catalogs-wrapper{display:flex;flex-wrap:wrap}.vp-sub-catalog-link{display:inline-block;margin:4px 8px;padding:4px 8px;border-radius:6px;background-color:var(--catalog-bg-secondary-color);line-height:1.5;overflow-wrap:break-word;transition:background-color .3s,color .3s}.vp-sub-catalog-link:hover{background-color:var(--catalog-hover-color);color:var(--catalog-bg-color);text-decoration:none!important}.vp-catalog-header-anchor{font-size:.85em;float:left;margin-left:-1em;padding-right:0;margin-top:.125em;opacity:0;-webkit-user-select:none;-moz-user-select:none;user-select:none;text-decoration:none;content:"¶"}@media print{.vp-catalog-header-anchor{display:none}}h2:hover .vp-catalog-header-anchor,h3:hover .vp-catalog-header-anchor{opacity:1;text-decoration:none}.vp-catalog-header-anchor:focus-visible{opacity:1}.vp-empty-catalog{font-size:1.25rem;text-align:center}:root{--catalog-bg-color: #fff;--catalog-bg-secondary-color: #f8f8f8;--catalog-border-color: #e5e5e5;--catalog-active-color: #3eaf7c;--catalog-hover-color: #71cda3}.lang-modal-fade-enter-active,.lang-modal-fade-leave-active{transition:opacity .5s}.lang-modal-fade-enter,.lang-modal-fade-leave-to{opacity:0}.lang-modal-mask{position:fixed;top:0;right:0;bottom:0;left:0;z-index:var(--redirect-z-index);display:flex;align-items:center;justify-content:center;-webkit-backdrop-filter:blur(10px);backdrop-filter:blur(10px)}@media print{.lang-modal-mask{display:none}}.lang-modal-wrapper{position:relative;z-index:1500;overflow:hidden;max-width:80vw;padding:1rem 2rem;border-radius:8px;background:var(--redirect-bg-color);box-shadow:0 2px 6px 0 var(--card-shadow)}.lang-modal-action{display:block;width:100%;margin:1rem 0;padding:.5rem .75rem;border:none;border-radius:8px;background-color:var(--redirect-bg-color-lighter);color:inherit;cursor:pointer}.lang-modal-action:hover{background-color:var(--redirect-bg-color-light)}.lang-modal-action.primary{background-color:var(--redirect-primary-color);color:var(--redirect-primary-text-color)}.lang-modal-action.primary:hover{background-color:var(--redirect-primary-hover-color)}:root{--redirect-z-index: 1499;--redirect-bg-color: #fff;--redirect-bg-color-light: #f3f4f5;--redirect-bg-color-lighter: #eeeeee;--redirect-text-color: #2c3e50;--redirect-primary-bg-color: #3eaf7c;--redirect-primary-hover-bg-color: #4abf8a;--redirect-primary-text-color: #fff}:root{--search-bg-color: #ffffff;--search-accent-color: #3eaf7c;--search-text-color: #2c3e50;--search-border-color: #eaecef;--search-item-text-color: #5d81a5;--search-item-focus-bg-color: #f3f4f5;--search-input-width: 8rem;--search-result-width: 20rem}.search-box{display:inline-block;position:relative;margin-left:1rem}@media print{.search-box{display:none}}.search-box input{-webkit-appearance:none;-moz-appearance:none;appearance:none;cursor:text;width:var(--search-input-width);height:2rem;color:var(--search-text-color);display:inline-block;border:1px solid var(--search-border-color);border-radius:2rem;font-size:.9rem;line-height:2rem;padding:0 .5rem 0 2rem;outline:none;transition:all ease .3s;background:var(--search-bg-color) url("data:image/svg+xml,%3c?xml%20version='1.0'%20encoding='UTF-8'?%3e%3csvg%20xmlns='http://www.w3.org/2000/svg'%20width='12'%20height='13'%3e%3cg%20stroke-width='2'%20stroke='%23aaa'%20fill='none'%3e%3cpath%20d='M11.29%2011.71l-4-4'/%3e%3ccircle%20cx='5'%20cy='5'%20r='4'/%3e%3c/g%3e%3c/svg%3e") .6rem .5rem no-repeat;background-size:1rem}@media (max-width: 719px){.search-box input{cursor:pointer;width:0;border-color:transparent;position:relative}}.search-box input:focus{cursor:auto;border-color:var(--search-accent-color)}@media (max-width: 719px){.search-box input:focus{cursor:text;left:0;width:10rem}}@media (max-width: 419px){.search-box input:focus{width:8rem}}.search-box .suggestions{background:var(--search-bg-color);width:var(--search-result-width);position:absolute;top:2rem;right:0;border:1px solid var(--search-border-color);border-radius:6px;padding:.4rem;list-style-type:none}@media (max-width: 419px){.search-box .suggestions{width:calc(100vw - 4rem);right:-.5rem}}.search-box .suggestion{line-height:1.4;padding:.4rem .6rem;border-radius:4px;cursor:pointer}.search-box .suggestion a{white-space:normal;color:var(--search-item-text-color)}.search-box .suggestion.focus{background-color:var(--search-item-focus-bg-color)}.search-box .suggestion.focus a{color:var(--search-accent-color)}.search-box .suggestion .page-title{font-weight:600}.search-box .suggestion .page-header{font-size:.9em;margin-left:.25em}.npm-badge[data-v-c758b2a0]{margin-right:.5rem} diff --git a/assets/styles.html-Ab_-qJux.js b/assets/styles.html-Ab_-qJux.js new file mode 100644 index 0000000000..3a35c7bfe0 --- /dev/null +++ b/assets/styles.html-Ab_-qJux.js @@ -0,0 +1,263 @@ +import{_ as e,r as a,o as c,c as D,a as l,b as s,d as n,e as r}from"./app-DI49vYHA.js";const t={},i=s("h1",{id:"样式",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#样式"},[s("span",null,"样式")])],-1),y={href:"https://sass-lang.com/",target:"_blank",rel:"noopener noreferrer"},d=r(`

    用户可以通过 palette 文件 来自定义样式变量,还可以通过 style 文件 来添加额外的样式。

    Palette 文件

    Palette 文件的路径是 .vuepress/styles/palette.scss

    你可以利用它来覆盖默认主题的预定义 SASS 变量。

    点击查看 SASS 变量
    // responsive breakpoints
    +$MQNarrow: 959px !default;
    +$MQMobile: 719px !default;
    +$MQMobileNarrow: 419px !default;
    +

    Style 文件

    Style 文件的路径是 .vuepress/styles/index.scss

    你可以在这里添加额外的样式,或者覆盖默认样式:

    :root {
    +  scroll-behavior: smooth;
    +}
    +

    你也可以利用它来覆盖默认主题的预定义 CSS 变量。

    点击查看 CSS 变量
    :root {
    +  // brand colors
    +  --c-brand: #3eaf7c;
    +  --c-brand-light: #4abf8a;
    +
    +  // background colors
    +  --c-bg: #ffffff;
    +  --c-bg-light: #f3f4f5;
    +  --c-bg-lighter: #eeeeee;
    +  --c-bg-dark: #ebebec;
    +  --c-bg-darker: #e6e6e6;
    +  --c-bg-navbar: var(--c-bg);
    +  --c-bg-sidebar: var(--c-bg);
    +  --c-bg-arrow: #cccccc;
    +
    +  // text colors
    +  --c-text: #2c3e50;
    +  --c-text-accent: var(--c-brand);
    +  --c-text-light: #3a5169;
    +  --c-text-lighter: #4e6e8e;
    +  --c-text-lightest: #6a8bad;
    +  --c-text-quote: #999999;
    +
    +  // border colors
    +  --c-border: #eaecef;
    +  --c-border-dark: #dfe2e5;
    +
    +  // custom container colors
    +  --c-tip: #42b983;
    +  --c-tip-bg: var(--c-bg-light);
    +  --c-tip-title: var(--c-text);
    +  --c-tip-text: var(--c-text);
    +  --c-tip-text-accent: var(--c-text-accent);
    +  --c-warning: #ffc310;
    +  --c-warning-bg: #fffae3;
    +  --c-warning-bg-light: #fff3ba;
    +  --c-warning-bg-lighter: #fff0b0;
    +  --c-warning-border-dark: #f7dc91;
    +  --c-warning-details-bg: #fff5ca;
    +  --c-warning-title: #f1b300;
    +  --c-warning-text: #746000;
    +  --c-warning-text-accent: #edb100;
    +  --c-warning-text-light: #c1971c;
    +  --c-warning-text-quote: #ccab49;
    +  --c-danger: #f11e37;
    +  --c-danger-bg: #ffe0e0;
    +  --c-danger-bg-light: #ffcfde;
    +  --c-danger-bg-lighter: #ffc9c9;
    +  --c-danger-border-dark: #f1abab;
    +  --c-danger-details-bg: #ffd4d4;
    +  --c-danger-title: #ed1e2c;
    +  --c-danger-text: #660000;
    +  --c-danger-text-accent: #bd1a1a;
    +  --c-danger-text-light: #b5474d;
    +  --c-danger-text-quote: #c15b5b;
    +  --c-details-bg: #eeeeee;
    +
    +  // badge component colors
    +  --c-badge-tip: var(--c-tip);
    +  --c-badge-warning: #ecc808;
    +  --c-badge-warning-text: var(--c-bg);
    +  --c-badge-danger: #dc2626;
    +  --c-badge-danger-text: var(--c-bg);
    +
    +  // code group colors
    +  --c-code-group-tab-title: rgba(255, 255, 255, 0.9);
    +  --c-code-group-tab-bg: var(--code-bg-color);
    +  --c-code-group-tab-outline: var(var(--c-code-group-tab-title));
    +  --c-code-group-tab-active-border: var(--c-brand);
    +
    +  // transition vars
    +  --t-color: 0.3s ease;
    +  --t-transform: 0.3s ease;
    +
    +  // code blocks vars
    +  --code-bg-color: #282c34;
    +  --code-hl-bg-color: rgba(0, 0, 0, 0.66);
    +  --code-ln-color: #9e9e9e;
    +  --code-ln-wrapper-width: 3.5rem;
    +
    +  // font vars
    +  --font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen,
    +    Ubuntu, Cantarell, 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif;
    +  --font-family-code: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace;
    +
    +  // layout vars
    +  --navbar-height: 3.6rem;
    +  --navbar-padding-v: 0.7rem;
    +  --navbar-padding-h: 1.5rem;
    +  --sidebar-width: 20rem;
    +  --sidebar-width-mobile: calc(var(--sidebar-width) * 0.82);
    +  --content-width: 740px;
    +  --homepage-width: 960px;
    +}
    +
    +// plugin-back-to-top
    +.vp-back-to-top-button {
    +  --back-to-top-color: var(--c-brand);
    +  --back-to-top-color-hover: var(--c-brand-light);
    +  --back-to-top-bg-color: var(--c-bg);
    +}
    +
    +// plugin-catalog
    +.vp-catalog-wrapper {
    +  --catalog-bg-color: var(--c-bg);
    +  --catalog-bg-secondary-color: var(--c-bg-dark);
    +  --catalog-border-color: var(--c-border);
    +  --catalog-active-color: var(--c-brand);
    +  --catalog-hover-color: var(--c-brand-light);
    +}
    +
    +// plugin-docsearch
    +.DocSearch {
    +  --docsearch-primary-color: var(--c-brand);
    +  --docsearch-text-color: var(--c-text);
    +  --docsearch-highlight-color: var(--c-brand);
    +  --docsearch-muted-color: var(--c-text-quote);
    +  --docsearch-container-background: rgba(9, 10, 17, 0.8);
    +  --docsearch-modal-background: var(--c-bg-light);
    +  --docsearch-searchbox-background: var(--c-bg-lighter);
    +  --docsearch-searchbox-focus-background: var(--c-bg);
    +  --docsearch-searchbox-shadow: inset 0 0 0 2px var(--c-brand);
    +  --docsearch-hit-color: var(--c-text-light);
    +  --docsearch-hit-active-color: var(--c-bg);
    +  --docsearch-hit-background: var(--c-bg);
    +  --docsearch-hit-shadow: 0 1px 3px 0 var(--c-border-dark);
    +  --docsearch-footer-background: var(--c-bg);
    +}
    +
    +// plugin-external-link-icon
    +.external-link-icon {
    +  --external-link-icon-color: var(--c-text-quote);
    +}
    +
    +// plugin-medium-zoom
    +.medium-zoom-overlay {
    +  --medium-zoom-bg-color: var(--c-bg);
    +}
    +
    +// plugin-nprogress
    +#nprogress {
    +  --nprogress-color: var(--c-brand);
    +}
    +
    +// plugin-photo-swipe
    +body {
    +  --photo-swipe-bullet: var(--c-bg);
    +  --photo-swipe-bullet-active: var(--c-brand);
    +}
    +
    +// plugin-pwa-popup
    +html {
    +  --pwa-text-color: var(--c-text);
    +  --pwa-bg-color: var(--c-bg);
    +  --pwa-border-color: var(--c-brand);
    +  --pwa-btn-text-color: var(--c-bg);
    +  --pwa-btn-bg-color: var(--c-brand);
    +  --pwa-btn-hover-bg-color: var(--c-brand-light);
    +}
    +
    +html.dark {
    +  --pwa-shadow-color: rgb(0 0 0 / 30%);
    +  --pwa-content-color: #ccc;
    +  --pwa-content-light-color: #999;
    +}
    +
    +// plugin-redirect
    +.language-modal-mask {
    +  --redirect-bg-color: var(--c-bg);
    +  --redirect-bg-color-light: var(--c-bg-light);
    +  --redirect-bg-color-lighter: var(--c-bg-lighter);
    +  --redirect-text-color: var(--c-text);
    +  --redirect-primary-bg-color: var(--c-brand);
    +  --redirect-primary-hover-bg-color: var(--c-brand-light);
    +  --redirect-primary-text-color: var(--c-bg);
    +}
    +
    +// plugin-search
    +.search-box {
    +  --search-bg-color: var(--c-bg);
    +  --search-accent-color: var(--c-brand);
    +  --search-text-color: var(--c-text);
    +  --search-border-color: var(--c-border);
    +
    +  --search-item-text-color: var(--c-text-lighter);
    +  --search-item-focus-bg-color: var(--c-bg-light);
    +}
    +
    点击查看暗黑模式 CSS 变量
    html.dark {
    +  // brand colors
    +  --c-brand: #3aa675;
    +  --c-brand-light: #349469;
    +
    +  // background colors
    +  --c-bg: #22272e;
    +  --c-bg-light: #2b313a;
    +  --c-bg-lighter: #262c34;
    +  --c-bg-dark: #343b44;
    +  --c-bg-darker: #37404c;
    +
    +  // text colors
    +  --c-text: #adbac7;
    +  --c-text-light: #96a7b7;
    +  --c-text-lighter: #8b9eb0;
    +  --c-text-lightest: #8094a8;
    +
    +  // border colors
    +  --c-border: #3e4c5a;
    +  --c-border-dark: #34404c;
    +
    +  // custom container colors
    +  --c-tip: #318a62;
    +  --c-warning: #e0ad15;
    +  --c-warning-bg: #2d2f2d;
    +  --c-warning-bg-light: #423e2a;
    +  --c-warning-bg-lighter: #44442f;
    +  --c-warning-border-dark: #957c35;
    +  --c-warning-details-bg: #39392d;
    +  --c-warning-title: #fdca31;
    +  --c-warning-text: #d8d96d;
    +  --c-warning-text-accent: #ffbf00;
    +  --c-warning-text-light: #ddb84b;
    +  --c-warning-text-quote: #ccab49;
    +  --c-danger: #fc1e38;
    +  --c-danger-bg: #39232c;
    +  --c-danger-bg-light: #4b2b35;
    +  --c-danger-bg-lighter: #553040;
    +  --c-danger-border-dark: #a25151;
    +  --c-danger-details-bg: #482936;
    +  --c-danger-title: #fc2d3b;
    +  --c-danger-text: #ea9ca0;
    +  --c-danger-text-accent: #fd3636;
    +  --c-danger-text-light: #d9777c;
    +  --c-danger-text-quote: #d56b6b;
    +  --c-details-bg: #323843;
    +
    +  // badge component colors
    +  --c-badge-warning: var(--c-warning);
    +  --c-badge-warning-text: #3c2e05;
    +  --c-badge-danger: var(--c-danger);
    +  --c-badge-danger-text: #401416;
    +
    +  // code blocks vars
    +  --code-hl-bg-color: #363b46;
    +}
    +
    +// plugin-docsearch
    +html.dark .DocSearch {
    +  --docsearch-logo-color: var(--c-text);
    +  --docsearch-modal-shadow: inset 1px 1px 0 0 #2c2e40, 0 3px 8px 0 #000309;
    +  --docsearch-key-shadow: inset 0 -2px 0 0 #282d55, inset 0 0 1px 1px #51577d,
    +    0 2px 2px 0 rgba(3, 4, 9, 0.3);
    +  --docsearch-key-gradient: linear-gradient(-225deg, #444950, #1c1e21);
    +  --docsearch-footer-shadow: inset 0 1px 0 0 rgba(73, 76, 106, 0.5),
    +    0 -4px 8px 0 rgba(0, 0, 0, 0.2);
    +}
    +
    `,12);function C(v,b){const p=a("NpmBadge"),o=a("ExternalLinkIcon");return c(),D("div",null,[i,l(p,{package:"@vuepress/theme-default"}),s("p",null,[n("默认主题使用 "),s("a",y,[n("SASS"),l(o)]),n(" 作为 CSS 预处理器。")]),d])}const u=e(t,[["render",C],["__file","styles.html.vue"]]),m=JSON.parse('{"path":"/zh/themes/default/styles.html","title":"样式","lang":"zh-CN","frontmatter":{},"headers":[{"level":2,"title":"Palette 文件","slug":"palette-文件","link":"#palette-文件","children":[]},{"level":2,"title":"Style 文件","slug":"style-文件","link":"#style-文件","children":[]}],"git":{"updatedTime":1706605723000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":1}]},"filePathRelative":"zh/themes/default/styles.md"}');export{u as comp,m as data}; diff --git a/assets/styles.html-DfDnb8qj.js b/assets/styles.html-DfDnb8qj.js new file mode 100644 index 0000000000..1603025a1f --- /dev/null +++ b/assets/styles.html-DfDnb8qj.js @@ -0,0 +1,263 @@ +import{_ as o,r as a,o as c,c as D,a as l,b as s,d as n,e as r}from"./app-DI49vYHA.js";const t={},i=s("h1",{id:"styles",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#styles"},[s("span",null,"Styles")])],-1),y={href:"https://sass-lang.com/",target:"_blank",rel:"noopener noreferrer"},d=r(`

    Users can customize style variables via a palette file, and add extra styles via a style file.

    Palette File

    The path of the palette file is .vuepress/styles/palette.scss.

    You can make use of it to override predefined SASS variables of the default theme.

    Click to expand SASS variables
    // responsive breakpoints
    +$MQNarrow: 959px !default;
    +$MQMobile: 719px !default;
    +$MQMobileNarrow: 419px !default;
    +

    Style File

    The path of the style file is .vuepress/styles/index.scss.

    You can add extra styles here, or override the default styles:

    :root {
    +  scroll-behavior: smooth;
    +}
    +

    You can also make use of it to override predefined CSS variables of the default theme.

    Click to expand CSS variables
    :root {
    +  // brand colors
    +  --c-brand: #3eaf7c;
    +  --c-brand-light: #4abf8a;
    +
    +  // background colors
    +  --c-bg: #ffffff;
    +  --c-bg-light: #f3f4f5;
    +  --c-bg-lighter: #eeeeee;
    +  --c-bg-dark: #ebebec;
    +  --c-bg-darker: #e6e6e6;
    +  --c-bg-navbar: var(--c-bg);
    +  --c-bg-sidebar: var(--c-bg);
    +  --c-bg-arrow: #cccccc;
    +
    +  // text colors
    +  --c-text: #2c3e50;
    +  --c-text-accent: var(--c-brand);
    +  --c-text-light: #3a5169;
    +  --c-text-lighter: #4e6e8e;
    +  --c-text-lightest: #6a8bad;
    +  --c-text-quote: #999999;
    +
    +  // border colors
    +  --c-border: #eaecef;
    +  --c-border-dark: #dfe2e5;
    +
    +  // custom container colors
    +  --c-tip: #42b983;
    +  --c-tip-bg: var(--c-bg-light);
    +  --c-tip-title: var(--c-text);
    +  --c-tip-text: var(--c-text);
    +  --c-tip-text-accent: var(--c-text-accent);
    +  --c-warning: #ffc310;
    +  --c-warning-bg: #fffae3;
    +  --c-warning-bg-light: #fff3ba;
    +  --c-warning-bg-lighter: #fff0b0;
    +  --c-warning-border-dark: #f7dc91;
    +  --c-warning-details-bg: #fff5ca;
    +  --c-warning-title: #f1b300;
    +  --c-warning-text: #746000;
    +  --c-warning-text-accent: #edb100;
    +  --c-warning-text-light: #c1971c;
    +  --c-warning-text-quote: #ccab49;
    +  --c-danger: #f11e37;
    +  --c-danger-bg: #ffe0e0;
    +  --c-danger-bg-light: #ffcfde;
    +  --c-danger-bg-lighter: #ffc9c9;
    +  --c-danger-border-dark: #f1abab;
    +  --c-danger-details-bg: #ffd4d4;
    +  --c-danger-title: #ed1e2c;
    +  --c-danger-text: #660000;
    +  --c-danger-text-accent: #bd1a1a;
    +  --c-danger-text-light: #b5474d;
    +  --c-danger-text-quote: #c15b5b;
    +  --c-details-bg: #eeeeee;
    +
    +  // badge component colors
    +  --c-badge-tip: var(--c-tip);
    +  --c-badge-warning: #ecc808;
    +  --c-badge-warning-text: var(--c-bg);
    +  --c-badge-danger: #dc2626;
    +  --c-badge-danger-text: var(--c-bg);
    +
    +  // code group colors
    +  --c-code-group-tab-title: rgba(255, 255, 255, 0.9);
    +  --c-code-group-tab-bg: var(--code-bg-color);
    +  --c-code-group-tab-outline: var(var(--c-code-group-tab-title));
    +  --c-code-group-tab-active-border: var(--c-brand);
    +
    +  // transition vars
    +  --t-color: 0.3s ease;
    +  --t-transform: 0.3s ease;
    +
    +  // code blocks vars
    +  --code-bg-color: #282c34;
    +  --code-hl-bg-color: rgba(0, 0, 0, 0.66);
    +  --code-ln-color: #9e9e9e;
    +  --code-ln-wrapper-width: 3.5rem;
    +
    +  // font vars
    +  --font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen,
    +    Ubuntu, Cantarell, 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif;
    +  --font-family-code: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace;
    +
    +  // layout vars
    +  --navbar-height: 3.6rem;
    +  --navbar-padding-v: 0.7rem;
    +  --navbar-padding-h: 1.5rem;
    +  --sidebar-width: 20rem;
    +  --sidebar-width-mobile: calc(var(--sidebar-width) * 0.82);
    +  --content-width: 740px;
    +  --homepage-width: 960px;
    +}
    +
    +// plugin-back-to-top
    +.vp-back-to-top-button {
    +  --back-to-top-color: var(--c-brand);
    +  --back-to-top-color-hover: var(--c-brand-light);
    +  --back-to-top-bg-color: var(--c-bg);
    +}
    +
    +// plugin-catalog
    +.vp-catalog-wrapper {
    +  --catalog-bg-color: var(--c-bg);
    +  --catalog-bg-secondary-color: var(--c-bg-dark);
    +  --catalog-border-color: var(--c-border);
    +  --catalog-active-color: var(--c-brand);
    +  --catalog-hover-color: var(--c-brand-light);
    +}
    +
    +// plugin-docsearch
    +.DocSearch {
    +  --docsearch-primary-color: var(--c-brand);
    +  --docsearch-text-color: var(--c-text);
    +  --docsearch-highlight-color: var(--c-brand);
    +  --docsearch-muted-color: var(--c-text-quote);
    +  --docsearch-container-background: rgba(9, 10, 17, 0.8);
    +  --docsearch-modal-background: var(--c-bg-light);
    +  --docsearch-searchbox-background: var(--c-bg-lighter);
    +  --docsearch-searchbox-focus-background: var(--c-bg);
    +  --docsearch-searchbox-shadow: inset 0 0 0 2px var(--c-brand);
    +  --docsearch-hit-color: var(--c-text-light);
    +  --docsearch-hit-active-color: var(--c-bg);
    +  --docsearch-hit-background: var(--c-bg);
    +  --docsearch-hit-shadow: 0 1px 3px 0 var(--c-border-dark);
    +  --docsearch-footer-background: var(--c-bg);
    +}
    +
    +// plugin-external-link-icon
    +.external-link-icon {
    +  --external-link-icon-color: var(--c-text-quote);
    +}
    +
    +// plugin-medium-zoom
    +.medium-zoom-overlay {
    +  --medium-zoom-bg-color: var(--c-bg);
    +}
    +
    +// plugin-nprogress
    +#nprogress {
    +  --nprogress-color: var(--c-brand);
    +}
    +
    +// plugin-photo-swipe
    +body {
    +  --photo-swipe-bullet: var(--c-bg);
    +  --photo-swipe-bullet-active: var(--c-brand);
    +}
    +
    +// plugin-pwa-popup
    +html {
    +  --pwa-text-color: var(--c-text);
    +  --pwa-bg-color: var(--c-bg);
    +  --pwa-border-color: var(--c-brand);
    +  --pwa-btn-text-color: var(--c-bg);
    +  --pwa-btn-bg-color: var(--c-brand);
    +  --pwa-btn-hover-bg-color: var(--c-brand-light);
    +}
    +
    +html.dark {
    +  --pwa-shadow-color: rgb(0 0 0 / 30%);
    +  --pwa-content-color: #ccc;
    +  --pwa-content-light-color: #999;
    +}
    +
    +// plugin-redirect
    +.language-modal-mask {
    +  --redirect-bg-color: var(--c-bg);
    +  --redirect-bg-color-light: var(--c-bg-light);
    +  --redirect-bg-color-lighter: var(--c-bg-lighter);
    +  --redirect-text-color: var(--c-text);
    +  --redirect-primary-bg-color: var(--c-brand);
    +  --redirect-primary-hover-bg-color: var(--c-brand-light);
    +  --redirect-primary-text-color: var(--c-bg);
    +}
    +
    +// plugin-search
    +.search-box {
    +  --search-bg-color: var(--c-bg);
    +  --search-accent-color: var(--c-brand);
    +  --search-text-color: var(--c-text);
    +  --search-border-color: var(--c-border);
    +
    +  --search-item-text-color: var(--c-text-lighter);
    +  --search-item-focus-bg-color: var(--c-bg-light);
    +}
    +
    Click to expand dark mode CSS variables
    html.dark {
    +  // brand colors
    +  --c-brand: #3aa675;
    +  --c-brand-light: #349469;
    +
    +  // background colors
    +  --c-bg: #22272e;
    +  --c-bg-light: #2b313a;
    +  --c-bg-lighter: #262c34;
    +  --c-bg-dark: #343b44;
    +  --c-bg-darker: #37404c;
    +
    +  // text colors
    +  --c-text: #adbac7;
    +  --c-text-light: #96a7b7;
    +  --c-text-lighter: #8b9eb0;
    +  --c-text-lightest: #8094a8;
    +
    +  // border colors
    +  --c-border: #3e4c5a;
    +  --c-border-dark: #34404c;
    +
    +  // custom container colors
    +  --c-tip: #318a62;
    +  --c-warning: #e0ad15;
    +  --c-warning-bg: #2d2f2d;
    +  --c-warning-bg-light: #423e2a;
    +  --c-warning-bg-lighter: #44442f;
    +  --c-warning-border-dark: #957c35;
    +  --c-warning-details-bg: #39392d;
    +  --c-warning-title: #fdca31;
    +  --c-warning-text: #d8d96d;
    +  --c-warning-text-accent: #ffbf00;
    +  --c-warning-text-light: #ddb84b;
    +  --c-warning-text-quote: #ccab49;
    +  --c-danger: #fc1e38;
    +  --c-danger-bg: #39232c;
    +  --c-danger-bg-light: #4b2b35;
    +  --c-danger-bg-lighter: #553040;
    +  --c-danger-border-dark: #a25151;
    +  --c-danger-details-bg: #482936;
    +  --c-danger-title: #fc2d3b;
    +  --c-danger-text: #ea9ca0;
    +  --c-danger-text-accent: #fd3636;
    +  --c-danger-text-light: #d9777c;
    +  --c-danger-text-quote: #d56b6b;
    +  --c-details-bg: #323843;
    +
    +  // badge component colors
    +  --c-badge-warning: var(--c-warning);
    +  --c-badge-warning-text: #3c2e05;
    +  --c-badge-danger: var(--c-danger);
    +  --c-badge-danger-text: #401416;
    +
    +  // code blocks vars
    +  --code-hl-bg-color: #363b46;
    +}
    +
    +// plugin-docsearch
    +html.dark .DocSearch {
    +  --docsearch-logo-color: var(--c-text);
    +  --docsearch-modal-shadow: inset 1px 1px 0 0 #2c2e40, 0 3px 8px 0 #000309;
    +  --docsearch-key-shadow: inset 0 -2px 0 0 #282d55, inset 0 0 1px 1px #51577d,
    +    0 2px 2px 0 rgba(3, 4, 9, 0.3);
    +  --docsearch-key-gradient: linear-gradient(-225deg, #444950, #1c1e21);
    +  --docsearch-footer-shadow: inset 0 1px 0 0 rgba(73, 76, 106, 0.5),
    +    0 -4px 8px 0 rgba(0, 0, 0, 0.2);
    +}
    +
    `,12);function C(v,b){const p=a("NpmBadge"),e=a("ExternalLinkIcon");return c(),D("div",null,[i,l(p,{package:"@vuepress/theme-default"}),s("p",null,[n("The default theme uses "),s("a",y,[n("SASS"),l(e)]),n(" as the CSS pre-processor.")]),d])}const u=o(t,[["render",C],["__file","styles.html.vue"]]),m=JSON.parse('{"path":"/themes/default/styles.html","title":"Styles","lang":"en-US","frontmatter":{},"headers":[{"level":2,"title":"Palette File","slug":"palette-file","link":"#palette-file","children":[]},{"level":2,"title":"Style File","slug":"style-file","link":"#style-file","children":[]}],"git":{"updatedTime":1706605723000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":1}]},"filePathRelative":"themes/default/styles.md"}');export{u as comp,m as data}; diff --git a/assets/theme-data.html-CH0nXsCv.js b/assets/theme-data.html-CH0nXsCv.js new file mode 100644 index 0000000000..a618ea2c3b --- /dev/null +++ b/assets/theme-data.html-CH0nXsCv.js @@ -0,0 +1,51 @@ +import{_ as o,r as e,o as t,c as i,a as n,b as s,d as a,w as c,e as r}from"./app-DI49vYHA.js";const d={},D=s("h1",{id:"theme-data",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#theme-data"},[s("span",null,"theme-data")])],-1),u=r(`

    This plugin is mainly used to develop themes, and has been integrated into the default theme. You won't need to use it directly in most cases.

    For theme authors, this plugin will help you to use the same i18n mechanism as VuePress and the default theme. But if you don't want to provide i18n support, or you want to implement in your own way, you don't need this plugin.

    Usage

    npm i -D @vuepress/plugin-theme-data@next
    +
    import { themeDataPlugin } from '@vuepress/plugin-theme-data'
    +
    +export default {
    +  plugins: [
    +    themeDataPlugin({
    +      // options
    +    }),
    +  ],
    +}
    +

    Options

    themeData

    • Type: ThemeData

    • Default: {}

    • Details:

      The theme data object that you want to use in client side.

      You can provide theme data in Node side via this option, and use it in client side via useThemeData and useThemeLocaleData.

    • Example:

    export default {
    +  plugins: [
    +    themeDataPlugin({
    +      themeData: {
    +        foo: 'foo',
    +        locales: {
    +          '/zh/': {
    +            foo: 'zh-foo',
    +          },
    +        },
    +      },
    +    }),
    +  ],
    +}
    +

    WARNING

    The theme data object will be processed by JSON.stringify() before forwarding to client side, so you should ensure that you are providing a JSON-friendly object.

    Composition API

    useThemeData

    • Details:

      Returns the theme data ref object.

      The value is provided by themeData option.

    • Example:

    import { useThemeData } from '@vuepress/plugin-theme-data/client'
    +import type { ThemeData } from '@vuepress/plugin-theme-data/client'
    +
    +type MyThemeData = ThemeData<{
    +  foo: string
    +}>
    +
    +export default {
    +  setup() {
    +    const themeData = useThemeData<MyThemeData>()
    +    console.log(themeData.value)
    +  },
    +}
    +

    useThemeLocaleData

    • Details:

      Returns the theme data ref object in current locale.

      The properties of current locale has been merged into the root-level properties.

    • Example:

    import { useThemeLocaleData } from '@vuepress/plugin-theme-data/client'
    +import type { ThemeData } from '@vuepress/plugin-theme-data/client'
    +
    +type MyThemeData = ThemeData<{
    +  foo: string
    +}>
    +
    +export default {
    +  setup() {
    +    const themeLocaleData = useThemeLocaleData<MyThemeData>()
    +    console.log(themeLocaleData.value)
    +  },
    +}
    +
    `,17);function m(y,h){const l=e("NpmBadge"),p=e("RouteLink");return t(),i("div",null,[D,n(l,{package:"@vuepress/plugin-theme-data"}),s("p",null,[a("Provide client data for your theme, with VuePress "),n(p,{to:"/guide/i18n.html"},{default:c(()=>[a("i18n")]),_:1}),a(" support.")]),u])}const C=o(d,[["render",m],["__file","theme-data.html.vue"]]),b=JSON.parse('{"path":"/plugins/theme-data.html","title":"theme-data","lang":"en-US","frontmatter":{},"headers":[{"level":2,"title":"Usage","slug":"usage","link":"#usage","children":[]},{"level":2,"title":"Options","slug":"options","link":"#options","children":[{"level":3,"title":"themeData","slug":"themedata","link":"#themedata","children":[]}]},{"level":2,"title":"Composition API","slug":"composition-api","link":"#composition-api","children":[{"level":3,"title":"useThemeData","slug":"usethemedata","link":"#usethemedata","children":[]},{"level":3,"title":"useThemeLocaleData","slug":"usethemelocaledata","link":"#usethemelocaledata","children":[]}]}],"git":{"updatedTime":1706605723000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":1}]},"filePathRelative":"plugins/theme-data.md"}');export{C as comp,b as data}; diff --git a/assets/theme-data.html-D41xVb4s.js b/assets/theme-data.html-D41xVb4s.js new file mode 100644 index 0000000000..bb06351d48 --- /dev/null +++ b/assets/theme-data.html-D41xVb4s.js @@ -0,0 +1,51 @@ +import{_ as o,r as n,o as t,c as i,a as e,b as s,d as a,w as c,e as r}from"./app-DI49vYHA.js";const D={},d=s("h1",{id:"theme-data",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#theme-data"},[s("span",null,"theme-data")])],-1),m=r(`

    该插件主要用于开发主题,并且已经集成到默认主题中。大部分情况下你不需要直接使用它。

    对于主题作者,该插件可以提供与 VuePress 及默认主题相同的多语言支持机制。但是如果你的主题不需要提供多语言支持,或者你想用你自己的方式来实现多语言支持,那么你不需要使用该插件。

    使用方法

    npm i -D @vuepress/plugin-theme-data@next
    +
    import { themeDataPlugin } from '@vuepress/plugin-theme-data'
    +
    +export default {
    +  plugins: [
    +    themeDataPlugin({
    +      // 配置项
    +    }),
    +  ],
    +}
    +

    配置项

    themeData

    • 类型: ThemeData

    • 默认值: {}

    • 详情:

      你希望在 Client 端中使用的主题数据对象。

      你可以通过该配置项,在 Node 端提供主题数据,然后在客户端通过 useThemeDatauseThemeLocaleData 来使用主题数据。

    • 示例:

    export default {
    +  plugins: [
    +    themeDataPlugin({
    +      themeData: {
    +        foo: 'foo',
    +        locales: {
    +          '/zh/': {
    +            foo: 'zh-foo',
    +          },
    +        },
    +      },
    +    }),
    +  ],
    +}
    +

    注意

    主题数据对象在传递到客户端之前,会使用 JSON.stringify() 进行处理,因此你需要保证你提供的是一个可以被 JSON 序列化的对象。

    Composition API

    useThemeData

    • 详情:

      返回主题数据的 Ref 对象。

      数据是通过 themeData 配置项提供的。

    • 示例:

    import { useThemeData } from '@vuepress/plugin-theme-data/client'
    +import type { ThemeData } from '@vuepress/plugin-theme-data/client'
    +
    +type MyThemeData = ThemeData<{
    +  foo: string
    +}>
    +
    +export default {
    +  setup() {
    +    const themeData = useThemeData<MyThemeData>()
    +    console.log(themeData.value)
    +  },
    +}
    +

    useThemeLocaleData

    • 详情:

      返回当前 locale 下主题数据的 Ref 对象。

      当前 locale 中的字段已被合并到顶层字段中。

    • 示例:

    import { useThemeLocaleData } from '@vuepress/plugin-theme-data/client'
    +import type { ThemeData } from '@vuepress/plugin-theme-data/client'
    +
    +type MyThemeData = ThemeData<{
    +  foo: string
    +}>
    +
    +export default {
    +  setup() {
    +    const themeLocaleData = useThemeLocaleData<MyThemeData>()
    +    console.log(themeLocaleData.value)
    +  },
    +}
    +
    `,17);function u(y,v){const l=n("NpmBadge"),p=n("RouteLink");return t(),i("div",null,[d,e(l,{package:"@vuepress/plugin-theme-data"}),s("p",null,[a("为你的主题提供客户端数据,包含 VuePress 的 "),e(p,{to:"/guide/i18n.html"},{default:c(()=>[a("多语言支持")]),_:1}),a(" 。")]),m])}const C=o(D,[["render",u],["__file","theme-data.html.vue"]]),b=JSON.parse('{"path":"/zh/plugins/theme-data.html","title":"theme-data","lang":"zh-CN","frontmatter":{},"headers":[{"level":2,"title":"使用方法","slug":"使用方法","link":"#使用方法","children":[]},{"level":2,"title":"配置项","slug":"配置项","link":"#配置项","children":[{"level":3,"title":"themeData","slug":"themedata","link":"#themedata","children":[]}]},{"level":2,"title":"Composition API","slug":"composition-api","link":"#composition-api","children":[{"level":3,"title":"useThemeData","slug":"usethemedata","link":"#usethemedata","children":[]},{"level":3,"title":"useThemeLocaleData","slug":"usethemelocaledata","link":"#usethemelocaledata","children":[]}]}],"git":{"updatedTime":1706605723000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":1}]},"filePathRelative":"zh/plugins/theme-data.md"}');export{C as comp,b as data}; diff --git a/assets/toc.html-B5VRzXZc.js b/assets/toc.html-B5VRzXZc.js new file mode 100644 index 0000000000..56996487bf --- /dev/null +++ b/assets/toc.html-B5VRzXZc.js @@ -0,0 +1,74 @@ +import{_ as i,r as p,o as c,c as r,a,b as n,d as s,w as e,e as o}from"./app-DI49vYHA.js";const d={},D=n("h1",{id:"toc",tabindex:"-1"},[n("a",{class:"header-anchor",href:"#toc"},[n("span",null,"toc")])],-1),u=o(`

    This plugin will provide a table-of-contents (TOC) component.

    Usage

    npm i -D @vuepress/plugin-toc@next
    +
    import { tocPlugin } from '@vuepress/plugin-toc'
    +
    +export default {
    +  plugins: [
    +    tocPlugin({
    +      // options
    +    }),
    +  ],
    +}
    +

    Differences with Markdown TOC Syntax

    `,5),y=o(`
    <!-- markdown toc syntax -->
    +
    +[[toc]]
    +
    +<!-- vue toc component -->
    +<Toc />
    +

    Both of them can be pre-rendered correctly in build mode. However, there are some differences between them.

    The markdown syntax [[toc]] could only be used in markdown files. It is parsed by markdown-it, and the generated TOC is static content.

    The component <Toc/> could be used in both markdown files and vue files. It is loaded by vue, and the generated TOC is a vue component.

    `,4),v=n("code",null,"linkClass",-1),m=n("code",null,"linkActiveClass",-1),C=o(`

    Therefore, this plugin is more useful for theme developers.

    Options

    componentName

    • Type: string

    • Default: 'Toc'

    • Details:

      Specify the name of the TOC component.

    defaultPropsOptions

    • Type: Partial<TocPropsOptions>

    • Default: {}

    • Details:

      Override the default values of the component options prop.

    Component Props

    The TOC component also accepts props for customization.

    <template>
    +  <Toc :headers="headers" :options="options" />
    +</template>
    +

    headers

    • Type: PageHeader[]
    interface PageHeader {
    +  level: number
    +  title: string
    +  slug: string
    +  children: PageHeader[]
    +}
    +
    • Details:

      Specify the headers array to render.

      If this prop is not specified, the headers of current page will be used.

    options

    • Type: Partial<TocPropsOptions>
    interface TocPropsOptions {
    +  containerTag: string
    +  containerClass: string
    +  listClass: string
    +  itemClass: string
    +  linkTag: 'a' | 'RouterLink' | 'RouteLink'
    +  linkClass: string
    +  linkActiveClass: string
    +  linkChildrenActiveClass: string
    +}
    +
    const defaultOptions = {
    +  containerTag: 'nav',
    +  containerClass: 'vuepress-toc',
    +  listClass: 'vuepress-toc-list',
    +  itemClass: 'vuepress-toc-item',
    +  linkTag: 'RouteLink',
    +  linkClass: 'vuepress-toc-link',
    +  linkActiveClass: 'active',
    +  linkChildrenActiveClass: 'active',
    +}
    +
    • Details:

      Customize the TOC component.

      If the containerTag is set to an empty string '', the <nav> container will be removed totally.

    • Example:

      The rendered TOC component with default options looks like:

    <template>
    +  <!-- container -->
    +  <nav class="vuepress-toc">
    +    <!-- list -->
    +    <ul class="vuepress-toc-list">
    +      <!-- item -->
    +      <li class="vuepress-toc-item">
    +        <!-- link -->
    +        <RouteLink class="vuepress-toc-link" to="#foo">Foo</RouteLink>
    +      </li>
    +      <!-- item with children -->
    +      <li class="vuepress-toc-item">
    +        <!-- link (children active) -->
    +        <RouteLink class="vuepress-toc-link active" to="#bar">Bar</RouteLink>
    +        <!-- list (children) -->
    +        <ul class="vuepress-toc-list">
    +          <!-- item -->
    +          <li class="vuepress-toc-item">
    +            <!-- link (active) -->
    +            <RouteLink class="vuepress-toc-link active" to="#bar-child">
    +              Bar Child
    +            </RouteLink>
    +          </li>
    +        </ul>
    +      </li>
    +    </ul>
    +  </nav>
    +</template>
    +
    `,20);function h(b,g){const t=p("NpmBadge"),l=p("RouteLink");return c(),r("div",null,[D,a(t,{package:"@vuepress/plugin-toc"}),u,n("p",null,[s("Similar to the "),a(l,{to:"/guide/markdown.html#table-of-contents"},{default:e(()=>[s("Table of Contents Markdown Syntax")]),_:1}),s(", the TOC component that provided by this plugin could be used in your markdown content directly:")]),y,n("p",null,[s("This plugin could work together with "),a(l,{to:"/plugins/active-header-links.html"},{default:e(()=>[s("@vuepress/plugin-active-header-links")]),_:1}),s(" by setting the "),a(l,{to:"/plugins/active-header-links.html#headerlinkselector"},{default:e(()=>[s("headerLinkSelector")]),_:1}),s(" to match the "),v,s(" option. When the page scroll to a certain header anchor, this corresponding link will be added "),m,s(" class name.")]),C])}const k=i(d,[["render",h],["__file","toc.html.vue"]]),f=JSON.parse('{"path":"/plugins/toc.html","title":"toc","lang":"en-US","frontmatter":{},"headers":[{"level":2,"title":"Usage","slug":"usage","link":"#usage","children":[]},{"level":2,"title":"Differences with Markdown TOC Syntax","slug":"differences-with-markdown-toc-syntax","link":"#differences-with-markdown-toc-syntax","children":[]},{"level":2,"title":"Options","slug":"options","link":"#options","children":[{"level":3,"title":"componentName","slug":"componentname","link":"#componentname","children":[]},{"level":3,"title":"defaultPropsOptions","slug":"defaultpropsoptions","link":"#defaultpropsoptions","children":[]}]},{"level":2,"title":"Component Props","slug":"component-props","link":"#component-props","children":[{"level":3,"title":"headers","slug":"headers","link":"#headers","children":[]},{"level":3,"title":"options","slug":"options-1","link":"#options-1","children":[]}]}],"git":{"updatedTime":1707119730000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":4}]},"filePathRelative":"plugins/toc.md"}');export{k as comp,f as data}; diff --git a/assets/toc.html-CHxAKS6q.js b/assets/toc.html-CHxAKS6q.js new file mode 100644 index 0000000000..9b3af8f7f7 --- /dev/null +++ b/assets/toc.html-CHxAKS6q.js @@ -0,0 +1,74 @@ +import{_ as i,r as o,o as c,c as r,a as l,b as n,d as s,w as e,e as p}from"./app-DI49vYHA.js";const d={},D=n("h1",{id:"toc",tabindex:"-1"},[n("a",{class:"header-anchor",href:"#toc"},[n("span",null,"toc")])],-1),u=p(`

    该插件会提供一个目录 (table-of-contents, TOC) 组件。

    使用方法

    npm i -D @vuepress/plugin-toc@next
    +
    import { tocPlugin } from '@vuepress/plugin-toc'
    +
    +export default {
    +  plugins: [
    +    tocPlugin({
    +      // 配置项
    +    }),
    +  ],
    +}
    +

    与 Markdown 目录语法的区别

    `,5),y=p(`
    <!-- Markdown 目录语法 -->
    +
    +[[toc]]
    +
    +<!-- Vue 目录组件 -->
    +<Toc />
    +

    在 Build 模式中,它们都可以被正确地预渲染。然而,它们之间存在一些区别。

    Markdown 语法 [[toc]] 仅能在 Markdown 文件中使用。它是由 markdown-it 解析的,生成的目录是静态内容。

    组件 <Toc/> 既可以用在 Markdown 文件中,也可以用在 Vue 文件中。它是由 Vue 加载的,生成的目录是一个 Vue 组件。

    `,4),v=n("code",null,"linkClass",-1),C=n("code",null,"linkActiveClass",-1),m=p(`

    因此,该插件对于主题开发者来说更为有用。

    配置项

    componentName

    • 类型: string

    • 默认值: 'Toc'

    • 详情:

      指定目录组件的名称。

    defaultPropsOptions

    • 类型: Partial<TocPropsOptions>

    • 默认值: {}

    • 详情:

      覆盖组件 options Prop 的默认值。

    组件 Props

    目录组件可以通过 Props 来进行自定义。

    <template>
    +  <Toc :headers="headers" :options="options" />
    +</template>
    +

    headers

    • 类型: PageHeader[]
    interface PageHeader {
    +  level: number
    +  title: string
    +  slug: string
    +  children: PageHeader[]
    +}
    +
    • 详情:

      指定要渲染的标题数组。

      如果该 Prop 没有被设置,默认会使用当前页面的标题。

    options

    • 类型: Partial<TocPropsOptions>
    interface TocPropsOptions {
    +  containerTag: string
    +  containerClass: string
    +  listClass: string
    +  itemClass: string
    +  linkTag: 'a' | 'RouterLink' | 'RouteLink'
    +  linkClass: string
    +  linkActiveClass: string
    +  linkChildrenActiveClass: string
    +}
    +
    const defaultOptions = {
    +  containerTag: 'nav',
    +  containerClass: 'vuepress-toc',
    +  listClass: 'vuepress-toc-list',
    +  itemClass: 'vuepress-toc-item',
    +  linkTag: 'RouteLink',
    +  linkClass: 'vuepress-toc-link',
    +  linkActiveClass: 'active',
    +  linkChildrenActiveClass: 'active',
    +}
    +
    • 详情:

      自定义目录组件。

      如果 containerTag 设置为空字符串 '' ,那么最外层的 <nav> Container 会被完全移除。

    • 示例:

      使用默认 options 的目录组件的渲染结果类似以下结构:

    <template>
    +  <!-- container -->
    +  <nav class="vuepress-toc">
    +    <!-- list -->
    +    <ul class="vuepress-toc-list">
    +      <!-- item -->
    +      <li class="vuepress-toc-item">
    +        <!-- link -->
    +        <RouteLink class="vuepress-toc-link" to="#foo">Foo</RouteLink>
    +      </li>
    +      <!-- item with children -->
    +      <li class="vuepress-toc-item">
    +        <!-- link (children active) -->
    +        <RouteLink class="vuepress-toc-link active" to="#bar">Bar</RouteLink>
    +        <!-- list (children) -->
    +        <ul class="vuepress-toc-list">
    +          <!-- item -->
    +          <li class="vuepress-toc-item">
    +            <!-- link (active) -->
    +            <RouteLink class="vuepress-toc-link active" to="#bar-child">
    +              Bar Child
    +            </RouteLink>
    +          </li>
    +        </ul>
    +      </li>
    +    </ul>
    +  </nav>
    +</template>
    +
    `,20);function b(h,g){const t=o("NpmBadge"),a=o("RouteLink");return c(),r("div",null,[D,l(t,{package:"@vuepress/plugin-toc"}),u,n("p",null,[s("与 "),l(a,{to:"/guide/markdown.html#%E7%9B%AE%E5%BD%95"},{default:e(()=>[s("Markdown 目录语法")]),_:1}),s(" 类似,该插件提供的目录组件可以直接在你的 Markdown 内容中使用:")]),y,n("p",null,[s("该插件可以和 "),l(a,{to:"/zh/plugins/active-header-links.html"},{default:e(()=>[s("@vuepress/plugin-active-header-links")]),_:1}),s(" 协同工作,你只需要将 "),l(a,{to:"/zh/plugins/active-header-links.html#headerlinkselector"},{default:e(()=>[s("headerLinkSelector")]),_:1}),s(" 与该插件的 "),v,s(" 匹配即可。当页面滚动至某个标题锚点后,对应的链接就会被加上 "),C,s(" 类名。")]),m])}const k=i(d,[["render",b],["__file","toc.html.vue"]]),F=JSON.parse('{"path":"/zh/plugins/toc.html","title":"toc","lang":"zh-CN","frontmatter":{},"headers":[{"level":2,"title":"使用方法","slug":"使用方法","link":"#使用方法","children":[]},{"level":2,"title":"与 Markdown 目录语法的区别","slug":"与-markdown-目录语法的区别","link":"#与-markdown-目录语法的区别","children":[]},{"level":2,"title":"配置项","slug":"配置项","link":"#配置项","children":[{"level":3,"title":"componentName","slug":"componentname","link":"#componentname","children":[]},{"level":3,"title":"defaultPropsOptions","slug":"defaultpropsoptions","link":"#defaultpropsoptions","children":[]}]},{"level":2,"title":"组件 Props","slug":"组件-props","link":"#组件-props","children":[{"level":3,"title":"headers","slug":"headers","link":"#headers","children":[]},{"level":3,"title":"options","slug":"options","link":"#options","children":[]}]}],"git":{"updatedTime":1707119730000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":4}]},"filePathRelative":"zh/plugins/toc.md"}');export{k as comp,F as data}; diff --git a/favicon.ico b/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..e481e5dda71327b885d4797cac93939d8030d0d6 GIT binary patch literal 15086 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 T8@2H(qeWd=>ve^qb@=}Sfyh#R literal 0 HcmV?d00001 diff --git a/images/hero.png b/images/hero.png new file mode 100644 index 0000000000000000000000000000000000000000..ac6beaff060d4e383169f86b007e13ef7313d393 GIT binary patch literal 153793 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%0Aq;h1Y(#@xNill5DXwfr+?5g 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 N20Es&IxXkN{{t)V3w8hi literal 0 HcmV?d00001 diff --git a/images/icons/android-chrome-384x384.png b/images/icons/android-chrome-384x384.png new file mode 100644 index 0000000000000000000000000000000000000000..86e1fd58b3f6add80bcbd795214aa540b8023ba4 GIT binary patch literal 15528 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 literal 0 HcmV?d00001 diff --git a/images/icons/apple-touch-icon.png b/images/icons/apple-touch-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..208915f1dea6694cf2b6cd9824f5036a4508a715 GIT binary patch literal 5373 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 L9hC|ttMLB;KPvf_ literal 0 HcmV?d00001 diff --git a/images/icons/favicon-16x16.png b/images/icons/favicon-16x16.png new file mode 100644 index 0000000000000000000000000000000000000000..ca5047e7b8f2d6abe0063329af60eec70a1ca4fa GIT binary patch literal 806 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 literal 0 HcmV?d00001 diff --git a/images/icons/safari-pinned-tab.svg b/images/icons/safari-pinned-tab.svg new file mode 100644 index 0000000000..dc0b992c04 --- /dev/null +++ b/images/icons/safari-pinned-tab.svg @@ -0,0 +1,23 @@ + + + + +Created by potrace 1.11, written by Peter Selinger 2001-2013 + + + + + diff --git a/images/logo.png b/images/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..60e17006ad07cd313788585e2f5e528fbb7cf821 GIT binary patch literal 3451 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 literal 0 HcmV?d00001 diff --git a/index.html b/index.html new file mode 100644 index 0000000000..27898b33f5 --- /dev/null +++ b/index.html @@ -0,0 +1,37 @@ + + + + + + + + + Home | VuePress Ecosystem + + + + + +

    + + + diff --git a/manifest.webmanifest b/manifest.webmanifest new file mode 100644 index 0000000000..d2e935f1ae --- /dev/null +++ b/manifest.webmanifest @@ -0,0 +1,21 @@ +{ + "name": "VuePress", + "short_name": "VuePress", + "description": "Vue-powered Static Site Generator", + "start_url": "/index.html", + "display": "standalone", + "background_color": "#fff", + "theme_color": "#3eaf7c", + "icons": [ + { + "src": "/images/icons/android-chrome-192x192.png", + "sizes": "192x192", + "type": "image/png" + }, + { + "src": "/images/icons/android-chrome-384x384.png", + "sizes": "384x384", + "type": "image/png" + } + ] +} diff --git a/plugins/active-header-links.html b/plugins/active-header-links.html new file mode 100644 index 0000000000..fcff7c9758 --- /dev/null +++ b/plugins/active-header-links.html @@ -0,0 +1,47 @@ + + + + + + + + + active-header-links | VuePress Ecosystem + + + + + +

    active-header-links

    @vuepress/plugin-active-header-links

    This plugin will listen to page scroll event. When the page scrolls to a certain header anchor, this plugin will change the route hash to that header anchor if there is a corresponding header link.

    This plugin is mainly used to develop themes, and has been integrated into the default theme. You won't need to use it directly in most cases.

    Usage

    npm i -D @vuepress/plugin-active-header-links@next
    +
    import { activeHeaderLinksPlugin } from '@vuepress/plugin-active-header-links'
    +
    +export default {
    +  plugins: [
    +    activeHeaderLinksPlugin({
    +      // options
    +    }),
    +  ],
    +}
    +

    Options

    headerLinkSelector

    • Type: string

    • Default: 'a.sidebar-item'

    • Details:

      Selector of header link.

      If a header anchor does not have a corresponding header link, this plugin won't change the route hash to that anchor when scrolling to it.

    headerAnchorSelector

    delay

    • Type: number

    • Default: 200

    • Details:

      The delay of the debounced scroll event listener.

    offset

    • Type: number

    • Default: 5

    • Details:

      Even if you click the link of the header anchor directly, the scrollTop might not be exactly equal to offsetTop of the header anchor, so we add an offset to avoid the error.

    + + + diff --git a/plugins/back-to-top.html b/plugins/back-to-top.html new file mode 100644 index 0000000000..24d29271ac --- /dev/null +++ b/plugins/back-to-top.html @@ -0,0 +1,51 @@ + + + + + + + + + back-to-top | VuePress Ecosystem + + + + + +

    back-to-top

    @vuepress/plugin-back-to-top

    This plugin will add a back to top button to your site. The button will be displayed in the bottom right corner of the page when scrolling down. By clicking the button, the page will scroll to the top.

    This plugin has been integrated into the default theme.

    Usage

    npm i -D @vuepress/plugin-back-to-top@next
    +
    import { backToTopPlugin } from '@vuepress/plugin-back-to-top'
    +
    +export default {
    +  plugins: [backToTopPlugin()],
    +}
    +

    Options

    threshold

    • Type: number
    • Default: 100
    • Details: Scroll threshold distance to display back to top button (in pixels)

    progress

    • Type: boolean
    • Default: true
    • Details: Whether display progress bar around icon

    Styles

    You can customize the style of the back to top button via CSS variables:

    :root {
    +  --back-to-top-z-index: 5;
    +  --back-to-top-icon: url("back-to-top.svg");
    +  --back-to-top-bg-color: #fff;
    +  --back-to-top-color: #3eaf7c;
    +  --back-to-top-color-hover: #71cda3;
    +  --back-to-top-shadow: rgb(0 0 0 / 20%);
    +}
    +
    + + + diff --git a/plugins/baidu-analytics.html b/plugins/baidu-analytics.html new file mode 100644 index 0000000000..615cc5263d --- /dev/null +++ b/plugins/baidu-analytics.html @@ -0,0 +1,47 @@ + + + + + + + + + baidu-analytics | VuePress Ecosystem + + + + + +

    baidu-analytics

    @vuepress/plugin-baidu-analytics

    Integrate Baidu Analyticsopen in new window into VuePress.

    Usage

    npm i -D @vuepress/plugin-baidu-analytics@next
    +
    import { baiduAnalyticsPlugin } from '@vuepress/plugin-baidu-analytics'
    +
    +export default {
    +  plugins: [
    +    baiduAnalyticsPlugin({
    +      // options
    +    }),
    +  ],
    +}
    +

    Reporting Events

    After using this plugin, the global hmt array is available on the window object, and you can use it for custom events reportingopen in new window.

    Options

    id

    • Type: string

    • Details: The ID of Baidu Analytics, which is the query of hm.js URL.

    + + + diff --git a/plugins/blog/config.html b/plugins/blog/config.html new file mode 100644 index 0000000000..922ba0ce32 --- /dev/null +++ b/plugins/blog/config.html @@ -0,0 +1,179 @@ + + + + + + + + + Config | VuePress Ecosystem + + + + + +

    Config

    Plugin Options

    getInfo

    • Type: (page: Page) => Record<string, unknown>

    • Required: No

    • Reference:

    • Details:

      Function getting article info.

      Article info will be injected in route meta so that they will be available later in client composables.

    filter

    • Type: (page: Page) => boolean

    • Default: (page) => Boolean(page.filePathRelative) && !page.frontmatter.home

    • Reference:

    • Details:

      Page filter, determine whether a page should be included.

      By default, all the pages generated from Markdown files but not homepage will be included as articles.

    category

    type

    slugify

    • Type: (name: string) => string
    • Default: (name) => name.replace(/ _/g, '-').replace(/[:?*|\\/<>]/g, "").toLowerCase()
    • Details: Slugify function, used to convert key name which they are register in routes.

    excerpt

    excerptSeparator

    excerptLength

    • Type: number

    • Default: 300

    • Reference:

    • Details:

      Length of excerpt when auto generating.

      TIP

      Excerpt length will be the minimal possible length reaching this value.

      You can set it to 0 to disable auto excerpt generation.

    excerptFilter

    • Type: (page: Page) => boolean

    • Default: filter option

    • Reference:

    • Details:

      Page filter, determine whether the plugin should generate excerpt for it.

      TIP

      You should use this to skip pages that you don't need to generate excerpt for. E.g.: If users set excerpt or description in frontmatter, you may want to use them directly.

    isCustomElement

    • Type: (tagName: string) => boolean

    • Default: () => false

    • Reference:

    • Details:

      Tags which is considered as custom elements.

      This is used to determine whether a tag is a custom element since all unknown tags are removed in excerpt.

    metaScope

    • Type: string

    • Default: "_blog"

    • Details:

      Key used when injecting info to route meta.

      TIP

      Setting to an empty key will inject to route meta directly instead of a field.

    hotReload

    • Type: boolean

    • Default: Whether using --debug flag

    • Details:

      Whether enable hotReload in devServer.

      To theme developers

      It's disabled by default because it does have performance impact in sites with a lot of categories and types. And it can slow down hotReload speed when editing Markdown.

      If users are adding or organizing your categories or tags, you may tell them to enable this, for the rest it's better to keep it disabled.

      Also, you can try to detect number of pages in users project and decide whether to enable it.

    Blog Category Config

    Blog category config should be an array, while each item is controlling a "category" rule.

    interface BlogCategoryOptions {
    +  /**
    +   * Unique category name
    +   */
    +  key: string
    +
    +  /**
    +   * Function getting category from page
    +   */
    +  getter: (page: Page) => string[]
    +
    +  /**
    +   * A custom function to sort the pages
    +   */
    +  sorter?: (pageA: Page, pageB: Page) => number
    +
    +  /**
    +   * Path pattern of page to be registered
    +   *
    +   * @description `:key` will be replaced by the "slugify" result of the original key
    +   *
    +   * @default `/:key/`
    +   */
    +  path?: string
    +
    +  /**
    +   * Page layout name
    +   *
    +   * @default 'Layout'
    +   */
    +  layout?: string
    +
    +  /**
    +   * Frontmatter
    +   */
    +  frontmatter?: (localePath: string) => Record<string, string>
    +
    +  /**
    +   * Item page path pattern or custom function to be registered
    +   *
    +   * @description When filling in a string, `:key` and `:name` will be replaced by the "slugify" result of the original key and name
    +   *
    +   * @default `/:key/:name/`
    +   */
    +  itemPath?: string | ((name: string) => string)
    +
    +  /**
    +   * Item page layout name
    +   *
    +   * @default 'Layout'
    +   */
    +  itemLayout?: string
    +
    +  /**
    +   * Items Frontmatter
    +   */
    +  itemFrontmatter?: (name: string, localePath: string) => Record<string, string>
    +}
    +

    Blog Type Config

    Blog type config should be an array, while each item is controlling a "type" rule.

    interface BlogTypeOptions {
    +  /**
    +   * Unique type name
    +   */
    +  key: string
    +
    +  /**
    +   * A filter function to determine whether a page should be the type
    +   */
    +  filter: (page: Page) => boolean
    +
    +  /**
    +   * A custom function to sort the pages
    +   */
    +  sorter?: (pageA: Page, pageB: Page) => number
    +
    +  /**
    +   * Page path to be registered
    +   *
    +   * @default '/:key/'
    +   */
    +  path?: string
    +
    +  /**
    +   * Layout name
    +   *
    +   * @default 'Layout'
    +   */
    +  layout?: string
    +
    +  /**
    +   * Frontmatter
    +   */
    +  frontmatter?: (localePath: string) => Record<string, string>
    +}
    +

    Composition API

    You can import the following API from @vuepress/plugin-blog/client.

    • Blog category

      const useBlogCategory: <
      +  T extends Record<string, unknown> = Record<string, unknown>,
      +>(
      +  key?: string,
      +) => ComputedRef<BlogCategoryData<T>>
      +

      Argument key should be the category unique key.

      If no key is passed, the plugin will try to use the key in current path.

    • Blog category

      const useBlogType: <
      +  T extends Record<string, unknown> = Record<string, unknown>,
      +>(
      +  key?: string,
      +) => ComputedRef<BlogTypeData<T>>
      +

      Argument key should be the type unique key.

      If no key is passed, the plugin will try to use the key in current path.

    Returning values are:

    interface Article<T extends Record<string, unknown> = Record<string, unknown>> {
    +  /** Article path */
    +  path: string
    +  /** Article info */
    +  info: T
    +}
    +
    +interface BlogCategoryData<
    +  T extends Record<string, unknown> = Record<string, unknown>,
    +> {
    +  /** Category path */
    +  path: string
    +
    +  /**
    +   * Only available when current route matches an item path
    +   */
    +  currentItems?: Article<T>[]
    +
    +  /** Category map */
    +  map: {
    +    /** Unique key under current category */
    +    [key: string]: {
    +      /** Category path of the key */
    +      path: string
    +      /** Category items of the key */
    +      items: Article<T>[]
    +    }
    +  }
    +}
    +
    +interface BlogTypeData<
    +  T extends Record<string, unknown> = Record<string, unknown>,
    +> {
    +  /** Type path */
    +  path: string
    +
    +  /** Items under current type */
    +  items: Article<T>[]
    +}
    +
    + + + diff --git a/plugins/blog/guide.html b/plugins/blog/guide.html new file mode 100644 index 0000000000..1772c933d0 --- /dev/null +++ b/plugins/blog/guide.html @@ -0,0 +1,254 @@ + + + + + + + + + Guide | VuePress Ecosystem + + + + + +

    With @vuepress/plugin-blog, you can easily bring blog feature into your theme.

    Collecting Articles

    The plugin filters all pages using filter option to drop pages you don't want.

    By default, all pages generating from Markdown files but not homepage are considered as articles.

    You can fully customize pages to collect through option filter, which accepts a function (page: Page) => boolean.

    Gathering Info

    You should set getInfo option with a function accepting Page as argument and returning an object containing the info you want.

    The plugin will collect all the info you want and write them to routeMeta field of each page, so you will be able to get this information through Composition API later.

    Demo
    import { blogPlugin } from '@vuepress/plugin-blog'
    +
    +export default {
    +  name: 'vuepress-theme-xxx',
    +  plugins: [
    +    blogPlugin({
    +      filter: ({ filePathRelative, frontmatter }) => {
    +        // drop those pages which is NOT generated from file
    +        if (!filePathRelative) return false
    +
    +        // drop those pages in `archives` directory
    +        if (filePathRelative.startsWith('archives/')) return false
    +
    +        // drop those pages which do not use default layout
    +        if (frontmatter.home || frontmatter.layout) return false
    +
    +        return true
    +      },
    +
    +      getInfo: ({ frontmatter, git = {}, data = {} }) => {
    +        // getting page info
    +        const info: Record<string, any> = {
    +          author: frontmatter.author || '',
    +          categories: frontmatter.categories || [],
    +          date: frontmatter.date || git.createdTime || null,
    +          tags: frontmatter.tags || [],
    +          excerpt: data.excerpt || '',
    +        }
    +
    +        return info
    +      },
    +    }),
    +    // other plugins ...
    +  ],
    +}
    +

    Customizing Categories and Types

    Basically, you would want 2 types of collection in your blog:

    • Category:

      "Category" means grouping articles with their labels.

      For example, each article may have their "categories" and "tags".

    • Type:

      "Type" means identifying articles with conditions.

      For example, you may want to describe some of your articles as diary.

    After understanding description of these 2 types, you can set category and type options, each accepts an array, and each element represents a configuration.

    Let's start with 2 examples here.

    Imagine you are setting tags for each articles with tag field in page frontmatter. You want a tag mapping page in /tag/ with TagMap layout, and group each tag list with tagName in /tag/tagName with TagList layout, you probably need a configuration like this:

    import { blogPlugin } from '@vuepress/plugin-blog'
    +
    +export default {
    +  name: 'vuepress-theme-xxx',
    +  plugins: [
    +    blogPlugin({
    +      // other options ...
    +      category: [
    +        {
    +          key: 'tag',
    +          getter: ({ frontmatter }) => frontmatter.tag || [],
    +          path: '/tag/',
    +          layout: 'TagMap',
    +          frontmatter: () => ({ title: 'Tag page' }),
    +          itemPath: '/tag/:name/',
    +          itemLayout: 'TagList',
    +          itemFrontmatter: (name) => ({ title: `Tag ${name}` }),
    +        },
    +      ],
    +    }),
    +    // other plugins ...
    +  ],
    +}
    +

    Also, you may want to star some of your articles, and display them to visitors. When you are setting star: true in frontmatter to mark them, you probably need a configuration like this to display them in /star/ path with StarList layout:

    import { blogPlugin } from '@vuepress/plugin-blog'
    +
    +export default {
    +  name: 'vuepress-theme-xxx',
    +  plugins: [
    +    blogPlugin({
    +      // other options ...
    +      type: [
    +        {
    +          key: 'star',
    +          filter: ({ frontmatter }) => frontmatter.star,
    +          path: '/star/',
    +          layout: 'StarList',
    +          frontmatter: () => ({ title: 'Star page' }),
    +        },
    +      ],
    +    }),
    +    // other plugins ...
    +  ],
    +}
    +

    See, setting these 2 types is easy. For full options, please see Category Config and Type Config.

    Using Composition API in Client-side

    When generating each page, the plugin will set following information under frontmatter.blog:

    interface BlogFrontmatterOptions {
    +  /** Current type of the page */
    +  type: 'category' | 'type'
    +  /** Unique key under current category or tag */
    +  key: string
    +  /**
    +   * Current category name
    +   *
    +   * @description Only available in category item page
    +   */
    +  name?: string
    +}
    +

    So you can invoke useBlogCategory() and useBlogType() directly, and the result will be the category or type bind to current route.

    Also, you can pass key you want as argument, then you will get information bind to that key.

    So with node side settings above, you can get information about "tag" and "star" in client side:

    TagMap layout:

    <script setup lang="ts">
    +import { useBlogCategory } from '@vuepress/plugin-blog'
    +import { RouteLink } from 'vuepress/client'
    +
    +const categoryMap = useBlogCategory('tag')
    +</script>
    +
    +<template>
    +  <div>
    +    <h1>Tag page</h1>
    +    <ul>
    +      <li v-for="({ items, path }, name) in categoryMap.map">
    +        <RouteLink :key="name" :to="path" class="category">
    +          {{ name }}
    +          <span class="category-num">
    +            {{ items.length }}
    +          </span>
    +        </RouteLink>
    +      </li>
    +    </ul>
    +  </div>
    +</template>
    +

    TagList layout:

    <script setup lang="ts">
    +import { useBlogCategory } from '@vuepress/plugin-blog'
    +
    +const categoryMap = useBlogCategory('tag')
    +</script>
    +
    +<template>
    +  <div>
    +    <h1>Tag page</h1>
    +    <div class="category-wrapper">
    +      <RouteLink
    +        v-for="({ items, path }, name) in categoryMap.map"
    +        :key="name"
    +        :to="path"
    +        class="category"
    +      >
    +        {{ name }}
    +        <span class="category-num">
    +          {{ items.length }}
    +        </span>
    +      </RouteLink>
    +    </div>
    +    <div class="article-wrapper" v-if="categoryMap.currentItems">
    +      <div v-if="!categoryMap.currentItems.length">Nothing in here.</div>
    +      <article
    +        v-for="{ info, path } in categoryMap.currentItems"
    +        class="article"
    +        @click="$router.push(path)"
    +      >
    +        <header class="title">
    +          {{
    +            (isTimeline
    +              ? `${new Date(info.date).toLocaleDateString()}: `
    +              : '') + info.title
    +          }}
    +        </header>
    +        <hr />
    +        <div class="article-info">
    +          <span v-if="info.author" class="author"
    +            >Author: {{ info.author }}</span
    +          >
    +          <span v-if="info.date && !isTimeline" class="date"
    +            >Date: {{ new Date(info.date).toLocaleDateString() }}</span
    +          >
    +          <span v-if="info.category" class="category"
    +            >Category: {{ info.category.join(', ') }}</span
    +          >
    +          <span v-if="info.tag" class="tag"
    +            >Tag: {{ info.tag.join(', ') }}</span
    +          >
    +        </div>
    +        <div v-if="info.excerpt" class="excerpt" v-html="info.excerpt" />
    +      </article>
    +    </div>
    +  </div>
    +</template>
    +

    StarList layout:

    <script setup lang="ts">
    +import { useBlogType } from '@vuepress/plugin-blog/client'
    +import { RouteLink } from 'vuepress/client'
    +
    +import ArticleList from '../components/ArticleList.vue'
    +import ParentLayout from '@vuepress/theme-default/layouts/Layout.vue'
    +
    +const stars = useBlogType('star')
    +</script>
    +
    +<template>
    +  <div class="article-wrapper" v-if="stars.items">
    +    <div v-if="!stars.items.length">Nothing in here.</div>
    +    <article
    +      v-for="{ info, path } in stars.items"
    +      class="article"
    +      @click="$router.push(path)"
    +    >
    +      <header class="title">
    +        {{
    +          (isTimeline ? `${new Date(info.date).toLocaleDateString()}: ` : '') +
    +          info.title
    +        }}
    +      </header>
    +      <hr />
    +      <div class="article-info">
    +        <span v-if="info.author" class="author">Author: {{ info.author }}</span>
    +        <span v-if="info.date && !isTimeline" class="date"
    +          >Date: {{ new Date(info.date).toLocaleDateString() }}</span
    +        >
    +        <span v-if="info.category" class="category"
    +          >Category: {{ info.category.join(', ') }}</span
    +        >
    +        <span v-if="info.tag" class="tag">Tag: {{ info.tag.join(', ') }}</span>
    +      </div>
    +      <div v-if="info.excerpt" class="excerpt" v-html="info.excerpt" />
    +    </article>
    +  </div>
    +</template>
    +

    For return types, please see Composition API Return Types.

    I18n Support

    This plugin adds native i18n support, so your settings will be automatically applied to each language.

    For example, if user has the following locales' config, and you are setting the "star" example above:

    export default {
    +  locales: {
    +    '/': {
    +      lang: 'en-US',
    +    },
    +    '/zh/': {
    +      lang: 'zh-CN',
    +    },
    +  },
    +}
    +

    Then /zh/star/ and /star/ will both be available, and only articles under the correct locale will appear.

    Generating Excerpt

    This plugin provides a built-in excerpt generator, which can be enabled by setting excerpt option to true.

    Excerpt introduction

    An excerpt is an HTML fragment that is used to display a short description of an article in the blog list, so the excerpt has the following restrictions:

    • It doesn't support any unknown tags (including all Vue components) and Vue syntax, so these contents will be removed when generating. If you have custom components (non-Vue components), set isCustomElement option.
    • Since the snippet is an HTML fragment, you will not be able to import any images via relative paths or aliases, they will be removed directly. If you want to keep images, please use absolute path based on .vuepress/public or full URL to ensure they can be accessed in other places.

    The excerpt generator will try to find a valid excerpt separator from markdown contents, if it finds one, it will use content before the separator. The separator is default <!-- more -->, and you can customize it by setting excerptSeparator option.

    If it cannot find a valid separator, it will parse content from the beginning of markdown file, and stop till its length reaches a preset value. The value is default 300, and you can customize it by setting excerptLength option.

    To choose which page should generate excerpt, you can use excerptFilter option.

    Example

    Normally you may want to use frontmatter.description if users set them, so you can let filter function return false if frontmatter.description is not empty.

    + + + diff --git a/plugins/blog/index.html b/plugins/blog/index.html new file mode 100644 index 0000000000..baaeae180b --- /dev/null +++ b/plugins/blog/index.html @@ -0,0 +1,47 @@ + + + + + + + + + blog | VuePress Ecosystem + + + + + +

    blog

    @vuepress/plugin-blog

    Usage

    npm i -D @vuepress/plugin-blog@next
    +
    import { blogPlugin } from '@vuepress/plugin-blog'
    +
    +export default {
    +  plugins: [
    +    blogPlugin({
    +      // options
    +    }),
    +  ],
    +}
    +
    + + + diff --git a/plugins/catalog.html b/plugins/catalog.html new file mode 100644 index 0000000000..f40d95aa12 --- /dev/null +++ b/plugins/catalog.html @@ -0,0 +1,129 @@ + + + + + + + + + catalog | VuePress Ecosystem + + + + + +

    catalog

    @vuepress/plugin-catalog

    The plugin can automatically generate catalog pages and provide catalog components.

    Usage

    npm i -D @vuepress/plugin-catalog@next
    +
    import { catalogPlugin } from '@vuepress/plugin-catalog'
    +
    +export default {
    +  plugins: [
    +    catalogPlugin({
    +      // Your options
    +    }),
    +  ],
    +}
    +

    First, you should set catalog info in routeMeta:

    import { catalogPlugin } from '@vuepress/plugin-catalog'
    +
    +export default {
    +  extendsPage: (page) => {
    +    // set catalog info in routeMeta
    +    page.routeMeta = {
    +      // catalog title
    +      title: page.title,
    +      // ... other information
    +    }
    +  },
    +}
    +

    You can then import defineCatalogInfoGetter from @vuepress/plugin-catalog/client and use it in client config fileopen in new window to extract catalog info from meta.

    import { defineCatalogInfoGetter } from '@vuepress/plugin-catalog/client'
    +
    +export default {
    +  setup: () => {
    +    defineCatalogInfoGetter((meta) =>
    +      meta.title ? { title: meta.title } : null,
    +    )
    +  },
    +}
    +

    Catalog info should contains:

    • title: catalog title
    • order: catalog order (optional)
    • content: catalog content component (optional)

    Sorting with order

    The plugin will sort pages by order in the following way:

    // order positive numbers from small to large
    +Project with order 1
    +Project with order 2
    +...
    +Project with order 10
    +...
    +// Project without order
    +Project without order
    +Project without order
    +...
    +// order negative numbers from small to large
    +Project with order -10
    +// ...
    +Project with order -2
    +Project with order -1
    +

    Options

    level Built-in component only

    • Type: 1 | 2 | 3
    • Default: 3
    • Details: Max depth of catalog items.

    index Built-in component only

    • Type: boolean
    • Default: false
    • Details: Whether show index for catalog

    frontmatter

    • Type: (path: string) => Record<string, any>

    • Required: No

    • Details: Frontmatter getter for the generated page.

    • Example:

      import { catalogPlugin } from '@vuepress/plugin-catalog'
      +
      +export default {
      +  plugins: [
      +    catalogPlugin({
      +      frontmatter: (path) => ({
      +        // frontmatter you want
      +        // you may customize title, author. time, etc.
      +      }),
      +    }),
      +  ],
      +}
      +

    exclude

    • Type: (RegExp | string)[]

    • Default: []

    • Details:

      Catalog page path to be excluded during generation.

      • "/foo/" means only exclude catalog page generation at /foo/ folder.
      • /^\/foo\// means exclude catalog page generation at /foo/ folder and its subfolders.

      404 pages will be automatically excluded.

    component

    • Type: string
    • Required: No
    • Details: Component name to use as catalog.

    locales

    • Type: CatalogPluginLocaleConfig

      interface CatalogPluginLocaleData {
      +  /**
      +   * Catalog title
      +   */
      +  title: string
      +
      +  /**
      +   * Empty hint
      +   */
      +  empty: string
      +}
      +
      +interface CatalogPluginLocaleConfig {
      +  [localePath: string]: CatalogPluginLocaleData
      +}
      +
    • Required: No

    • Details: Locales config for catalog component.

    Built-in Supported Languages
    • Simplified Chinese (zh-CN)
    • Traditional Chinese (zh-TW)
    • English (United States) (en-US)
    • German (de-DE)
    • German (Australia) (de-AT)
    • Russian (ru-RU)
    • Ukrainian (uk-UA)
    • Vietnamese (vi-VN)
    • Portuguese (Brazil) (pt-BR)
    • Polish (pl-PL)
    • French (fr-FR)
    • Spanish (es-ES)
    • Slovak (sk-SK)
    • Japanese (ja-JP)
    • Turkish (tr-TR)
    • Korean (ko-KR)
    • Finnish (fi-FI)
    • Indonesian (id-ID)
    • Dutch (nl-NL)

    Client options

    defineCatalogInfoGetter

    interface CatalogInfo {
    +  /** Catalog title */
    +  title: string
    +  /** Catalog order */
    +  order?: number
    +  /** Catalog content */
    +  content?: Component
    +}
    +
    +type CatalogInfoGetter = (meta: Record<string, unknown>) => CatalogInfo | null
    +
    +const defineCatalogInfoGetter: (options: CatalogInfoGetter) => void
    +

    Customize how to extract catalog info from meta.

    Components

    Catalog

    • Details:

      The plugin will globally register a <Catalog /> component by default (unless you set the component option).

      You can use <Catalog /> in the theme layout or directly in the Markdown file.

      The component supports four props:

      • level: Change the display depth (maximum support 3 levels), default is 3.
      • base: Display catalog of the specified folder, default is the current folder directory.
      • index: Add an index number to the directory item, default is no number.
      • hideHeading: Hide the component title, default is to display the Catalog title.

    Styles

    You can customize the style of catalog via CSS variables:

    :root {
    +  --catalog-bg-color: #fff;
    +  --catalog-bg-secondary-color: #f8f8f8;
    +  --catalog-border-color: #e5e5e5;
    +  --catalog-active-color: #3eaf7c;
    +  --catalog-hover-color: #71cda3;
    +}
    +
    + + + diff --git a/plugins/container.html b/plugins/container.html new file mode 100644 index 0000000000..fd676e3af5 --- /dev/null +++ b/plugins/container.html @@ -0,0 +1,75 @@ + + + + + + + + + container | VuePress Ecosystem + + + + + +

    container

    @vuepress/plugin-container

    Register markdown custom containers in your VuePress site.

    This plugin simplifies the use of markdown-it-containeropen in new window, but also retains its original capabilities.

    The Custom Containers of default theme is powered by this plugin.

    Usage

    npm i -D @vuepress/plugin-container@next
    +
    import { containerPlugin } from '@vuepress/plugin-container'
    +
    +export default {
    +  plugins: [
    +    containerPlugin({
    +      // options
    +    }),
    +  ],
    +}
    +

    Container Syntax

    ::: <type> [info]
    +[content]
    +:::
    +
    • The type is required and should be specified via type option.
    • The info is optional, and the default value can be specified via defaultInfo in locales option.
    • The content can be any valid markdown content.

    TIP

    This plugin can be used multiple times to support different types of containers.

    Options

    type

    locales

    • Type: Record<string, { defaultInfo: string }>

    • Details:

      The default info of the container in different locales.

      If this option is not specified, the default info will fallback to the uppercase of the type option.

    • Example:

    export default {
    +  plugins: [
    +    containerPlugin({
    +      type: 'tip',
    +      locales: {
    +        '/': {
    +          defaultInfo: 'TIP',
    +        },
    +        '/zh/': {
    +          defaultInfo: '提示',
    +        },
    +      },
    +    }),
    +  ],
    +}
    +

    before

    • Type: (info: string) => string

    • Default:

    (info: string): string =>
    +  `<div class="custom-container ${type}">${info ? `<p class="custom-container-title">${info}</p>` : ''}\n`
    +
    • Details:

      A function to render the starting tag of the container.

      The first param is the info part of container syntax.

      This option will not take effect if you don't specify the after option.

    after

    • Type: (info: string) => string

    • Default:

    (): string => '</div>\n'
    +
    • Details:

      A function to render the ending tag of the container.

      The first param is the info part of container syntax.

      This option will not take effect if you don't specify the before option.

    render

    • Type:
    type MarkdownItContainerRenderFunction = (
    +  tokens: Token[],
    +  index: number,
    +  options: any,
    +  env: MarkdownEnv,
    +  self: Renderer,
    +) => string
    +

    validate

    marker

    + + + diff --git a/plugins/copy-code.html b/plugins/copy-code.html new file mode 100644 index 0000000000..b9aacfd2d4 --- /dev/null +++ b/plugins/copy-code.html @@ -0,0 +1,97 @@ + + + + + + + + + copy-code | VuePress Ecosystem + + + + + +

    copy-code

    @vuepress/plugin-copy-code

    This plugin will automatically add a copy button to the top right corner of each code block on PC devices.

    The default selector matches @vuepress/theme-default, so you might need to change it when integrating your own theme.

    Usage

    npm i -D @vuepress/plugin-copy-code@next
    +
    import { copyCodePlugin } from '@vuepress/plugin-copy-code'
    +
    +export default {
    +  plugins: [
    +    copyCodePlugin({
    +      // options
    +    }),
    +  ],
    +}
    +

    Options

    selector

    • Type: string | string[]

    • Default: '.theme-default-content div[class*="language-"] pre'

    • Details:

      Code block selector

    showInMobile

    • Type: boolean

    • Default: false

    • Details:

      Whether to display copy button on the mobile device

    duration

    • Type: number

    • Default: 2000

    • Details:

      Hint display time, setting it to 0 will disable the hint.

    delay

    • Type: number

    • Default: 800

    • Details:

      The delay of registering copy code buttons, in ms.

      If the theme you are using has a switching animation, it is recommended to configure this option to Switch animation duration + 200.

    locales

    • Type: CopyCodePluginLocaleConfig

      interface CopyCodePluginLocaleData {
      +  /**
      +   * Copy text
      +   */
      +  copy: string
      +
      +  /**
      +   * Copied text
      +   */
      +  copied: string
      +}
      +
      +interface CopyCodePluginLocaleConfig {
      +  [localePath: string]: CopyCodePluginLocaleData
      +}
      +
    • Required: No

    • Details:

      Locales config for copy code plugin.

    • Example:

      import { copyCodePlugin } from '@vuepress/plugin-copy-code'
      +
      +export default {
      +  locales: {
      +    '/': {
      +      // this is a supported language
      +      lang: 'en-US',
      +    },
      +    '/xx/': {
      +      // the plugin does not support this language
      +      lang: 'mm-NN',
      +    },
      +  },
      +
      +  plugins: [
      +    copyCodePlugin({
      +      locales: {
      +        '/': {
      +          // Override copy button label text
      +          copy: 'Copy Codes from code block',
      +        },
      +
      +        '/xx/': {
      +          // Complete locale config for `mm-NN` language here
      +        },
      +      },
      +    }),
      +  ],
      +}
      +
    Built-in Supported Languages
    • Simplified Chinese (zh-CN)
    • Traditional Chinese (zh-TW)
    • English (United States) (en-US)
    • German (de-DE)
    • German (Australia) (de-AT)
    • Russian (ru-RU)
    • Ukrainian (uk-UA)
    • Vietnamese (vi-VN)
    • Portuguese (Brazil) (pt-BR)
    • Polish (pl-PL)
    • French (fr-FR)
    • Spanish (es-ES)
    • Slovak (sk-SK)
    • Japanese (ja-JP)
    • Turkish (tr-TR)
    • Korean (ko-KR)
    • Finnish (fi-FI)
    • Indonesian (id-ID)
    • Dutch (nl-NL)

    Styles

    You can customize the icon of the copy button via CSS variables:

    :root {
    +  --code-copy-icon: url("copy-button.svg");
    +  --code-copied-icon: url("copied-button.svg");
    +  --copy-code-color: #9e9e9e;
    +  --copy-code-hover: rgb(0 0 0 / 50%);
    +}
    +
    + + + diff --git a/plugins/copyright.html b/plugins/copyright.html new file mode 100644 index 0000000000..860355f691 --- /dev/null +++ b/plugins/copyright.html @@ -0,0 +1,102 @@ + + + + + + + + + copyright | VuePress Ecosystem + + + + + +

    copyright

    @vuepress/plugin-copyright

    This plugin can automatically append copyright information when visitors copy content from your site, and can also prohibit site copying or selection.

    Usage

    npm i -D @vuepress/plugin-copyright@next
    +
    import { copyrightPlugin } from '@vuepress/plugin-copyright'
    +
    +export default {
    +  plugins: [
    +    copyrightPlugin({
    +      // options
    +    }),
    +  ],
    +}
    +

    This plugin is disabled globally by default, you can:

    • Manually enable it by setting copy: true in page frontmatter
    • Set global: true in plugin options to enable it globally, and set copy: false in page frontmatter to disable it.

    To avoid disturbing visitors, copyright information will be appended only when the copied content length is greater than 100. Set triggerLength in plugin options if you want to change this threshold, or via copy.triggerLength in page frontmatter.

    You can set default author and license information via author and license in plugin options.

    If your site have different authors and license in different pages, you can set authorGetter and licenseGetter with function (page: Page) => string that takes the current page object as parameter and returns the corresponding information.

    The plugin will generate copyright information from author, license, and page link via template by default, and append it when copying. If you think that this is not flexible enough, you can set copyrightGetter option to return a completely customized information with Page object or return null to use the default template.

    Disable Copy and Selection

    If you want to prevent users copying long content, you can set maxLength in plugin options to customize this limit, or via copy.maxLength in page frontmatter.

    • If you don't want users to copy your entire site or specific page text, you can set disableCopy in plugin options or copy.disableCopy in page frontmatter, the latter has higher priority.
    • If you don't want users to select your entire site or specific page text, you can set disableSelection in plugin options or copy.disableSelection in page frontmatter. This option has higher priority.

    Options

    author

    • Type: string
    • Details: Default author Information

    license

    • Type: string
    • Details: Default license Information

    authorGetter

    • Type: (page: Page) => string | null
    • Details: Author getter

    licenseGetter

    • Type: (page: Page) => string | null
    • Details: License getter

    copyrightGetter

    • Type: (page: Page) => string | null
    • Details: Copyright getter

    triggerLength

    • Type: number
    • Default: 100
    • Details: Min content length triggering copyright append

    maxLength

    • Type: number
    • Default: 0
    • Details: Max content length which allows to copy, 0 means no limit.

    global

    • Type: boolean
    • Default: false
    • Details: Whether enable globally.

    disableCopy

    • Type: boolean
    • Default: false
    • Details: Disable copy

    disableSelection

    • Type: boolean
    • Default: false
    • Details: Disable selection

    canonical

    • Type: string
    • Details: Canonical deploy location.

    Example

    If you are deploying same content under https://myblog.com and https://blog.com/username/, you may want to prefer one site as reference link.

    • If you prefer the first one, you should set canonical to https://myblog.com
    • If you prefer the second one, you should set canonical to https://blog.com/username/

    So copyright message triggered on another site also points to your preferred site.

    locales

    • Type: CopyrightPluginLocaleConfig

      interface CopyrightPluginLocaleData {
      +  /**
      +   * Author text
      +   *
      +   * @description `:author` will be replaced by author
      +   */
      +  author: string
      +
      +  /**
      +   * License text
      +   *
      +   * @description `:license` will be replaced by current license
      +   */
      +  license: string
      +
      +  /**
      +   * Link text
      +   *
      +   * @description `:link` will be replaced by current page link
      +   */
      +  link: string
      +}
      +
      +interface CopyrightPluginLocaleConfig {
      +  [localePath: string]: CopyrightPluginLocaleData
      +}
      +
    • Details: Locale config for copyright plugin.

    • Example:

      import { copyrightPlugin } from '@vuepress/plugin-copyright'
      +
      +export default {
      +  locales: {
      +    '/': {
      +      // this is a supported language
      +      lang: 'en-US',
      +    },
      +    '/xx/': {
      +      // the plugin does not support this language
      +      lang: 'mm-NN',
      +    },
      +  },
      +
      +  plugins: [
      +    copyrightPlugin({
      +      locales: {
      +        '/': {
      +          // Override link text
      +          link: 'Original posted at :link',
      +        },
      +
      +        '/xx/': {
      +          // Complete locale config for `mm-NN` language here
      +        },
      +      },
      +    }),
      +  ],
      +}
      +
    Built-in Supported Languages
    • Simplified Chinese (zh-CN)
    • Traditional Chinese (zh-TW)
    • English (United States) (en-US)
    • German (de-DE)
    • German (Australia) (de-AT)
    • Russian (ru-RU)
    • Ukrainian (uk-UA)
    • Vietnamese (vi-VN)
    • Portuguese (Brazil) (pt-BR)
    • Polish (pl-PL)
    • French (fr-FR)
    • Spanish (es-ES)
    • Slovak (sk-SK)
    • Japanese (ja-JP)
    • Turkish (tr-TR)
    • Korean (ko-KR)
    • Finnish (fi-FI)
    • Indonesian (id-ID)
    • Dutch (nl-NL)

    Frontmatter

    copy.triggerLength

    • Type: number
    • Default: 100
    • Details: Min content length triggering copyright append

    copy.maxLength

    • Type: number
    • Default: 0
    • Details: Max content length which allows to copy, 0 means no limit.

    copy.disableCopy

    • Type: boolean
    • Default: false
    • Details: Disable copy

    copy.disableSelection

    • Type: boolean
    • Default: false
    • Details: Disable selection
    + + + diff --git a/plugins/docsearch.html b/plugins/docsearch.html new file mode 100644 index 0000000000..0a39ab7071 --- /dev/null +++ b/plugins/docsearch.html @@ -0,0 +1,241 @@ + + + + + + + + + docsearch | VuePress Ecosystem + + + + + +

    docsearch

    @vuepress/plugin-docsearch

    Integrate Algolia DocSearchopen in new window into VuePress, which can provide search to your documentation site.

    TIP

    Default theme will add DocSearch to the navbar once you configure this plugin correctly.

    This plugin may not be used directly in other themes, so you'd better refer to the documentation of your theme for more details.

    Usage

    npm i -D @vuepress/plugin-docsearch@next
    +
    import { docsearchPlugin } from '@vuepress/plugin-docsearch'
    +
    +export default {
    +  plugins: [
    +    docsearchPlugin({
    +      // options
    +    }),
    +  ],
    +}
    +

    Get Search Index

    You need to submit the URL of your siteopen in new window to join the DocSearch program. The DocSearch team will send apiKey and indexName to your email once the index is generated. Then you can configure this plugin to enable DocSearch in VuePress.

    Alternatively, you can run your own crawleropen in new window to generate the index, and then use your own appId, apiKey and indexName to configure this plugin.

    Official crawler config
    new Crawler({
    +  appId: 'YOUR_APP_ID',
    +  apiKey: 'YOUR_API_KEY',
    +  rateLimit: 8,
    +  startUrls: [
    +    // These are urls which algolia start to craw
    +    // If your site is divided in to mutiple parts,
    +    // you may want to set mutiple entry links
    +    'https://YOUR_WEBSITE_URL/',
    +  ],
    +  sitemaps: [
    +    // if you are using sitemap plugins (e.g.: @vuepress-plugin/sitemap), you may provide one
    +    'https://YOUR_WEBSITE_URL/sitemap.xml',
    +  ],
    +  ignoreCanonicalTo: false,
    +  exclusionPatterns: [
    +    // You can use this to stop algolia crawing some paths
    +  ],
    +  discoveryPatterns: [
    +    // These are urls which algolia looking for,
    +    'https://YOUR_WEBSITE_URL/**',
    +  ],
    +  // Crawler schedule, set it according to your docs update frequency
    +  schedule: 'at 02:00 every 1 day',
    +  actions: [
    +    // you may have mutiple actions, especially when you are deploying mutiple docs under one domain
    +    {
    +      // name the index with name you like
    +      indexName: 'YOUR_INDEX_NAME',
    +      // paths where the index take effect
    +      pathsToMatch: ['https://YOUR_WEBSITE_URL/**'],
    +      // controls how algolia extracts records from your site
    +      recordExtractor: ({ $, helpers }) => {
    +        // options for @vuepress/theme-default
    +        return helpers.docsearch({
    +          recordProps: {
    +            lvl0: {
    +              selectors: '.sidebar-heading.active',
    +              defaultValue: 'Documentation',
    +            },
    +            lvl1: '.theme-default-content h1',
    +            lvl2: '.theme-default-content h2',
    +            lvl3: '.theme-default-content h3',
    +            lvl4: '.theme-default-content h4',
    +            lvl5: '.theme-default-content h5',
    +            lvl6: '.theme-default-content h6',
    +            content: '.theme-default-content p, .theme-default-content li',
    +          },
    +          indexHeadings: true,
    +        })
    +      },
    +    },
    +  ],
    +  initialIndexSettings: {
    +    // controls how index are initialized
    +    // only has effects before index are initialize
    +    // you may need to delete your index and recraw after modification
    +    YOUR_INDEX_NAME: {
    +      attributesForFaceting: ['type', 'lang'],
    +      attributesToRetrieve: ['hierarchy', 'content', 'anchor', 'url'],
    +      attributesToHighlight: ['hierarchy', 'hierarchy_camel', 'content'],
    +      attributesToSnippet: ['content:10'],
    +      camelCaseAttributes: ['hierarchy', 'hierarchy_radio', 'content'],
    +      searchableAttributes: [
    +        'unordered(hierarchy_radio_camel.lvl0)',
    +        'unordered(hierarchy_radio.lvl0)',
    +        'unordered(hierarchy_radio_camel.lvl1)',
    +        'unordered(hierarchy_radio.lvl1)',
    +        'unordered(hierarchy_radio_camel.lvl2)',
    +        'unordered(hierarchy_radio.lvl2)',
    +        'unordered(hierarchy_radio_camel.lvl3)',
    +        'unordered(hierarchy_radio.lvl3)',
    +        'unordered(hierarchy_radio_camel.lvl4)',
    +        'unordered(hierarchy_radio.lvl4)',
    +        'unordered(hierarchy_radio_camel.lvl5)',
    +        'unordered(hierarchy_radio.lvl5)',
    +        'unordered(hierarchy_radio_camel.lvl6)',
    +        'unordered(hierarchy_radio.lvl6)',
    +        'unordered(hierarchy_camel.lvl0)',
    +        'unordered(hierarchy.lvl0)',
    +        'unordered(hierarchy_camel.lvl1)',
    +        'unordered(hierarchy.lvl1)',
    +        'unordered(hierarchy_camel.lvl2)',
    +        'unordered(hierarchy.lvl2)',
    +        'unordered(hierarchy_camel.lvl3)',
    +        'unordered(hierarchy.lvl3)',
    +        'unordered(hierarchy_camel.lvl4)',
    +        'unordered(hierarchy.lvl4)',
    +        'unordered(hierarchy_camel.lvl5)',
    +        'unordered(hierarchy.lvl5)',
    +        'unordered(hierarchy_camel.lvl6)',
    +        'unordered(hierarchy.lvl6)',
    +        'content',
    +      ],
    +      distinct: true,
    +      attributeForDistinct: 'url',
    +      customRanking: [
    +        'desc(weight.pageRank)',
    +        'desc(weight.level)',
    +        'asc(weight.position)',
    +      ],
    +      ranking: [
    +        'words',
    +        'filters',
    +        'typo',
    +        'attribute',
    +        'proximity',
    +        'exact',
    +        'custom',
    +      ],
    +      highlightPreTag: '<span class="algolia-docsearch-suggestion--highlight">',
    +      highlightPostTag: '</span>',
    +      minWordSizefor1Typo: 3,
    +      minWordSizefor2Typos: 7,
    +      allowTyposOnNumericTokens: false,
    +      minProximity: 1,
    +      ignorePlurals: true,
    +      advancedSyntax: true,
    +      attributeCriteriaComputedByMinProximity: true,
    +      removeWordsIfNoResults: 'allOptional',
    +    },
    +  },
    +})
    +


































     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     








     
































































    The above recordProps is the configuration used for the default theme. You can modify them according to the theme you are using.

    Notice that the initialIndexSettings.YOUR_INDEX_NAME.attributesForFaceting fields must include 'lang' to make this plugin work properly.

    TIP

    If you are not using default theme, or you meet any problems when using docsearch, you can also check the above example crawler config, and ahead to Algolia Crawleropen in new window, and edit your config with 'Editor' panel in project sidebar.

    Options

    apiKey

    indexName

    appId

    searchParameters

    placeholder

    disableUserPersonalization

    initialQuery

    translations

    locales

    • Type: Record<string, DocsearchPluginOptions>

    • Details:

      Options of this plugin in different locales.

      All other options of this plugin are acceptable in locale config.

    • Example:

    export default {
    +  plugins: [
    +    docsearchPlugin({
    +      appId: '<APP_ID>',
    +      apiKey: '<API_KEY>',
    +      indexName: '<INDEX_NAME>',
    +      locales: {
    +        '/': {
    +          placeholder: 'Search Documentation',
    +          translations: {
    +            button: {
    +              buttonText: 'Search Documentation',
    +            },
    +          },
    +        },
    +        '/zh/': {
    +          placeholder: '搜索文档',
    +          translations: {
    +            button: {
    +              buttonText: '搜索文档',
    +            },
    +          },
    +        },
    +      },
    +    }),
    +  ],
    +}
    +

    indexBase

    • Type: string

    • Default: base

    • Details:

      The base path of the search index.

      If you are deploying your site to multiple domains, you don't need to submit all of them to DocSearch and generate search index separately. You could choose one of the domains as the index domain, and only submit the index domain to Docsearch for crawling search index. Then, you could reuse the search index across all deployments.

      However, if the base of your deployments are different for different domains, you need to set the option to the base of your index domain, so that other deployments could reuse the search index correctly.

    injectStyles

    • Type: boolean

    • Default: true

    • Details:

      Whether to inject the default styles of DocSearch or not.

      If you think the default styles of DocSearch is not compatible with your site, you can try to override the default styles, or set this option to false to totally exclude the default styles.

      When this option is disabled, you need to import your own styles for DocSearch. Also notice that all styles customization in Styles section would be unavailable.

    Styles

    You can customize styles via CSS variables that provided by @docsearch/cssopen in new window:

    :root {
    +  --docsearch-primary-color: rgb(84, 104, 255);
    +  --docsearch-text-color: rgb(28, 30, 33);
    +  --docsearch-spacing: 12px;
    +  --docsearch-icon-stroke-width: 1.4;
    +  --docsearch-highlight-color: var(--docsearch-primary-color);
    +  --docsearch-muted-color: rgb(150, 159, 175);
    +  --docsearch-container-background: rgba(101, 108, 133, 0.8);
    +  --docsearch-logo-color: rgba(84, 104, 255);
    +
    +  /* modal */
    +  --docsearch-modal-width: 560px;
    +  --docsearch-modal-height: 600px;
    +  --docsearch-modal-background: rgb(245, 246, 247);
    +  --docsearch-modal-shadow: inset 1px 1px 0 0 rgba(255, 255, 255, 0.5), 0 3px
    +      8px 0 rgba(85, 90, 100, 1);
    +
    +  /* searchbox */
    +  --docsearch-searchbox-height: 56px;
    +  --docsearch-searchbox-background: rgb(235, 237, 240);
    +  --docsearch-searchbox-focus-background: #fff;
    +  --docsearch-searchbox-shadow: inset 0 0 0 2px var(--docsearch-primary-color);
    +
    +  /* hit */
    +  --docsearch-hit-height: 56px;
    +  --docsearch-hit-color: rgb(68, 73, 80);
    +  --docsearch-hit-active-color: #fff;
    +  --docsearch-hit-background: #fff;
    +  --docsearch-hit-shadow: 0 1px 3px 0 rgb(212, 217, 225);
    +
    +  /* key */
    +  --docsearch-key-gradient: linear-gradient(
    +    -225deg,
    +    rgb(213, 219, 228) 0%,
    +    rgb(248, 248, 248) 100%
    +  );
    +  --docsearch-key-shadow: inset 0 -2px 0 0 rgb(205, 205, 230), inset 0 0 1px 1px
    +      #fff, 0 1px 2px 1px rgba(30, 35, 90, 0.4);
    +
    +  /* footer */
    +  --docsearch-footer-height: 44px;
    +  --docsearch-footer-background: #fff;
    +  --docsearch-footer-shadow: 0 -1px 0 0 rgb(224, 227, 232), 0 -3px 6px 0 rgba(69, 98, 155, 0.12);
    +}
    +

    Components

    Docsearch

    • Details:

      This plugin will register a <Docsearch /> component globally, and you can use it without any props.

      Put this component to where you want to place the docsearch button. For example, default theme puts this component to the end of the navbar.

    TIP

    This component is mainly used for theme development. You don't need to use it directly in most cases.

    + + + diff --git a/plugins/external-link-icon.html b/plugins/external-link-icon.html new file mode 100644 index 0000000000..3b8228348d --- /dev/null +++ b/plugins/external-link-icon.html @@ -0,0 +1,64 @@ + + + + + + + + + external-link-icon | VuePress Ecosystem + + + + + +

    external-link-icon

    @vuepress/plugin-external-link-icon

    This plugin will add an icon to the external link in your markdown content, i.e. open in new window

    This plugin has been integrated into the default theme.

    Usage

    npm i -D @vuepress/plugin-external-link-icon@next
    +
    import { externalLinkIconPlugin } from '@vuepress/plugin-external-link-icon'
    +
    +export default {
    +  plugins: [
    +    externalLinkIconPlugin({
    +      // options
    +    }),
    +  ],
    +}
    +

    Options

    locales

    • Type: Record<string, { openInNewWindow: string }>

    • Details:

      The a11y text of the external link icon in different locales.

      If this option is not specified, it will fallback to default text.

    • Example:

    export default {
    +  plugins: [
    +    externalLinkIconPlugin({
    +      locales: {
    +        '/': {
    +          openInNewWindow: 'open in new window',
    +        },
    +        '/zh/': {
    +          openInNewWindow: '在新窗口打开',
    +        },
    +      },
    +    }),
    +  ],
    +}
    +

    Frontmatter

    externalLinkIcon

    • Type: boolean

    • Details:

      Whether to append an external link icon to external links in current page.

    Styles

    You can customize the style of the external link icon via CSS variables:

    :root {
    +  --external-link-icon-color: #aaa;
    +}
    +

    Components

    ExternalLinkIcon

    • Details:

      This plugin will register a <ExternalLinkIcon /> component globally, and you can use it without any props.

    TIP

    This component is mainly used for theme development. You don't need to use it directly in most cases.

    + + + diff --git a/plugins/feed/channel.html b/plugins/feed/channel.html new file mode 100644 index 0000000000..ec34fbcd4e --- /dev/null +++ b/plugins/feed/channel.html @@ -0,0 +1,51 @@ + + + + + + + + + Channel Config | VuePress Ecosystem + + + + + +

    Channel Config

    The channel plugin option is used to config the feed channel.

    channel.title

    • Type: string
    • Default: SiteConfig.title

    Channel title

    • Type: string
    • Default: Deployment link (generated by options.hostname and context.base)

    Channel address

    channel.description

    • Type: string
    • Default: SiteConfig.description

    Channel description

    channel.language

    • Type: string

    • Default:

      • siteConfig.locales['/'].lang
      • If the above is not provided, fall back to "en-US"

    The language of the channel

    • Type: string

    • Default:

      • Try to read the author.name in channel options, and fall back to Copyright by $author
    • Recommended to set manually: Yes

    Channel copyright information

    channel.pubDate

    • Type: string (must be a valid Date ISOString)
    • Default: time when the plugin is called each time
    • Recommended to set manually: Yes

    Publish date of the Channel

    channel.lastUpdated

    • Type: string (must be a valid Date ISOString)
    • Default: time when the plugin is called each time

    Last update time of channel content

    channel.ttl

    • Type: number
    • Recommended to set manually: Yes

    The effective time of the content. It's the time to keep the cache after request without making new requests.

    channel.image

    • Type: string
    • Recommended to set manually: Yes

    A picture presenting the channel. A square picture with a size not smaller than 512×512 is recommended.

    channel.icon

    • Type: string
    • Recommended to set manually: Yes

    An icon representing a channel, a square picture, with not less than 128×128 in size, and transparent background color is recommended.

    channel.author

    • Type: FeedAuthor
    • Recommended to set manually: Yes

    The author of the channel.

    FeedAuthor format
    interface FeedAuthor {
    +  /** Author name */
    +  name: string
    +  /** Author's email */
    +  email?: string
    +  /** Author's site */
    +  url?: string
    +  /**
    +   * Author's avatar address
    +   *
    +   * Square, preferably not less than 128×128 with transparent background
    +   */
    +  avatar?: string
    +}
    +

    channel.hub

    • Type: string

    Link to Websub. Websub requires a server backend, which is inconsistent with VuePress, so ignore it if there is no special need.

    WebSub

    For details, see Websubopen in new window.

    + + + diff --git a/plugins/feed/config.html b/plugins/feed/config.html new file mode 100644 index 0000000000..da7cb0c1e9 --- /dev/null +++ b/plugins/feed/config.html @@ -0,0 +1,54 @@ + + + + + + + + + Plugin Config | VuePress Ecosystem + + + + + +

    Plugin Config

    hostname

    • Type: string
    • Required: Yes

    The domain name of the deployment site.

    atom

    • Type: boolean
    • Default: false

    Whether to output Atom syntax files.

    json

    • Type: boolean
    • Default: false

    Whether output JSON syntax files.

    rss

    • Type: boolean
    • Default: false

    Whether to output RSS syntax files.

    image

    • Type: string

    A large image/icon of the feed, probably used as banner.

    icon

    • Type: string

    A small icon of the feed, probably used as favicon.

    count

    • Type: number
    • Default: 100

    Set the maximum number of items in the feed. After all pages are sorted, the first count items will be intercepted.

    If your site has a lot of articles, you may consider this option to reduce feed file size.

    preservedElements

    • Type: (RegExp | string)[] | (tagName: string) => boolean

    Custom element or component which should be preserved in feed.

    By default, all unknown tags will be removed.

    filter

    • Type: (page: Page)=> boolean

    • Default:

      ;({ frontmatter, filePathRelative }: Page): boolean =>
      +  !(
      +    frontmatter.home ||
      +    !filePathRelative ||
      +    frontmatter.article === false ||
      +    frontmatter.feed === false
      +  )
      +

    A custom filter function, used to filter feed items.

    sorter

    • Type: (pageA: Page, pageB: Page)=> number

    • Default:

      // dateSorter is from @vuepress/helper
      +;(pageA: Page, pageB: Page): number =>
      +  dateSorter(
      +    pageA.data.git?.createdTime
      +      ? new Date(pageA.data.git?.createdTime)
      +      : pageA.frontmatter.date,
      +    pageB.data.git?.createdTime
      +      ? new Date(pageB.data.git?.createdTime)
      +      : pageB.frontmatter.date,
      +  )
      +

    Custom sorter function for feed items.

    The default sorting behavior is by file adding time coming from git (needs @vuepress/plugin-git).

    TIP

    You should enable @vuepress/plugin-git to get the newest created pages as feed items. Otherwise, the feed items will be sorted by the default order of pages in VuePress.

    channel

    channel option is used to config Feed Channels.

    For available options, please see Config → Channel

    devServer

    • Type: boolean
    • Default: false

    Whether enabled in devServer.

    TIP

    For performance reasons, we do not provide hot reload. Reboot your devServer to sync your changes.

    devHostname

    • Type: string
    • Default: "http://localhost:${port}"

    Hostname to use in devServer

    atomOutputFilename

    • Type: string
    • Default: "atom.xml"

    Atom syntax output filename, relative to dest folder.

    atomXslTemplate

    • Type: string
    • Default: Content of @vuepress/plugin-feed/templates/atom.xsl

    Atom xsl template file content.

    atomXslFilename

    • Type: string
    • Default: "atom.xsl"

    Atom xsl filename, relative to dest folder.

    jsonOutputFilename

    • Type: string
    • Default: "feed.json"

    JSON syntax output filename, relative to dest folder.

    rssOutputFilename

    • Type: string
    • Default: "rss.xml"

    RSS syntax output filename, relative to dest folder.

    rssXslTemplate

    • Type: string
    • Default: Content of @vuepress/plugin-feed/templates/rss.xsl

    RSS xsl template file content.

    rssXslFilename

    • Type: string
    • Default: "rss.xsl"

    RSS syntax xsl filename, relative to dest folder.

    getter

    Feed generation controller, see Feed Getter.

    The plugin has a built-in getter, only set this if you want full control of feed generation.

    locales

    • Type: Record<string, BaseFeedOptions>

    You can use it to specific options for each locale.

    Any options above are supported except hostname.

    + + + diff --git a/plugins/feed/frontmatter.html b/plugins/feed/frontmatter.html new file mode 100644 index 0000000000..582dcfe2ad --- /dev/null +++ b/plugins/feed/frontmatter.html @@ -0,0 +1,87 @@ + + + + + + + + + Frontmatter Config | VuePress Ecosystem + + + + + +

    Frontmatter Config

    You can control each feed item generation by setting page frontmatter.

    Additions and Removals

    By default, all articles are added to the feed stream. Set feed: false in frontmatter to remove a page from feed.

    Frontmatter Information

    title

    • Type: string

    Automatically generated by VuePress, defaults to the h1 content of the page

    description

    • Type: string

    Description of the page

    date

    • Type: Date

    Date when the page was published

    article

    • Type: boolean

    Whether the page is an article

    If this is set to false, the page will not be included in the final feed.

    • Type: string

    Page copyright information

    cover / image / banner

    • Type: string

    Image used as page cover , should be full link or absolute link.

    Frontmatter Options

    feed.title

    • Type: string

    The title of the feed item

    feed.description

    • Type: string

    Description of the feed item

    feed.content

    • Type: string

    The content of the feed item

    feed.author

    • Type: FeedAuthor[] | FeedAuthor

    The author of the feed item

    FeedAuthor format
    interface FeedAuthor {
    +  /**
    +   * Author name
    +   */
    +  name?: string
    +
    +  /**
    +   * Author email
    +   */
    +  email?: string
    +
    +  /**
    +   * Author site
    +   *
    +   * @description json format only
    +   */
    +  url?: string
    +
    +  /**
    +   * Author avatar
    +   *
    +   * @description json format only
    +   */
    +  avatar?: string
    +}
    +

    feed.contributor

    • Type: FeedContributor[] | FeedContributor

    Contributors to feed item

    FeedContributor format
    interface FeedContributor {
    +  /**
    +   * Author name
    +   */
    +  name?: string
    +
    +  /**
    +   * Author email
    +   */
    +  email?: string
    +
    +  /**
    +   * Author site
    +   *
    +   * @description json format only
    +   */
    +  url?: string
    +
    +  /**
    +   * Author avatar
    +   *
    +   * @description json format only
    +   */
    +  avatar?: string
    +}
    +

    feed.guid

    • Type: string

    The identifier of feed item, used to identify the feed item.

    You should ensure every feed has a unique guid.

    + + + diff --git a/plugins/feed/getter.html b/plugins/feed/getter.html new file mode 100644 index 0000000000..20edfc7ffc --- /dev/null +++ b/plugins/feed/getter.html @@ -0,0 +1,127 @@ + + + + + + + + + Feed Getter | VuePress Ecosystem + + + + + +

    Feed Getter

    You can take full control of feed items generation by setting getter in the plugin options.

    getter.title

    • Type: (page: Page) => string

    Item title getter

    • Type: (page: Page) => string

    Item link getter

    getter.description

    • Type: (page: Page) => string | undefined

    Item description getter

    TIP

    Due to Atom support HTML in summary, so you can return HTML content here if possible, but the content must start with mark html:.

    getter.content

    • Type: (page: Page) => string

    Item content getter

    getter.author

    • Type: (page: Page) => FeedAuthor[]

    Item author getter.

    The getter should return an empty array when author information is missing.

    FeedAuthor format
    interface FeedAuthor {
    +  /**
    +   * Author name
    +   */
    +  name?: string
    +
    +  /**
    +   * Author email
    +   */
    +  email?: string
    +
    +  /**
    +   * Author site
    +   *
    +   * @description json format only
    +   */
    +  url?: string
    +
    +  /**
    +   * Author avatar
    +   *
    +   * @description json format only
    +   */
    +  avatar?: string
    +}
    +

    getter.category

    • Type: (page: Page) => FeedCategory[] | undefined

    Item category getter.

    FeedCategory format
    interface FeedCategory {
    +  /**
    +   * Category Name
    +   */
    +  name: string
    +
    +  /**
    +   * A string that identifies a categorization taxonomy
    +   *
    +   * @description rss format only
    +   */
    +  domain?: string
    +
    +  /**
    +   * the categorization scheme via a URI
    +   *
    +   * @description atom format only
    +   */
    +  scheme?: string
    +}
    +

    getter.enclosure

    • Type: (page: Page) => FeedEnclosure | undefined

    Item enclosure getter.

    FeedEnclosure format
    interface FeedEnclosure {
    +  /**
    +   * Enclosure link
    +   */
    +  url: string
    +
    +  /**
    +   * what its type is
    +   *
    +   * @description should be a standard MIME Type, rss format only
    +   */
    +  Type: string
    +
    +  /**
    +   * Size in bytes
    +   *
    +   * @description rss format only
    +   */
    +  length?: number
    +}
    +

    getter.publishDate

    • Type: (page: Page) => Date | undefined

    Item release date getter

    getter.lastUpdateDate

    • Type: (page: Page) => Date

    Item last update date getter

    getter.image

    • Type: (page: Page) => string

    Item Image Getter

    Ensure it's returning a full URL

    getter.contributor

    • Type: (page: Page) => FeedContributor[]

    Item Contributor Getter

    The getter should return an empty array when contributor information is missing.

    FeedContributor format
    interface FeedContributor {
    +  /**
    +   * Author name
    +   */
    +  name?: string
    +
    +  /**
    +   * Author email
    +   */
    +  email?: string
    +
    +  /**
    +   * Author site
    +   *
    +   * @description json format only
    +   */
    +  url?: string
    +
    +  /**
    +   * Author avatar
    +   *
    +   * @description json format only
    +   */
    +  avatar?: string
    +}
    +
    • Type: (page: Page) => string | undefined

    Item copyright getter

    + + + diff --git a/plugins/feed/guide.html b/plugins/feed/guide.html new file mode 100644 index 0000000000..ddc095fa69 --- /dev/null +++ b/plugins/feed/guide.html @@ -0,0 +1,37 @@ + + + + + + + + + Guide | VuePress Ecosystem + + + + + +

    Guide

    Usage

    The plugin can generate feed files in the following three formats for you:

    • Atom 1.0
    • JSON 1.1
    • RSS 2.0

    Please set atom, json or rss to true in the plugin options according to the formats you want to generate.

    To correctly generate feed links, you need to set hostname in the plugin options,

    Readable Preview

    When you open the feed file in browser, we magically convert atom and rss feed xml to human readable html via xsl template. Check atomopen in new window and rssopen in new window feed of this site as an example!

    If you want to preview your feed in devServer, set devServer: true in plugin options. You may also need to set devHostname if you are not using the default http://localhost:{port}.

    Channel settings

    You can customize the feed channel information by setting the channel option.

    We recommend the following settings:

    • Convert the date of creating the feed to ISOString and write it into channel.pubDate
    • The update period of the content set in channel.ttl (unit: minutes)
    • Set copyright information via channel.copyright
    • Set the channel author via channel.author.

    For detailed options and their default values, see Channel Config

    Feed Generation

    By default, all articles are added to the feed stream.

    You can set feed and other options in page frontmatter to control contents of feed item. See Frontmatter Config for how they are converted.

    You can take full control of feed items generation by configuring the getter in the plugin options. For detailed options and their default values, see Configuration → Feed Getter.

    I18n Config

    The plugin generates separate feeds for each language.

    You can provide different settings for different languages via locales in the plugin options.

    + + + diff --git a/plugins/feed/index.html b/plugins/feed/index.html new file mode 100644 index 0000000000..545c73efba --- /dev/null +++ b/plugins/feed/index.html @@ -0,0 +1,47 @@ + + + + + + + + + feed | VuePress Ecosystem + + + + + +

    feed

    @vuepress/plugin-feed

    Usage

    npm i -D @vuepress/plugin-feed@next
    +
    import { feedPlugin } from '@vuepress/plugin-feed'
    +
    +export default {
    +  plugins: [
    +    feedPlugin({
    +      // options
    +    }),
    +  ],
    +}
    +
    + + + diff --git a/plugins/git.html b/plugins/git.html new file mode 100644 index 0000000000..73a695ac0e --- /dev/null +++ b/plugins/git.html @@ -0,0 +1,66 @@ + + + + + + + + + git | VuePress Ecosystem + + + + + +

    git

    @vuepress/plugin-git

    This plugin will collect git information of your pages, including the created and updated time, the contributors, etc.

    The lastUpdated and contributors of default theme is powered by this plugin.

    This plugin is mainly used to develop themes. You won't need to use it directly in most cases.

    Usage

    npm i -D @vuepress/plugin-git@next
    +
    import { gitPlugin } from '@vuepress/plugin-git'
    +
    +export default {
    +  plugins: [
    +    gitPlugin({
    +      // options
    +    }),
    +  ],
    +}
    +

    Git Repository

    This plugin requires your project to be inside a Git Repositoryopen in new window, so that it can collect information from the commit history.

    You should ensure all commits are available when building your site. For example, CI workflows usually clone your repository with --depth 1open in new window to avoid fetching all commits, so you should disable the behavior to make this plugin work properly in CI.

    WARNING

    This plugin will significantly slow down the speed of data preparation, especially when you have a lot of pages. You can consider disabling this plugin in dev mode to get better development experience.

    Options

    createdTime

    • Type: boolean

    • Default: true

    • Details:

      Whether to collect page created time or not.

    updatedTime

    • Type: boolean

    • Default: true

    • Details:

      Whether to collect page updated time or not.

    contributors

    • Type: boolean

    • Default: true

    • Details:

      Whether to collect page contributors or not.

    Frontmatter

    gitInclude

    • Type: string[]

    • Details:

      An array of relative paths to be included when calculating page data.

    • Example:

    ---
    +gitInclude:
    +  - relative/path/to/file1
    +  - relative/path/to/file2
    +---
    +

    Page Data

    This plugin will add a git field to page data.

    After using this plugin, you can get the collected git information in page data:

    import type { GitPluginPageData } from '@vuepress/plugin-git'
    +import { usePageData } from 'vuepress/client'
    +
    +export default {
    +  setup() {
    +    const page = usePageData<GitPluginPageData>()
    +    console.log(page.value.git)
    +  },
    +}
    +

    git.createdTime

    • Type: number

    • Details:

      Unix timestamp in milliseconds of the first commit of the page.

      This attribute would take the minimum of the first commit timestamps of the current page and the files listed in gitInclude.

    git.updatedTime

    • Type: number

    • Details:

      Unix timestamp in milliseconds of the last commit of the page.

      This attribute would take the maximum of the last commit timestamps of the current page and the files listed in gitInclude.

    git.contributors

    • Type: GitContributor[]
    interface GitContributor {
    +  name: string
    +  email: string
    +  commits: number
    +}
    +
    • Details:

      The contributors information of the page.

      This attribute would also include contributors to the files listed in gitInclude.

    + + + diff --git a/plugins/google-analytics.html b/plugins/google-analytics.html new file mode 100644 index 0000000000..e298dbee13 --- /dev/null +++ b/plugins/google-analytics.html @@ -0,0 +1,62 @@ + + + + + + + + + google-analytics | VuePress Ecosystem + + + + + +

    google-analytics

    @vuepress/plugin-google-analytics

    Integrate Google Analyticsopen in new window into VuePress.

    This plugin will import gtag.jsopen in new window for Google Analytics 4open in new window.

    Usage

    npm i -D @vuepress/plugin-google-analytics@next
    +
    import { googleAnalyticsPlugin } from '@vuepress/plugin-google-analytics'
    +
    +export default {
    +  plugins: [
    +    googleAnalyticsPlugin({
    +      // options
    +    }),
    +  ],
    +}
    +

    Reporting Events

    Google Analytics will automatically collect some eventsopen in new window, such as page_view, first_visit, etc.

    So if you only want to collect some basic data of your site, you don't need to do anything else except setting the Measurement ID correctly.

    After using this plugin, the global gtag() function is available on the window object, and you can use it for custom events reportingopen in new window.

    Options

    id

    • Type: string

    • Details:

      The Measurement ID of Google Analytics 4, which should start with 'G-'.

      You can follow the instructions hereopen in new window to find your Measurement ID. Notice the difference between Google Analytics 4 Measurement ID (i.e. "G-" ID) and Universal Analytics Tracking ID (i.e. "UA-" ID).

    • Example:

    export default {
    +  plugins: [
    +    googleAnalyticsPlugin({
    +      id: 'G-XXXXXXXXXX',
    +    }),
    +  ],
    +}
    +

    debug

    export default {
    +  plugins: [
    +    googleAnalyticsPlugin({
    +      id: 'G-XXXXXXXXXX',
    +      debug: true,
    +    }),
    +  ],
    +}
    +
    + + + diff --git a/plugins/index.html b/plugins/index.html new file mode 100644 index 0000000000..0d2136887f --- /dev/null +++ b/plugins/index.html @@ -0,0 +1,37 @@ + + + + + + + + + Plugins | VuePress Ecosystem + + + + + +
    + + + diff --git a/plugins/medium-zoom.html b/plugins/medium-zoom.html new file mode 100644 index 0000000000..cce8a6b53f --- /dev/null +++ b/plugins/medium-zoom.html @@ -0,0 +1,67 @@ + + + + + + + + + medium-zoom | VuePress Ecosystem + + + + + +

    medium-zoom

    @vuepress/plugin-medium-zoom

    Integrate medium-zoomopen in new window into VuePress, which can provide the ability to zoom images.

    This plugin has been integrated into the default theme.

    Usage

    npm i -D @vuepress/plugin-medium-zoom@next
    +
    import { mediumZoomPlugin } from '@vuepress/plugin-medium-zoom'
    +
    +export default {
    +  plugins: [
    +    mediumZoomPlugin({
    +      // options
    +    }),
    +  ],
    +}
    +

    Options

    selector

    • Type: string

    • Default: ':not(a) > img'

    • Details:

      Selector of zoomable images.

      By default this plugin will make all images zoomable except those inside <a> tags.

    delay

    • Type: number

    • Default: 500

    • Details:

      Delay in milliseconds.

      After navigating to a new page, this plugin will make images zoomable with a delay.

    zoomOptions

    Styles

    You can customize most of the zoom styles via zoomOptions, while this plugin also provides some CSS variables for additional customization:

    :root {
    +  --medium-zoom-z-index: 100;
    +  --medium-zoom-bg-color: #ffffff;
    +  --medium-zoom-opacity: 1;
    +}
    +

    Composition API

    useMediumZoom

    • Details:

      Returns the Zoom instance that used by this plugin, so that you can use the instance methodsopen in new window directly.

      This plugin will make images zoomable after navigating to current page. But if you are going to add new images dynamically, you may need this method to make those new images zoomable, too.

      This plugin adds an extra refresh method on the Zoom instance, which will call zoom.detach() then zoom.attach() with the selector as the default parameter. It will help you to refresh the zoomable images for current page.

    • Example:

    import { nextTick } from 'vue'
    +import { useMediumZoom } from '@vuepress/plugin-medium-zoom/client'
    +
    +export default {
    +  setup() {
    +    const zoom = useMediumZoom()
    +
    +    // ... do something to add new images in current page
    +
    +    // then you may need to call `refresh` manually to make those new images zoomable
    +    nextTick(() => {
    +      zoom.refresh()
    +    })
    +  },
    +}
    +
    + + + diff --git a/plugins/nprogress.html b/plugins/nprogress.html new file mode 100644 index 0000000000..23d97e6b75 --- /dev/null +++ b/plugins/nprogress.html @@ -0,0 +1,47 @@ + + + + + + + + + nprogress | VuePress Ecosystem + + + + + +

    nprogress Plugin

    @vuepress/plugin-nprogress

    Integrate nprogressopen in new window into VuePress, which can provide a progress bar when navigating to another page.

    This plugin has been integrated into the default theme.

    Usage

    npm i -D @vuepress/plugin-nprogress@next
    +
    import { nprogressPlugin } from '@vuepress/plugin-nprogress'
    +
    +export default {
    +  plugins: [nprogressPlugin()],
    +}
    +

    Styles

    You can customize the style of the progress bar via CSS variables:

    :root {
    +  --nprogress-color: #29d;
    +  --nprogress-z-index: 1031;
    +}
    +
    + + + diff --git a/plugins/palette.html b/plugins/palette.html new file mode 100644 index 0000000000..fcd1c73513 --- /dev/null +++ b/plugins/palette.html @@ -0,0 +1,75 @@ + + + + + + + + + palette | VuePress Ecosystem + + + + + +

    palette

    @vuepress/plugin-palette

    Provide palette support for your theme.

    This plugin is mainly used to develop themes, and has been integrated into the default theme. You won't need to use it directly in most cases.

    For theme authors, this plugin will help you to provide styles customization for users.

    Usage

    npm i -D @vuepress/plugin-palette@next
    +
    import { palettePlugin } from '@vuepress/plugin-palette'
    +
    +export default {
    +  plugins: [
    +    palettePlugin({
    +      // options
    +    }),
    +  ],
    +}
    +

    Palette and Style

    This plugin will provide a @vuepress/plugin-palette/palette (palette file) and a @vuepress/plugin-palette/style (style file) to be imported in your theme styles.

    The palette file is used for defining style variables, so it's likely to be imported at the beginning of your theme styles. For example, users can define CSS variablesopen in new window, SASS variablesopen in new window, LESS variablesopen in new window or Stylus variablesopen in new window in the palette, and then you can use those variables in your theme styles.

    The style file is used for overriding the default styles or adding extra styles, so it's likely to be imported at the end of your theme styles.

    Usage

    Use this plugin in your theme, assuming you are using SASS:

    export default {
    +  // ...
    +  plugins: [palettePlugin({ preset: 'sass' })],
    +}
    +

    Usage of Palette

    Import the plugin's palette file where your theme needs to use the corresponding variables, such as in the Layout.vue file:

    <template>
    +  <h1 class="palette-title">Hello, Palette!</h1>
    +</template>
    +
    +<style lang="scss">
    +/* import variables from the plugin's palette file */
    +@import '@vuepress/plugin-palette/palette';
    +
    +/* set default value for variables */
    +$color: red !default;
    +
    +/* use variables in your styles */
    +.palette-title {
    +  color: $color;
    +}
    +</style>
    +

    Then users can customize variables in .vuepress/styles/palette.scss:

    $color: green;
    +

    Usage of Style

    Import the plugin's style file after your theme's styles, for example, in the clientConfigFile:

    // import your theme's style file
    +import 'path/to/your/theme/style'
    +// import the plugin's style file
    +import '@vuepress/plugin-palette/style'
    +

    Then users can add extra styles in .vuepress/styles/index.scss and override the default styles of your theme:

    h1 {
    +  font-size: 2.5rem;
    +}
    +

    Options

    preset

    • Type: 'css' | 'sass' | 'less' | 'stylus'

    • Default: 'css'

    • Details:

      Set preset for other options.

      If you don't need advanced customization of the plugin, it's recommended to only set this option and omit other options.

    userPaletteFile

    • Type: string

    • Default:

      • css: '.vuepress/styles/palette.css'
      • sass: '.vuepress/styles/palette.scss'
      • less: '.vuepress/styles/palette.less'
      • stylus: '.vuepress/styles/palette.styl'
    • Details:

      File path of the user palette file, relative to source directory.

      The default value depends on the preset option.

      The file is where users define style variables, and it's recommended to keep the default file path as a convention.

    tempPaletteFile

    • Type: string

    • Default:

      • css: 'styles/palette.css'
      • sass: 'styles/palette.scss'
      • less: 'styles/palette.less'
      • stylus: 'styles/palette.styl'
    • Details:

      File path of the generated palette temp file, relative to temp directory.

      The default value depends on the preset option.

      You should import the palette file via '@vuepress/plugin-palette/palette' alias, so you don't need to change this option in most cases.

    userStyleFile

    • Type: string

    • Default:

      • css: '.vuepress/styles/index.css'
      • sass: '.vuepress/styles/index.scss'
      • less: '.vuepress/styles/index.less'
      • stylus: '.vuepress/styles/index.styl'
    • Details:

      File path of the user style file, relative to source directory.

      The default value depends on the preset option.

      The file is where users override default styles or add extra styles, and it's recommended to keep the default file path as a convention.

    tempStyleFile

    • Type: string

    • Default:

      • css: 'styles/index.css'
      • sass: 'styles/index.scss'
      • less: 'styles/index.less'
      • stylus: 'styles/index.styl'
    • Details:

      File path of the generated style temp file, relative to temp directory.

      The default value depends on the preset option.

      You should import the style file via '@vuepress/plugin-palette/style' alias, so you don't need to change this option in most cases.

    importCode

    • Type: (filePath: string) => string

    • Default:

      • css: (filePath) => `@import '${filePath}';\n`
      • sass: (filePath) => `@forward 'file:///${filePath}';\n`
      • less: (filePath) => `@import '${filePath}';\n`
      • stylus: (filePath) => `@require '${filePath}';\n`
    • Details:

      Function to generate import code.

      The default value depends on the preset option.

      This option is used for generating tempPaletteFile and tempStyleFile, and you don't need to change this option in most cases.

    + + + diff --git a/plugins/photo-swipe.html b/plugins/photo-swipe.html new file mode 100644 index 0000000000..3b21dfd7db --- /dev/null +++ b/plugins/photo-swipe.html @@ -0,0 +1,181 @@ + + + + + + + + + photo-swipe | VuePress Ecosystem + + + + + +

    photo-swipe

    @vuepress/plugin-photo-swipe

    This plugin will make the pictures in the body of the page enter the preview mode when clicked.

    Usage

    npm i -D @vuepress/plugin-photo-swipe@next
    +
    import { photoSwipePlugin } from '@vuepress/plugin-photo-swipe'
    +
    +export default {
    +  plugins: [
    +    photoSwipePlugin({
    +      // options
    +    }),
    +  ],
    +}
    +

    In preview mode, you can:

    • Swipe left and right to preview other pictures on the page in order
    • View the description of the picture
    • Zoom in and zoom out the picture
    • View pictures in full screen
    • Download pictures
    • Share pictures

    TIP

    • Besides clicking "×" in the upper right corner to exit the preview mode, scrolling up and down more than a certain distance will also exit preview mode.
    • On mobile devices, or using the PC trackpad, you can use pan and zoom gestures to pan and zoom in the preview mode.

    Options

    selector

    • Type: string | string[]
    • Default: ".theme-default-content :not(a) > img:not([no-view])"
    • Details: Image selector

    scrollToClose

    • Type: boolean
    • Default: true
    • Details: Whether close the current image when scrolling.

    delay

    • Type: number

    • Default: 800

    • Details:

      The delay of operating dom, in ms.

      TIP

      If the theme you are using has a switching animation, it is recommended to configure this option to Switch animation duration + 200.

    locales

    • Type: PhotoSwipePluginLocaleConfig

      interface PhotoSwipePluginLocaleData {
      +  /**
      +   * Close button label text
      +   */
      +  close: string
      +
      +  /**
      +   * Full screen button label text
      +   */
      +  fullscreen: string
      +
      +  /**
      +   * Share button label text
      +   */
      +  share: string
      +
      +  /**
      +   * Zoom button label text
      +   */
      +  zoom: string
      +
      +  /**
      +   * Previous image button label text
      +   */
      +  prev: string
      +
      +  /**
      +   * Next image button label text
      +   */
      +  next: string
      +
      +  /**
      +   * Share button config
      +   */
      +  buttons: PhotoSwipeDefaultUI.ShareButtonData[]
      +}
      +
      +interface PhotoSwipePluginLocaleConfig {
      +  [localePath: string]: PhotoSwipePluginLocaleData
      +}
      +
    • Details: Locales config for photo-swipe plugin.

    • Example:

      import { defineUserConfig } from 'vuepress'
      +import { photoSwipePlugin } from '@vuepress/plugin-photo-swipe'
      +
      +export default defineUserConfig({
      +  locales: {
      +    '/': {
      +      // this is a supported language
      +      lang: 'en-US',
      +    },
      +    '/xx/': {
      +      // the plugin does not support this language
      +      lang: 'mm-NN',
      +    },
      +  },
      +
      +  plugins: [
      +    photoSwipePlugin({
      +      locales: {
      +        '/': {
      +          // Override share label text
      +          share: 'Share with friends',
      +        },
      +
      +        '/xx/': {
      +          // Complete locale config for `mm-NN` language here
      +        },
      +      },
      +    }),
      +  ],
      +})
      +
    Built-in Supported Languages
    • Simplified Chinese (zh-CN)
    • Traditional Chinese (zh-TW)
    • English (United States) (en-US)
    • German (de-DE)
    • German (Australia) (de-AT)
    • Russian (ru-RU)
    • Ukrainian (uk-UA)
    • Vietnamese (vi-VN)
    • Portuguese (Brazil) (pt-BR)
    • Polish (pl-PL)
    • French (fr-FR)
    • Spanish (es-ES)
    • Slovak (sk-SK)
    • Japanese (ja-JP)
    • Turkish (tr-TR)
    • Korean (ko-KR)
    • Finnish (fi-FI)
    • Indonesian (id-ID)
    • Dutch (nl-NL)

    Frontmatter

    • Type: string | false
    • Details:

    Image selector for the current page, or false to disable photo-swipe in current page.

    Client Config

    definePhotoSwipeConfig

    Options passed to photo-swipeopen in new window

    import { definePhotoSwipeConfig } from '@vuepress/plugin-photo-swipe/client'
    +
    +definePhotoSwipeConfig({
    +  // set photoswipe options here
    +})
    +
    +export default {}
    +

    API

    You can also call photoswipe with apis.

    createPhotoSwipe allows you to programmatically view images links with PhotoSwipe:

    <script setup lang="ts">
    +import { onMounted, onUnmounted } from 'vue';
    +import { createPhotoSwipe } from "@vuepress/plugin-photo-swipe/client";
    +
    +let state: PhotoSwipeState | null = null;
    +
    +const openPhotoSwipe = (index: number) => {
    +  state?.open(index - 1);
    +};
    +
    +onMounted(async () => {
    +  // create a new photoswipe instance with image links
    +  state=  await createPhotoSwipe(
    +    [
    +      'https://exmaple.com/image1.png'
    +      'https://exmaple.com/image2.png'
    +      'https://exmaple.com/image3.png'
    +    ],
    +    {
    +      // photoswipe options
    +    }
    +  );
    +});
    +
    +onUnmounted(() => {
    +  state?.destroy()
    +})
    +</script>
    +
    +<template>
    +  <button v-for="i in 3" @click="openPhotoSwipe(i)">open photo {{ i }}</button>
    +</template>
    +

    registerPhotoSwipe allows you to register photoswipe for the given image elements:

    <script setup lang="ts">
    +import { onMounted, onUnmounted } from 'vue'
    +import { registerPhotoSwipe } from '@vuepress/plugin-photo-swipe/client'
    +
    +let destroy: () => void | null = null
    +
    +onMounted(async () => {
    +  await nextTick()
    +
    +  const images = Array.from(document.querySelectorAll('img'))
    +
    +  // create a new photoswipe instance on image elements
    +  state = await registerPhotoSwipe(images, {
    +    // photoswipe options
    +  })
    +})
    +
    +onUnmounted(() => {
    +  destroy?.()
    +})
    +</script>
    +

    Styles

    You can customize the style via CSS variables:

    :root {
    +  --photo-swipe-bullet: #fff;
    +  --photo-swipe-bullet-active: #3eaf7c;
    +}
    +
    + + + diff --git a/plugins/prismjs.html b/plugins/prismjs.html new file mode 100644 index 0000000000..267b709bd5 --- /dev/null +++ b/plugins/prismjs.html @@ -0,0 +1,47 @@ + + + + + + + + + prismjs | VuePress Ecosystem + + + + + +

    prismjs

    @vuepress/plugin-prismjs

    This plugin will enable syntax highlighting for markdown code fence with Prism.jsopen in new window.

    This plugin has been integrated into the default theme.

    Notice that this plugin would only tokenize the code fence without adding styles. When using it with a custom theme, you may need to choose and import Prism.js style theme yourself.

    Usage

    npm i -D @vuepress/plugin-prismjs@next
    +
    import { prismjsPlugin } from '@vuepress/plugin-prismjs'
    +
    +export default {
    +  plugins: [
    +    prismjsPlugin({
    +      // options
    +    }),
    +  ],
    +}
    +

    Options

    preloadLanguages

    • Type: string[]

    • Default: ['markdown', 'jsdoc', 'yaml']

    • Details:

      Languages to preload.

      By default, languages will be loaded on demand when parsing markdown files.

      However, Prism.js has some potential issuesopen in new window about loading languages dynamically. To avoid them, you can preload languages via this option.

    + + + diff --git a/plugins/pwa/config.html b/plugins/pwa/config.html new file mode 100644 index 0000000000..2e20dd4ca2 --- /dev/null +++ b/plugins/pwa/config.html @@ -0,0 +1,176 @@ + + + + + + + + + Config | VuePress Ecosystem + + + + + +

    Config

    Options

    showInstall

    • Type: boolean
    • Default: false
    • Details: Whether display install button when Service Worker is first registered successfully.

    manifest

    • Type: AppManifest

    • Details:

      You can fill with an object which will be parsed to manifest.webmanifest.

      TIP

      Some options have their fallback if you don't set them.

      • name: siteConfig.title || siteConfig.locales['/'].title || "Site"
      • short_name: siteConfig.title || siteConfig.locales['/'].title || "Site"
      • description: siteConfig.description || siteConfig.locales['/'].description || "A site built with vuepress"
      • lang: siteConfig.locales['/'].lang || "en-US"
      • start_url: context.base
      • scope: context.base
      • display: "standalone"
      • theme_color: "#46bd87"
      • background_color: "#ffffff"
      • orientation: "portrait-primary"
      • prefer_related_applications: false
    • Reference:

    favicon

    • Type: string

    • Details:

      Link of favicon.ico.

      WARNING

      We recommend you to set favicon for your site.

    themeColor

    • Type: string

    • Default: "#46bd87"

    • Details:

      Theme Color of the pwa.

    cacheHTML

    • Type: boolean

    • Default: false

    • Details:

      Whether cache HTML files besides home page and 404 page.

    cacheImage

    • Type: boolean

    • Default: false

    • Details:

      Whether cache pictures

    maxSize

    • Type: number

    • Default: 2048

    • Details:

      Max size allowed to be cached, with KB unit

      WARNING

      This option has the highest priority, and any files exceeding this value will be excluded.

      So if you generate very large HTML or JS files, please consider increasing this value, otherwise your PWA may not work normally in offline mode.

    maxImageSize

    • Type: number

    • Default: 1024

    • Details: Max picture size allowed to be cached, with KB unit

      The value must not be greater than maxSize option

    update

    • Type: "disabled" | "available" | "hint" | "force"

    • Default: "available"

    • Details: Control logic when new content is found.

      • "disabled": Do nothing even when new service worker is available. After new service work succeeds installing and starts waiting, it will control page and provide new content in next visit.

      • "available": Only display update popup when the new service worker is available

      • "hint": Display a hint to let user choose to refresh immediately

        This is helpful when you want users to see new docs immediately.

        TIP

        If users choose to refresh, the current service worker will be unregister, and request will start coming to web. Later the new service worker will start installing and control current page after installed.

      • "force": unregister current service worker immediately then refresh to get new content

        DANGER

        Although this ensures users are viewing the latest content, it may affect viewing experiences.

      TIP

      How docs are updated is controlled by a previous version, so the current option only effect next update from this version.

    apple

    Special settings for better supporting Safari, ignoring these options are safe.

    apple.icon

    • Type: string
    • Details: Icon link used by Safari.

    apple.statusBarColor

    • Type: "black" | "white"
    • Default: "black"
    • Details: Status bar color for Safari

    apple.maskIcon

    • Type: string
    • Details: Safari mask icon

    msTile

    Special settings for Microsoft tiles, ignoring these options are safe.

    msTile.image

    • Type: string
    • Details: Tile image

    msTile.color

    • Type: string
    • Default value: themeColor
    • Details: Tile color

    foundComponent

    • Type: string
    • Default: "PWAFoundPopup"
    • Details: Path of custom hint popup component.

    readyComponent

    • Type: string
    • Default: "PWAReadyPopup"
    • Details: Path of custom update popup component.

    appendBase

    • Type: boolean
    • Default: false
    • Details: Whether append base to all absolute links in options.

    generateSwConfig

    locales

    • Type: PWAPluginLocaleConfig

      interface PWAPluginLocaleData {
      +  /**
      +   * Install button text
      +   */
      +  install: string
      +
      +  /**
      +   * iOS install hint text
      +   */
      +  iOSInstall: string
      +
      +  /**
      +   * Cancel button text
      +   */
      +  cancel: string
      +
      +  /**
      +   * Close button text
      +   */
      +  close: string
      +
      +  /**
      +   * Previous image text
      +   */
      +  prevImage: string
      +
      +  /**
      +   * Next image text
      +   */
      +  nextImage: string
      +
      +  /**
      +   * Install explain text
      +   */
      +  explain: string
      +
      +  /**
      +   * Description label text
      +   */
      +  desc: string
      +
      +  /**
      +   * Feature label text
      +   */
      +  feature: string
      +
      +  /**
      +   * Update hint text
      +   */
      +  hint: string
      +
      +  /**
      +   * Update available text
      +   */
      +  update: string
      +}
      +
      +interface PWAPluginLocaleConfig {
      +  [localePath: string]: PWAPluginLocaleData
      +}
      +
    • Details: Locales config for pwa plugin.

    Built-in Supported Languages
    • Simplified Chinese (zh-CN)
    • Traditional Chinese (zh-TW)
    • English (United States) (en-US)
    • German (de-DE)
    • German (Australia) (de-AT)
    • Russian (ru-RU)
    • Ukrainian (uk-UA)
    • Vietnamese (vi-VN)
    • Portuguese (Brazil) (pt-BR)
    • Polish (pl-PL)
    • French (fr-FR)
    • Spanish (es-ES)
    • Slovak (sk-SK)
    • Japanese (ja-JP)
    • Turkish (tr-TR)
    • Korean (ko-KR)
    • Finnish (fi-FI)
    • Indonesian (id-ID)
    • Dutch (nl-NL)

    Composition API

    usePwaEvent

    import { usePwaEvent } from '@vuepress/plugin-pwa/client'
    +
    +export default {
    +  setup() {
    +    const event = usePwaEvent()
    +    event.on('ready', (registration) => {
    +      console.log('Service worker is active.')
    +    })
    +  },
    +}
    +

    Utilities

    forceUpdate

    • Details:

      Force update the page when an update is found.

    • Example:

    import { forceUpdate } from '@vuepress/plugin-pwa/client'
    +import { onMounted } from 'vue'
    +
    +export default {
    +  setup() {
    +    onMounted(() => {
    +      forceUpdate()
    +    })
    +  },
    +}
    +

    registerSW

    • Details:

      Register service worker manually.

    • Parameters:

    ParameterTypeDescription
    serviceWorkerPathstringPath of the service worker
    hooksobjectHooks of service worker
    showStatusbooleanLog service worker status in console
    interface Hooks {
    +  registrationOptions?: RegistrationOptions
    +  ready?: (registration: ServiceWorkerRegistration) => void
    +  registered?: (registration: ServiceWorkerRegistration) => void
    +  cached?: (registration: ServiceWorkerRegistration) => void
    +  updated?: (registration: ServiceWorkerRegistration) => void
    +  updatefound?: (registration: ServiceWorkerRegistration) => void
    +  offline?: () => void
    +  error?: (error: Error) => void
    +}
    +
    • Example:
    import { registerSW } from '@vuepress/plugin-pwa/client'
    +import { onMounted } from 'vue'
    +
    +export default {
    +  setup() {
    +    onMounted(() => {
    +      registerSW('/service-worker.js', {
    +        ready(registration) {
    +          console.log('Service worker is active.')
    +        },
    +      })
    +    })
    +  },
    +}
    +

    skipWaiting

    • Details:

      Activate the waiting service worker.

    • Parameters:

    ParameterTypeDescription
    registrationServiceWorkerRegistrationThe registration of the service worker you want activate
    • Example:
    import { usePwaEvent, skipWaiting } from '@vuepress/plugin-pwa/client'
    +
    +export default {
    +  setup() {
    +    const event = usePwaEvent()
    +
    +    event.on('updated', (registration) => {
    +      console.log('The waiting service worker is available.')
    +      // activate the waiting service worker
    +      skipWaiting(registration)
    +    })
    +  },
    +}
    +

    unregisterSW

    • Details:

      Unregister service worker manually.

    • Example:

    import { unregisterSW } from '@vuepress/plugin-pwa/client'
    +import { onMounted } from 'vue'
    +
    +export default {
    +  setup() {
    +    onMounted(() => {
    +      unregisterSW()
    +    })
    +  },
    +}
    +

    Styles

    You can customize the style via CSS variables:

    :root {
    +  --pwa-z-index: 10;
    +  --pwa-color: #2c3e50;
    +  --pwa-bg-color: #ffffff;
    +  --pwa-border-color: #3eaf7c;
    +  --pwa-shadow-color: rgb(0 0 0 / 15%);
    +  --pwa-btn-text-color: #ffffff;
    +  --pwa-btn-bg-color: #3eaf7c;
    +  --pwa-btn-hover-bg-color: #4abf8a;
    +  --pwa-content-color: #333;
    +  --pwa-content-light-color: #666;
    +}
    +
    + + + diff --git a/plugins/pwa/guide.html b/plugins/pwa/guide.html new file mode 100644 index 0000000000..13c4cc5f4c --- /dev/null +++ b/plugins/pwa/guide.html @@ -0,0 +1,59 @@ + + + + + + + + + Guide | VuePress Ecosystem + + + + + +

    Guide

    Intro

    Make your VuePress site a Progressive Web Application (PWA)[1].

    This plugin uses workbox-buildopen in new window to generate service worker file, and uses register-service-workeropen in new window to register service worker.

    WARNING

    If you enabled this plugin once and you want to disable it, you might need `@vuepress/plugin-remove-pwa to remove the existing service worker.

    A PWA uses a Service Worker [2] (SW for short) to cache and proxy site content.

    Web App Manifests

    To make your website fully compliant with PWA, a Web app manifests [3] file is needed, and your pwa should satisfy the installability [4] specification.

    You can set manifest option to customize the manifest file, or provide a manifest.webmanifest or manifest.json in public folder. The former has higher priority.

    The plugin will automatically generate manifest.webmanifest for you and add manifest link declaration in each page, while you should still at least set a valid icon through manifest.icons or other icon related options in the PWA plugin.

    WARNING

    The installability [4:1] specification requires at least one valid icon to be declared in the manifest.

    So if you do not configure manifest.icons, visitors can only enjoy the offline accessibility brought by the Service Worker cache, while cannot install your site as a PWA.

    Besides the plugin does not process anything in the manifest by default, but outputs them as-is. This means that if you plan to deploy to a subdirectory, you should append the URL prefix to manifest Urls yourself. If everything you need is all under base directory, you can set appendBase: true in plugin options to let the plugin append base to any links in manifest.

    Cache Control

    To better control what the Service Worker can pre-cache, the plugin provides related options for cache control.

    Default cache

    By default, the plugin will pre-cache all js css files, and only homepage and 404 html are cached. The plugin will also cache font files (woff, woff2, eot, ttf, otf) and SVG icons.

    Image Cache

    If your site has only a few important images, and want them displayed in offline mode, you can cache site images by setting cacheImage: true .

    We recognize images by file extension. Any files ending with .png, .jpg, .jpeg, .gif, .bmp, .webp will be regarded as images.

    HTML Cache

    If you have small sites, and would like to make document fully offline available, you can set cacheHTML to true to cache all HTML files.

    Why only home and 404 page been cached by default?

    Though VuePress generates HTML files through SSG[5] for all pages, these files are mainly used for SEO[6] and allow you to directly configure the backend without SPA[7] Visit any link.

    VuePress is essentially an SPA. This means that you only need to cache the home page and enter from the home page to access all pages normally. Therefore, not caching other HTML by default can effectively reduce the cache size (40% smaller in size) and speed up the SW update speed.

    But this also has the disadvantage. If the user enters the site directly from a non-home page, the HTML file for the first page still needs to be loaded from the internet. Also, in offline environment, users can only enter through the homepage and then navigate to the corresponding page by themselves. If they directly access a link, an inaccessible prompt will appear.

    Size Control

    To prevent large files from being included in the pre-cache list, any files > 2 MB or images > 1 MB will be omitted. You can customize these limits with maxSize and maxImageSize options (in KB unit).

    Update Control

    We provide the update option to control how users receive updates.

    The default value of the update option is "available", which means that when new content available, the new SW will be installed and its resources will be fetched silently in the background. A pop-up window appears once the new SW is ready, and users can choose whether to refresh immediately to view new content. This means users are reading old content before a new SW is ready.

    If your project is still in building stage, and you want to alert the user that he may be reading outdated content, you can set this to "hint". This allows users to be notified that new content has been published within seconds after visiting docs. But the negative effect of this is that if the user chooses to update before the new SW is ready, he will need to get all the resources of the page from the internet before the new SW installs and controls the page.

    If your docs are stable, or you're hosting a blog and don't care much about users receiving the latest version right away, you can set this to "disabled", which means that the new SW will be installed completely silently in the background and start waiting, when all pages controlled by old SW are all closed, the new SW will start to take control and provide users with new content during next visit. This setting can prevent users from being disturbed during the visit.

    To speed up user access under weak or no network conditions through SW, but also want users to always access new content, you can set this option to "force". This means any old SW will be removed as soon as a new SW is detected, and all pages are refreshed to ensure the user is browsing the latest content. The biggest disadvantage is that all users will experience unexpected sudden refresh within seconds after reentering an updated site.

    Popups

    When new content is detected (new SW detected), a update found popup appears; and when the new content is ready, an update ready popup appears.

    If you are not satisfied with the default popup content, you can use your own component. Import PWAFoundPopup or PWAReadyPopup from @vuepress/plugin-pwa/client and use its slot to customize the popup content, then pass the component path to foundComponent or readyComponent option:

    <script setup lang="ts">
    +import { PWAFoundPopup } from '@vuepress/plugin-pwa/client'
    +</script>
    +<template>
    +  <PWAFoundPopup v-slot="{ found, refresh }">
    +    <div v-if="found">
    +      New content is found.
    +      <button @click="refresh">Refresh</button>
    +    </div>
    +  </PWAFoundPopup>
    +</template>
    +
    <script setup lang="ts">
    +import { PWAReadyPopup } from '@vuepress/plugin-pwa/client'
    +</script>
    +<template>
    +  <PWAReadyPopup v-slot="{ isReady, reload }">
    +    <div v-if="isReady">
    +      New content is ready.
    +      <button @click="reload">Apply</button>
    +    </div>
    +  </PWAReadyPopup>
    +</template>
    +

    Other Options

    The plugin also provides other PWA-related options, such as Microsoft tile icon and color settings, Apple icon and so on. If you are an advanced user, you can also set generateSwConfig to configure workbox-build. Check Plugin options for more details.

    Further Reading

    For more details, please see:


    1. PWA introduction

      PWA, full name Progressive Web app. PWA standard is stipulated by W3C.

      It allows sites to install the site as an App on supported platform through a browser that supports this feature.

      See https://developer.mozilla.org/en-US/docs/Web/Progressive_web_appsopen in new window for details. ↩︎

    2. Service Worker Introduction

      1. The Service Worker will get and cache all the files registered in it during the registration process.

      2. After the registration complete, the Service Worker is activated, and starts to proxy and control all your requests.

      3. Whenever you want to initiate an access request through the browser, the Service Worker will check whether it exists in its own cache list, if it exists, it will directly return the cached result, otherwise it will call its own fetch method to get it. You can use a custom fetch method to fully control the result of the request for resources in the web page, such as providing a fallback web page when offline.

      4. Every time the user reopens the site, the Service Worker will request to the link when it was registered. If a new version of Service Worker is detected, it will update itself and start caching the list of resources registered in the new Service Worker . After the content update is successfully obtained, the Service Worker will trigger the update event. The user can be notified through this event, for example, a pop-up window will be displayed in the lower right corner, prompting the user that new content is available and allowing the user to trigger an update.

      ↩︎
    3. Manifest File

      The manifest file uses the JSON format and is responsible for declaring various information of the PWA, such as name, description, icon, and shortcut actions.

      In order for your site to be registered as a PWA, you need to meet the basic specifications of the manifest to make the browser consider the site as an installable PWA and allow users to install it.

      TIP

      For Manifest standards and specifications, please see MDN Web app manifestsopen in new window and W3C Manifestopen in new window.

      ↩︎
    4. Installable

      To let the site be registered as a PWA, the site needs to successfully register a valid service worker by itself, and declare a valid manifest file with its link in meta tag.

      The manifest file should contain at least name (or short_name) icons start_url.

      On safari, the maximum cache size of the service worker is 50 MB. ↩︎ ↩︎

    5. SSG: Static Site Generation, ↩︎

    6. SEO: Search Engine Optimization. ↩︎

    7. SPA: Single Page Application, most of them only have the homepage, and use history mode to handle routing instead of actually navigating between pages. ↩︎

    + + + diff --git a/plugins/pwa/index.html b/plugins/pwa/index.html new file mode 100644 index 0000000000..b93ce8e9d5 --- /dev/null +++ b/plugins/pwa/index.html @@ -0,0 +1,47 @@ + + + + + + + + + pwa | VuePress Ecosystem + + + + + +

    pwa

    @vuepress/plugin-pwa

    Usage

    npm i -D @vuepress/plugin-pwa@next
    +
    import { pwaPlugin } from '@vuepress/plugin-pwa'
    +
    +export default {
    +  plugins: [
    +    pwaPlugin({
    +      // options
    +    }),
    +  ],
    +}
    +
    + + + diff --git a/plugins/reading-time.html b/plugins/reading-time.html new file mode 100644 index 0000000000..da10f2f7dd --- /dev/null +++ b/plugins/reading-time.html @@ -0,0 +1,131 @@ + + + + + + + + + reading-time | VuePress Ecosystem + + + + + +

    reading-time

    @vuepress/plugin-reading-time

    This plugin will generate word count and estimated reading time for each page.

    Usage

    npm i -D @vuepress/plugin-reading-time@next
    +
    import { readingTimePlugin } from '@vuepress/plugin-reading-time'
    +
    +export default {
    +  plugins: [
    +    readingTimePlugin({
    +      // options
    +    }),
    +  ],
    +}
    +

    The plugin will inject reading time information into the readingTime of the page data, where:

    • readingTime.minutes: estimated reading time (minutes) number
    • readingTime.words: word count number

    Getting data on Node Side

    For any page, you can get estimated reading time and word count from page.data.readingTime:

    page.data.readingTime // { minutes: 3.2, words: 934 }
    +

    You can access it for further processing in the extendsPage lifecycle and other lifecycle:

    export default {
    +  // ...
    +  extendsPage: (page) => {
    +    page.data.readingTime // { minutes: 3.2, words: 934 }
    +  },
    +
    +  onInitialized: (app) => {
    +    app.pages.map((page) => {
    +      page.data.readingTime // { minutes: 3.2, words: 934 }
    +    })
    +  },
    +}
    +

    Getting data on Client Side

    You can import useReadingTimeData and useReadingTimeLocale from @vuepress/plugin-reading-time/client to get the reading time data and locale data of the current page:

    <script setup lang="ts">
    +import {
    +  useReadingTimeData,
    +  useReadingTimeLocale,
    +} from '@vuepress/plugin-reading-time/client'
    +
    +const readingTimeData = useReadingTimeData() // { minutes: 1.1, words: 100 }
    +const readingTimeLocale = useReadingTimeLocale() // { time: "1 minute", words: "100 words" }
    +</script>
    +

    Options

    wordPerMinute

    • Type: number
    • Default: 300
    • Details: Reading speed (words per minute)

    locales

    • Type: ReadingTimePluginLocaleConfig

      interface ReadingTimePluginLocaleData {
      +  /**
      +   * Word template, `$word` will be automatically replaced by actual words
      +   */
      +  word: string
      +
      +  /**
      +   * Text for less than one minute
      +   */
      +  less1Minute: string
      +
      +  /**
      +   * Time template
      +   */
      +  time: string
      +}
      +
      +interface ReadingTimePluginLocaleConfig {
      +  [localePath: string]: ReadingTimePluginLocaleData
      +}
      +
    • Required: No

    • Details:

      Locales config for reading-time plugin.

    Built-in Supported Languages
    • Simplified Chinese (zh-CN)
    • Traditional Chinese (zh-TW)
    • English (United States) (en-US)
    • German (de-DE)
    • German (Australia) (de-AT)
    • Russian (ru-RU)
    • Ukrainian (uk-UA)
    • Vietnamese (vi-VN)
    • Portuguese (Brazil) (pt-BR)
    • Polish (pl-PL)
    • French (fr-FR)
    • Spanish (es-ES)
    • Slovak (sk-SK)
    • Japanese (ja-JP)
    • Turkish (tr-TR)
    • Korean (ko-KR)
    • Finnish (fi-FI)
    • Indonesian (id-ID)
    • Dutch (nl-NL)

    Client API

    You can import and use these APIs from @vuepress/plugin-reading-time/client:

    These APIs won't throw even you disable the plugin.

    useReadingTimeData

    interface ReadingTime {
    +  /** Expect reading time in minute unit */
    +  minutes: number
    +  /** Words count of content */
    +  words: number
    +}
    +
    +const useReadingTimeData: () => ComputedRef<ReadingTime | null>
    +

    null is returned when the plugin is disabled.

    useReadingTimeLocale

    interface ReadingTimeLocale {
    +  /** Expect reading time text in locale */
    +  time: string
    +  /** Word count text in locale */
    +  words: string
    +}
    +
    +const useReadingTimeLocale: () => ComputedRef<ReadingTimeLocale>
    +

    Advanced Usage

    This plugin is targeting plugin and theme developers mostly, so we provide a "Use API":

    import { useReadingTimePlugin } from '@vuepress/plugin-reading-time'
    +
    +export default (options) => (app) => {
    +  useReadingTimePlugin(app, {
    +    // your options
    +  })
    +
    +  return {
    +    name: 'vuepress-plugin-xxx', // or vuepress-theme-xxx
    +  }
    +}
    +

    Why you should use "Use API"

    1. When you register a plugin multiple times, vuepress will gives you warning about that telling you only the first one takes effect. The useReadingTimePlugin automatically detects if the plugin is registered and avoid registering multiple times.
    2. If you access reading time data in extendsPage lifecycle, then @vuepress/plugin-reading-time must be called before your theme or plugin, otherwise you will get undefined for page.data.readingTime. The useReadingTimePlugin ensures that @vuepress/plugin-reading-time is called before your theme or plugin.

    We also provides a removeReadingTimePlugin api to remove the plugin.You can use this to ensure your call take effect or clear the plugin:

    import { useReadingTimePlugin } from '@vuepress/plugin-reading-time'
    +
    +export default (options) => (app) => {
    +  // this removes any existing reading time plugin at this time
    +  removeReadingTimePlugin(app)
    +
    +  // so this will take effect even if there is a reading time plugin registered before
    +  useReadingTimePlugin(app, {
    +    // your options
    +  })
    +
    +  return {
    +    name: 'vuepress-plugin-xxx', // or vuepress-theme-xxx
    +  }
    +}
    +
    + + + diff --git a/plugins/redirect.html b/plugins/redirect.html new file mode 100644 index 0000000000..1b44a25ae3 --- /dev/null +++ b/plugins/redirect.html @@ -0,0 +1,117 @@ + + + + + + + + + redirect | VuePress Ecosystem + + + + + +

    redirect

    @vuepress/plugin-redirect

    This plugin can automatically handle redirects for your site.

    Usage

    npm i -D @vuepress/plugin-redirect@next
    +
    import { redirectPlugin } from '@vuepress/plugin-redirect'
    +
    +export default {
    +  plugins: [
    +    redirectPlugin({
    +      // options
    +    }),
    +  ],
    +}
    +

    Control Page Redirection

    If you change the address of an existing page, you can use the redirectFrom option in Frontmatter to redirect to the address of this page, which ensures that users are redirected to the new address when they visit the old link.

    If you need to redirect an existing page to a new page, you can use the redirectTo option in Frontmatter to set the address to redirect to. This way the page will redirect to the new address when accessed.

    You can also set config with a redirect map in plugin options, see config for more details.

    Auto Locales

    The plugin can automatically redirect non-multilingual links to the multilingual pages the user needs based on the user's language preference.

    To achieve this, you need to leave the default language directory (/) blank and set autoLocale: true in plugin options. The plugin will automatically redirect to the correct page according to the user's language.

    I.E.: you need to set the following directory structure:

    .
    +├── en
    +│   ├── ...
    +│   ├── page.md
    +│   └── README.md
    +├── zh
    +│   ├── ...
    +│   ├── page.md
    +│   └── README.md
    +└── other_languages
    +    ├── ...
    +    ├── page.md
    +    └── README.md
    +

    And set locales in theme options with:

    export default {
    +  locales: {
    +    '/en/': {
    +      lang: 'en-US',
    +      // ...
    +    },
    +    '/zh/': {
    +      lang: 'zh-CN',
    +      // ...
    +    },
    +    // other languages
    +  },
    +  // ...
    +}
    +

    So when a user accesses / or /page.html, they are automatically redirected to /en/ /en/page.html and /en/ /en/page.html based on current browser language.

    Customizing fallback behavior

    Sometimes, users may add more than one language to the system settings. By default, when a site supports a preferred language, but the page not exists for the preferred language, the plugin attempts to match the alternate language set by the user.

    If you don't need to fall back to the user's alternate language, but directly match the user's preferred language, set localeFallback: false in the plugin options.

    Customizing missing behavior

    Sometimes, when a user visits a page, the document does not yet contain the language version the user needs (a common case is that the current page has not been localized in the relevant language), so the plugin needs to perform a default action, which you can customize by defaultBehavior in the plugin options:

    • "defaultLocale": Redirect to default language or first available language page (default behavior)
    • "homepage": redirect to the home page in the current language (only available if the document contains the user's language)
    • "404": Redirect to page 404 in current language (only available if the document contains the user's language)

    Customizing default locale path

    You can customize the default locale path by setting defaultLocale in the plugin options. By default, the plugin uses the first locale key in locales as the default language.

    Automatically switch languages

    The plugin supports automatically switching the link to the multilingual page that the user needs according to the user's language preference when opening a multilingual document. In order to achieve this, you need to set switchLocale in the plugin options, which can be the following two values:

    • direct: switch directly to the user language preference page without asking
    • modal: When the user's language preference is different from the current page language, show a modal asking whether to switch language

    Customizing Locale Settings

    By default, the plugin generates a locale setting by reading locale path and lang from the site's locales option. Sometimes, you may want multiple languages to hit the same path, in which case you should set localeConfig in plugin options.

    For example, you might want all English users to match to /en/ and Chinese Traditional users to /zh/, then you can set:

    redirect({
    +  localeConfig: {
    +    '/en/': ['en-US', 'en-UK', 'en'],
    +    '/zh/': ['zh-CN', 'zh-TW', 'zh'],
    +  },
    +})
    +

    Redirecting Sites

    Sometimes you may change base or use new domain for your site, so you may want the original site automatically redirects to the new one.

    To solve this, the plugin provide vp-redirect cli.

    Usage:
    +  $ vp-redirect generate [sourceDir]
    +
    +Options:
    +  --hostname <hostname>  Hostname to redirect to (E.g.: https://new.example.com/) (default: /)
    +  -c, --config <config>  Set path to config file
    +  -o, --output <output>  Set the output directory (default: .vuepress/redirect)
    +  --cache <cache>        Set the directory of the cache files
    +  -t, --temp <temp>      Set the directory of the temporary files
    +  --clean-cache          Clean the cache files before generation
    +  --clean-temp           Clean the temporary files before generation
    +  -h, --help             Display this message
    +

    You need to pass in VuePress project source dir and also set the hostname option. The redirect helper cli will initialize your VuePress project to get pages, then generate and output the redirect html files to the output directory.

    By default, the plugin will output to .vuepress/redirect directory under source directory. And you should upload it to your original site to provide redirection.

    Options

    config

    • Type: Record<string, string> | ((app: App) => Record<string, string>)

    • Details: Redirect map.

    • Example:

      When base is set to /base/:

      • redirect /base/foo.html to /base/bar.html
      • /base/baz.html to https://example.com/qux.html.
      redirect({
      +  config: {
      +    '/foo.html': '/bar.html',
      +    '/baz.html': 'https://example.com/qux.html',
      +  },
      +})
      +

      Redirect post folder to posts folder:

      redirect({
      +  hostname: 'https://example.com',
      +  config: (app) =>
      +    Object.fromEntries(
      +      app.pages
      +        .filter(({ path }) => path.startsWith('/posts/'))
      +        .map(({ path }) => [path.replace(/^\/posts\//, '/post/'), path]),
      +    ),
      +})
      +

    autoLocale

    • Type: boolean
    • Default: false
    • Details: Whether enable locales redirection.

    switchLocale

    • Type: "direct" | "modal" | false

    • Default: false

    • Details:

      Whether switch to a new locale based on user preference.

      • "direct": redirect to the new locale directly without asking
      • "modal": show a modal to let user choose whether to switch to the new locale

    localeConfig

    • Type: Record<string, string | string[]>

    • Details: Locale language config

    localeFallback

    • Type: boolean
    • Default: true
    • Details: Whether fallback to other locales user defined

    defaultBehavior

    • Type: "defaultLocale" | "homepage" | "404"
    • Default: "defaultLocale"
    • Details: Behavior when a locale version is not available for current link.

    defaultLocale

    • Type: string
    • Default: the first locale
    • Details: Default locale path.

    Frontmatter

    redirectFrom

    • Type: string | string[]
    • Details: The link which this page redirects from.

    redirectTo

    • Type: string
    • Details: The link which this page redirects to.

    Styles

    You can customize the style of the redirect popup via CSS variables:

    :root {
    +  --redirect-z-index: 1499;
    +  --redirect-bg-color: #fff;
    +  --redirect-bg-color-light: #f3f4f5;
    +  --redirect-bg-color-lighter: #eeeeee;
    +  --redirect-text-color: #2c3e50;
    +  --redirect-primary-bg-color: #3eaf7c;
    +  --redirect-primary-hover-bg-color: #4abf8a;
    +  --redirect-primary-text-color: #fff;
    +}
    +
    + + + diff --git a/plugins/register-components.html b/plugins/register-components.html new file mode 100644 index 0000000000..fc1688ffa8 --- /dev/null +++ b/plugins/register-components.html @@ -0,0 +1,85 @@ + + + + + + + + + register-components | VuePress Ecosystem + + + + + +

    register-components

    @vuepress/plugin-register-components

    Register Vue components from component files or directory automatically.

    Usage

    npm i -D @vuepress/plugin-register-components@next
    +
    import { registerComponentsPlugin } from '@vuepress/plugin-register-components'
    +
    +export default {
    +  plugins: [
    +    registerComponentsPlugin({
    +      // options
    +    }),
    +  ],
    +}
    +

    Options

    components

    • Type: Record<string, string>

    • Default: {}

    • Details:

      An object that defines name of components and their corresponding file path.

      The key will be used as the component name, and the value is an absolute path of the component file.

      If the component name from this option conflicts with componentsDir option, this option will have a higher priority.

    • Example:

    import { getDirname, path } from 'vuepress/utils'
    +
    +const __dirname = getDirname(import.meta.url)
    +
    +export default {
    +  plugins: [
    +    registerComponentsPlugin({
    +      components: {
    +        FooBar: path.resolve(__dirname, './components/FooBar.vue'),
    +      },
    +    }),
    +  ],
    +}
    +

    componentsDir

    • Type: string | null

    • Default: null

    • Details:

      Absolute path to the components directory.

      Files in this directory which are matched with componentsPatterns will be registered as Vue components automatically.

    • Example:

    import { getDirname, path } from 'vuepress/utils'
    +
    +const __dirname = getDirname(import.meta.url)
    +
    +export default {
    +  plugins: [
    +    registerComponentsPlugin({
    +      componentsDir: path.resolve(__dirname, './components'),
    +    }),
    +  ],
    +}
    +

    Components directory:

    components
    +├─ FooBar.vue
    +└─ Baz.vue
    +

    Components will be registered like this:

    import { defineAsyncComponent } from 'vue'
    +
    +app.component(
    +  'FooBar',
    +  defineAsyncComponent(() => import('/path/to/components/FooBar.vue')),
    +)
    +
    +app.component(
    +  'Baz',
    +  defineAsyncComponent(() => import('/path/to/components/Baz.vue')),
    +)
    +

    componentsPatterns

    getComponentName

    • Type: (filename: string) => string

    • Default: (filename) => path.trimExt(filename.replace(/\/|\\/g, '-'))

    • Details:

      A function to get component name from the filename.

      It will only take effect on the files in the componentsDir which are matched with the componentsPatterns.

      Notice that the filename is a filepath relative to componentsDir.

    + + + diff --git a/plugins/remove-pwa.html b/plugins/remove-pwa.html new file mode 100644 index 0000000000..2ffc30226f --- /dev/null +++ b/plugins/remove-pwa.html @@ -0,0 +1,47 @@ + + + + + + + + + remove-pwa | VuePress Ecosystem + + + + + +

    remove-pwa

    @vuepress/plugin-remove-pwa

    This plugin removes any related service worker from your VuePress site, so that users can still get updates if you removed any PWA plugin after enabling it.

    Why this plugin is needed if you used PWA plugin once?

    PWA plugins like @vuepress/plugin-pwa register service worker to your site, which will cache your site and make it available offline.

    However, if you remove pwa plugin, the old service worker will still be there, but they can never get an update because they can never found a new service worker to update to. So users will stay with the old version of your site.

    To solve this problem:

    1. A new service worker with empty contents shall be generated in the original place.
    2. The new service worker shall attempt to remove contents that old service worker cached, then it should unregister itself.

    Usage

    npm i -D @vuepress/plugin-remove-pwa@next
    +
    import { removePwaPlugin } from '@vuepress/plugin-remove-pwa'
    +
    +export default {
    +  plugins: [
    +    removePwaPlugin({
    +      // options
    +    }),
    +  ],
    +}
    +

    Options

    cachePrefix

    • Type: string
    • Default: 'workbox'
    • Details: The cache prefix for the service worker.

    swLocation

    • Type: string
    • Default: 'service-worker.js'
    • Details: The location of the old service worker.
    + + + diff --git a/plugins/rtl.html b/plugins/rtl.html new file mode 100644 index 0000000000..851fab66d7 --- /dev/null +++ b/plugins/rtl.html @@ -0,0 +1,53 @@ + + + + + + + + + rtl | VuePress Ecosystem + + + + + +

    rtl

    @vuepress/plugin-rtl

    This plugin will set direction to rtl on configured locales.

    Usage

    npm i -D @vuepress/plugin-rtl@next
    +
    import { rtlPlugin } from '@vuepress/plugin-rtl'
    +
    +export default {
    +  plugins: [
    +    rtlPlugin({
    +      // options
    +      locales: ['/ar/'],
    +    }),
    +  ],
    +}
    +

    Options

    locales

    • Type: string[]
    • Default: ['/']
    • Details: Locale path to enable rtl.

    selector

    • Type: SelectorOptions

      interface SelectorOptions {
      +  [element: string]: {
      +    [attrs: string]: string
      +  }
      +}
      +
    • Default: { 'html': { dir: 'rtl' } }

    • Details:

      Selector to enable rtl.

      The default settings mean that the dir attribute of the html element will be set to rtl in rtl locales.

    + + + diff --git a/plugins/search.html b/plugins/search.html new file mode 100644 index 0000000000..beb2cd7f8d --- /dev/null +++ b/plugins/search.html @@ -0,0 +1,111 @@ + + + + + + + + + search | VuePress Ecosystem + + + + + +

    search

    @vuepress/plugin-search

    Provide local search to your documentation site.

    TIP

    Default theme will add search box to the navbar once you configure this plugin correctly.

    This plugin may not be used directly in other themes, so you'd better refer to the documentation of your theme for more details.

    Usage

    npm i -D @vuepress/plugin-search@next
    +
    import { searchPlugin } from '@vuepress/plugin-search'
    +
    +export default {
    +  plugins: [
    +    searchPlugin({
    +      // options
    +    }),
    +  ],
    +}
    +

    Local Search Index

    This plugin will generate search index from your pages locally, and load the search index file when users enter your site. In other words, this is a lightweight built-in search which does not require any external requests.

    However, when your site has a large number of pages, the size of search index file would be very large, which could slow down the page loading speed. In this case, we recommend you to use a more professional solution - docsearch.

    Options

    locales

    • Type: Record<string, { placeholder: string }>

    • Details:

      The text of the search box in different locales.

      If this option is not specified, it will fallback to default text.

    • Example:

    export default {
    +  plugins: [
    +    searchPlugin({
    +      locales: {
    +        '/': {
    +          placeholder: 'Search',
    +        },
    +        '/zh/': {
    +          placeholder: '搜索',
    +        },
    +      },
    +    }),
    +  ],
    +}
    +

    hotKeys

    • Type: (string | HotKeyOptions)[]
    export interface HotKeyOptions {
    +    /**
    +     * Value of `event.key` to trigger the hot key
    +     */
    +    key: string;
    +    /**
    +     * Whether to press `event.altKey` at the same time
    +     *
    +     * @default false
    +     */
    +    alt?: boolean;
    +    /**
    +     * Whether to press `event.ctrlKey` at the same time
    +     *
    +     * @default false
    +     */
    +    ctrl?: boolean;
    +    /**
    +     * Whether to press `event.shiftKey` at the same time
    +     *
    +     * @default false
    +     */
    +    shift?: boolean;
    +}
    +
    • Default: ['s', '/']

    • Details:

      Specify the event.keyopen in new window of the hotkeys.

      When hotkeys are pressed, the search box input will be focused.

      Set to an empty array to disable hotkeys.

    maxSuggestions

    • Type: number

    • Default: 5

    • Details:

      Specify the maximum number of search results.

    isSearchable

    • Type: (page: Page) => boolean

    • Default: () => true

    • Details:

      A function to determine whether a page should be included in the search index.

      • Return true to include the page.
      • Return false to exclude the page.
    • Example:

    export default {
    +  plugins: [
    +    searchPlugin({
    +      // exclude the homepage
    +      isSearchable: (page) => page.path !== '/',
    +    }),
    +  ],
    +}
    +

    getExtraFields

    • Type: (page: Page) => string[]

    • Default: () => []

    • Details:

      A function to add extra fields to the search index of a page.

      By default, this plugin will use page title and headers as the search index. This option could help you to add more searchable fields.

    • Example:

    export default {
    +  plugins: [
    +    searchPlugin({
    +      // allow searching the `tags` frontmatter
    +      getExtraFields: (page) => page.frontmatter.tags ?? [],
    +    }),
    +  ],
    +}
    +

    Styles

    You can customize the style of the search box via CSS variables:

    :root {
    +  --search-bg-color: #ffffff;
    +  --search-accent-color: #3eaf7c;
    +  --search-text-color: #2c3e50;
    +  --search-border-color: #eaecef;
    +  --search-item-text-color: #5d81a5;
    +  --search-item-focus-bg-color: #f3f4f5;
    +  --search-input-width: 8rem;
    +  --search-result-width: 20rem;
    +}
    +

    Components

    • Details:

      This plugin will register a <SearchBox /> component globally, and you can use it without any props.

      Put this component to where you want to place the search box. For example, default theme puts this component to the end of the navbar.

    TIP

    This component is mainly used for theme development. You don't need to use it directly in most cases.

    + + + diff --git a/plugins/seo/config.html b/plugins/seo/config.html new file mode 100644 index 0000000000..c7ca2aa016 --- /dev/null +++ b/plugins/seo/config.html @@ -0,0 +1,81 @@ + + + + + + + + + Config | VuePress Ecosystem + + + + + +

    Config

    hostname

    • Type: string

    • Required: Yes

    • Details:

      Deploy hostname.

    author

    • Type: Author

      type AuthorName = string
      +
      +interface AuthorInfo {
      +  /**
      +   * Author name
      +   */
      +  name: string
      +
      +  /**
      +   * Author website
      +   */
      +  url?: string
      +
      +  /**
      +   * Author email
      +   */
      +  email?: string
      +}
      +
      +type Author = AuthorName | AuthorName[] | AuthorInfo | AuthorInfo[]
      +
    • Required: No

    • Details:

      Default author.

    autoDescription

    • Type: boolean

    • Default: true

    • Details:

      Whether generate description automatically

    canonical

    • Type: string | ((page: Page) => string | null)

    • Details:

      Canonical link

    fallBackImage

    • Type: string

    • Details:

      Fallback Image link when no image are found

    restrictions

    • Type: string

    • Details:

      The age rating of the content, the format is [int]+, such as "13+".

    twitterID

    • Type: string

    • Details:

      Fill in your twitter username.

    isArticle

    • Type: (page: Page) => boolean

    • Details:

      Use this option to judge whether the page is an article.

    ogp

    • Type:

      function ogp(
      +  /** OGP info inferred by plugin */
      +  ogp: SeoContent,
      +  /** Page Object */
      +  page: Page,
      +  /** VuePress App */
      +  app: App,
      +): SeoContent
      +
    • Required: No

    • Details:

      Custom OPG Generator.

      You can use this options to edit OGP tags.

    jsonLd

    • Type:

      function jsonLd(
      +  /** JSON-LD Object inferred by plugin */
      +  jsonLD: ArticleSchema | BlogPostingSchema | WebPageSchema,
      +  /** Page Object */
      +  page: Page,
      +  /** VuePress App */
      +  app: App,
      +): ArticleSchema | BlogPostingSchema | WebPageSchema
      +
    • Required: No

    • Details:

      Custom JSON-LD Generator.

      You can use this options to edit JSON-LD properties.

    customHead

    • Type:

      function customHead(
      +  /** Head tag config */
      +  head: HeadConfig[],
      +  /** Page Object */
      +  page: Page,
      +  /** VuePress App */
      +  app: App,
      +): void
      +
    • Required: No

    • Details:

      You can use this options to edit tags injected to <head>.

    + + + diff --git a/plugins/seo/guide.html b/plugins/seo/guide.html new file mode 100644 index 0000000000..5213100464 --- /dev/null +++ b/plugins/seo/guide.html @@ -0,0 +1,73 @@ + + + + + + + + + Guide | VuePress Ecosystem + + + + + +

    Guide

    This plugin will make your site fully support Open Content Protocol OGPopen in new window and JSON-LD 1.1open in new window to enhance the SEO of the site.

    Out of Box

    The plugin works out of the box. Without any config, it will extract information from the page content as much as possible to complete the necessary tags required by OGP and JSON-LD.

    By default, the plugin will read the site config and page frontmatter to automatically generate tags as much as possible. Such as site name, page title, page type, writing date, last update date, and article tags are all automatically generated.

    The following are the <meta> tags and their values that will be injected into <head> by default:

    Default OGP Generation

    The following are the <meta> tags and their value injected into <head> by default to satisfy OGP:

    Meta NameValue
    og:urloptions.hostname + path
    og:site_namesiteConfig.title
    og:titlepage.title
    og:descriptionpage.frontmatter.description || auto generated (when autoDescription is true in plugin options)
    og:type"article"
    og:imageoptions.hostname + page.frontmatter.image ||first image in page || fallbackImage in plugin options
    og:updated_timepage.git.updatedTime
    og:localepage.lang
    og:locale:alternateOther languages in siteData.locales
    twitter:card"summary_large_image" (only available when image found)
    twitter:image:altpage.title (only available when image found)
    article:authorpage.frontmatter.author || options.author
    article:tagpage.frontmatter.tags || page.frontmatter.tag
    article:published_timepage.frontmatter.date || page.git.createdTime
    article:modified_timepage.git.updatedTime

    Default JSON-LD Generation

    Property NameValue
    @context"https://schema.org"
    @type"NewsArticle"
    headlinepage.title
    imageimage in page || options.hostname + page.frontmatter.image || siteFavIcon in plugin options
    datePublishedpage.frontmatter.date || page.git.createdTime
    dateModifiedpage.git.updatedTime
    authorpage.frontmatter.author || options.author

    Setting Tags Directly

    You can configure the head option in the page's frontmatter to add specific tags to the page <head> to enhance SEO. For example:

    ---
    +head:
    +  - - meta
    +    - name: keywords
    +      content: SEO plugin
    +---
    +

    Will automatically inject <meta name="keywords" content="SEO plugin" />.

    Customize Generation

    The plugin also gives you full control over the build logic.

    Page Type

    For most pages, there are basically only two types: articles and website, so the plugin provides the isArticle option to allow you to provide logic for identifying articles.

    The option accepts a function in the format (page: Page) => boolean, by default all non-home pages generated from Markdown files are treated as articles.

    TIP

    If a page does fit into the "unpopular" genre like books, music, etc., you can handle them by setting the three options below.

    OGP

    You can use the plugin options ogp to pass in a function to modify the default OGP object to your needs and return it.

    function ogp(
    +  /** OGP Object inferred by plugin */
    +  ogp: SeoContent,
    +  /** Page Object */
    +  page: Page,
    +  /** VuePress App */
    +  app: App,
    +): SeoContent
    +

    For detailed parameter structure, see Config.

    For example, if you are using a third-party theme and set a banner in frontmatter for each article according to the theme requirements, then you can pass in the following ogp:

    seoPlugin({
    +  ogp: (ogp, page) => ({
    +    ...ogp,
    +    'og:image': page.frontmatter.banner || ogp['og:image'],
    +  }),
    +})
    +

    JSON-LD

    Like OGP, you can use the plugin options jsonLd to pass in a function to modify the default JSON-LD object to your needs and return it.

    function jsonLd(
    +  /** JSON-LD Object inferred by plugin */
    +  jsonLD: ArticleSchema | BlogPostingSchema | WebPageSchema,
    +  /** Page Object */
    +  page: Page,
    +  /** VuePress App */
    +  app: App,
    +): ArticleSchema | BlogPostingSchema | WebPageSchema
    +

    If you are deploying your content to different sites, or same content under different URLs, you may need to set canonical option to provide a "Canonical Link" for your page. You can either set a string which will be appended before page route link, or adding a custom function (page: Page) => string | null to return a canonical link if necessary.

    Example

    If your sites are deployed under docs directory in example.com, but available in:

    • http://example.com/docs/xxx
    • https://example.com/docs/xxx
    • http://www.example.com/docs/xxx
    • https://www.example.com/docs/xxx (primary)

    To let search engine results always be the primary choice, you may need to set canonical to https://www.example.com/docs/, so that search engine will know that the fourth URL is preferred to be indexed.

    Customize head Tags

    Sometimes you may need to fit other protocols or provide the corresponding SEO tags in the format provided by other search engines. In this case, you can use the customHead option, whose type is:

    function customHead(
    +  /** Head tag config */
    +  head: HeadConfig[],
    +  /** Page Object */
    +  page: Page,
    +  /** VuePress App */
    +  app: App,
    +): void
    +

    You should modify the head array in this function directly.

    SEO Introduction

    Search engine optimization (SEO) is the process of improving the quality and quantity of site traffic to a site or a web page from search engines. SEO targets unpaid traffic (known as "natural" or "organic" results) rather than direct traffic or paid traffic. Unpaid traffic may originate from different kinds of searches, including image search, video search, academic search, news search, and industry-specific vertical search engines.

    As an internet marketing strategy, SEO considers how search engines work, the computer-programmed algorithms that dictate search engine behavior, what people search for, the actual search terms or keywords typed into search engines, and which search engines are preferred by their targeted audience. SEO is performed because a site will receive more visitors from a search engine when sites rank higher on the search engine results page (SERP). These visitors can then potentially be converted into customers.

    You can use Google Rich Media Structure Test Toolopen in new window to test this site.

    + + + diff --git a/plugins/seo/index.html b/plugins/seo/index.html new file mode 100644 index 0000000000..dc7da99a59 --- /dev/null +++ b/plugins/seo/index.html @@ -0,0 +1,47 @@ + + + + + + + + + seo | VuePress Ecosystem + + + + + +

    seo

    @vuepress/plugin-seo

    Usage

    npm i -D @vuepress/plugin-seo@next
    +
    import { seoPlugin } from '@vuepress/plugin-seo'
    +
    +export default {
    +  plugins: [
    +    seoPlugin({
    +      // options
    +    }),
    +  ],
    +}
    +
    + + + diff --git a/plugins/shiki.html b/plugins/shiki.html new file mode 100644 index 0000000000..3ad2c6c221 --- /dev/null +++ b/plugins/shiki.html @@ -0,0 +1,48 @@ + + + + + + + + + shiki | VuePress Ecosystem + + + + + +

    shiki

    @vuepress/plugin-shiki

    This plugin will enable syntax highlighting for markdown code fence with Shikiopen in new window.

    TIP

    Shikiopen in new window is the syntax highlighter being used by VSCode. It has higher fidelity, but it could be slower than Prism.jsopen in new window, especially when you have a lot of code blocks.

    You could consider disabling this plugin in dev mode to get better development experience.

    Usage

    npm i -D @vuepress/plugin-shiki@next
    +
    import { shikiPlugin } from '@vuepress/plugin-shiki'
    +
    +export default {
    +  plugins: [
    +    shikiPlugin({
    +      // options
    +      langs: ['ts', 'json', 'vue', 'md', 'bash', 'diff'],
    +    }),
    +  ],
    +}
    +

    Options

    langs

    • Type: ShikiLang[]

    • Details:

      Languages of code blocks to be parsed by Shiki.

      This option will be forwarded to getHighlighter() method of Shiki.

      You'd better provide the languages list you are using explicitly, otherwise Shiki will load all languages and can affect performance.

    • Also see:

    theme

    themes

    • Type: Record<'dark' | 'light', ShikiTheme>

    • Details:

      Dark / Light Dual themes of Shiki.

      This option will be forwarded to codeToHtml() method of Shiki.

    • Also see:

    + + + diff --git a/plugins/sitemap/config.html b/plugins/sitemap/config.html new file mode 100644 index 0000000000..f4e879cf98 --- /dev/null +++ b/plugins/sitemap/config.html @@ -0,0 +1,37 @@ + + + + + + + + + Config | VuePress Ecosystem + + + + + +

    Config

    hostname

    • Type: string

    • Required: Yes

    • Details:

      The domain name where the current site is deployed, the plugin needs this option to work.

    extraUrls

    • Type: string[]

    • Details:

      Extra link to be included.

      If you have some links not including in VuePress Router (normally in public directory or generated by other tools directly), you may need this option.

    • Example: ['/about.html', '/api/']

    excludePaths

    • Type: string[]

    • Default: ['/404.html']

    • Details:

      Urls excluding from sitemap, starting with absolute path.

      By default, all the urls generated by VuePress (excluding 404 page) will be added into sitemap.

    devServer

    • Type: boolean

    • Default: false

    • Details:

      Whether enabled in devServer.

    TIP

    For performance reasons, we do not provide hot reload. Reboot your devServer to sync your changes.

    devHostname

    • Type: string

    • Default: "http://localhost:${port}"

    • Details:

      Hostname to use in devServer

    sitemapFilename

    • Type: string

    • Default value: "sitemap.xml"

    • Details:

      The output filename, relative to output directory.

    sitemapXSLFilename

    • Type: string

    • Default value: "sitemap.xsl"

    • Details:

      Output xsl filename, relative to dest folder.

    sitemapXSLTemplate

    • Type: string

    • Default value: "@vuepress/plugin-sitemap/templates/sitemap.xsl"

    • Details:

      XSL content used as template.

    changefreq

    • Type: "always" | "hourly" | "daily" | "weekly" |"monthly" | "yearly" | "never"

    • Default value: "daily"

    • Details:

      Page default update frequency, will be overridden by sitemap.changefreq in Frontmatter.

    priority

    • Type: number

    • Default: 0.5

    • Details:

      Page priority, from 0 to 1.

    modifyTimeGetter

    • Type: (page: Page, app: App) => string

    • Details:

      Last modify time getter. By default, the plugin will use the timestamp generated by git plugin.

    + + + diff --git a/plugins/sitemap/frontmatter.html b/plugins/sitemap/frontmatter.html new file mode 100644 index 0000000000..25f432b6fe --- /dev/null +++ b/plugins/sitemap/frontmatter.html @@ -0,0 +1,37 @@ + + + + + + + + + Frontmatter | VuePress Ecosystem + + + + + +

    Frontmatter

    sitemap

    • Type: SitemapFrontmatterOptions | false

    • Details:

      false means exclude the page from sitemap.

    sitemap.changefreq

    • Type: "always" | "hourly" | "daily" | "weekly" | "monthly" | "yearly" | "never"

    • Default: "daily"

    • Details:

      Page default update frequency. This will override changefreq in Plugin Options.

    sitemap.priority

    • Type: number

    • Default: 0.5

    • Details:

      Page priority, range from 0 to 1.

    + + + diff --git a/plugins/sitemap/guide.html b/plugins/sitemap/guide.html new file mode 100644 index 0000000000..21f69b4d6b --- /dev/null +++ b/plugins/sitemap/guide.html @@ -0,0 +1,45 @@ + + + + + + + + + Guide | VuePress Ecosystem + + + + + +

    Guide

    This plugin will automatically generate a Sitemap for your site. To let this plugin work, you need to pass the deployed domain name to the hostname option of the plugin. If you want to preview in devServer, set devServer options.

    The plugin will automatically generate the last update time of the page based on the Git timestamp of the page, and will also declare the alternative links of the page in other languages according to the locales' config.

    By default, all site links except 404 page will be added to the Sitemap.

    To add other pages to the Sitemap outside the VuePress project page, please turn them into an array and pass to the extraUrls plugin option.

    If you don't want certain pages to appear in the sitemap, you can turn their paths into an array and pass to the excludePaths plugin option, or set sitemap to false in the frontmatter of the corresponding page.

    Output Location

    You can also control the output link through the sitemapFilename option of the plugin, the link is relative to output directory. By default, the plugin will use sitemap.xml.

    Change Frequency

    The default update cycle of the page is daily (every day). To modify the entire page cycle, please set changefreq in the plugin options. You can also set sitemap.changefreq in the frontmatter of the page. Note that page has a higher priority.

    The legal frequencies are:

    • "always"
    • "hourly"
    • "daily"
    • "weekly"
    • "monthly"
    • "yearly"
    • "never"

    Priority

    You can set priority in the plugin to provide a default value. At the same time you can set the priority for each page through sitemap.priority in frontmatter. Acceptable values are floating point numbers from 0 to 1.

    Modify Time

    You can use option modifyTimeGetter to return a time in ISO string format, which is generated by the Git plugin by default.

    The following is an example based on the last modification time of a file.

    // Based on file last modified time
    +({
    +   modifyTimeGetter: (page, app) =>
    +     fs.statSync(app.dir.source(page.filePathRelative)).mtime.toISOString();
    +})
    +

    Sitemap Intro

    Sitemaps provide SEO (Search Engine Optimization):

    • Provide search engine spiders with links of the entire site;
    • Provide some links for search engine spiders to dynamic pages or pages that are difficult to reach by other methods;
    • If a visitor attempts to access a URL that does not exist within the site's domain, the visitor will be directed to a "file not found" error page, and the sitemap can be used as a navigation page.

    A sitemap enhances SEO by making all pages findable.

    Most search engines only follow a limited number of links within a page, so when the site is very large, a sitemap becomes essential to make everything on the site accessible to search engines and visitors.

    Sitemaps is a protocol for site administrators to publish pages that can be crawled on a site to search engine spiders. The content of sitemap files must follow the definition in XML format. Each URL can contain the update period and last update time, the priority of the URL across the site. This allows search engines to crawl site content better and more efficiently.

    Together with robots.txt

    Sitemap is basically used by search engines, when using this plugin, you'd better ensure that you have a valid robots.txt in the .vuepress/public directory to allow search engines spiders to visit your site. The simplest robots.txt is as follows (allow all search engines to access all paths)

    User-agent: *
    +
    +Allow: /
    +
    + + + diff --git a/plugins/sitemap/index.html b/plugins/sitemap/index.html new file mode 100644 index 0000000000..35072a41ad --- /dev/null +++ b/plugins/sitemap/index.html @@ -0,0 +1,47 @@ + + + + + + + + + sitemap | VuePress Ecosystem + + + + + +

    sitemap

    @vuepress/plugin-sitemap

    Usage

    npm i -D @vuepress/plugin-sitemap@next
    +
    import { sitemapPlugin } from '@vuepress/plugin-sitemap'
    +
    +export default {
    +  plugins: [
    +    sitemapPlugin({
    +      // options
    +    }),
    +  ],
    +}
    +
    + + + diff --git a/plugins/theme-data.html b/plugins/theme-data.html new file mode 100644 index 0000000000..c79f0bd5e1 --- /dev/null +++ b/plugins/theme-data.html @@ -0,0 +1,87 @@ + + + + + + + + + theme-data | VuePress Ecosystem + + + + + +

    theme-data

    @vuepress/plugin-theme-data

    Provide client data for your theme, with VuePress i18n support.

    This plugin is mainly used to develop themes, and has been integrated into the default theme. You won't need to use it directly in most cases.

    For theme authors, this plugin will help you to use the same i18n mechanism as VuePress and the default theme. But if you don't want to provide i18n support, or you want to implement in your own way, you don't need this plugin.

    Usage

    npm i -D @vuepress/plugin-theme-data@next
    +
    import { themeDataPlugin } from '@vuepress/plugin-theme-data'
    +
    +export default {
    +  plugins: [
    +    themeDataPlugin({
    +      // options
    +    }),
    +  ],
    +}
    +

    Options

    themeData

    • Type: ThemeData

    • Default: {}

    • Details:

      The theme data object that you want to use in client side.

      You can provide theme data in Node side via this option, and use it in client side via useThemeData and useThemeLocaleData.

    • Example:

    export default {
    +  plugins: [
    +    themeDataPlugin({
    +      themeData: {
    +        foo: 'foo',
    +        locales: {
    +          '/zh/': {
    +            foo: 'zh-foo',
    +          },
    +        },
    +      },
    +    }),
    +  ],
    +}
    +

    WARNING

    The theme data object will be processed by JSON.stringify() before forwarding to client side, so you should ensure that you are providing a JSON-friendly object.

    Composition API

    useThemeData

    • Details:

      Returns the theme data ref object.

      The value is provided by themeData option.

    • Example:

    import { useThemeData } from '@vuepress/plugin-theme-data/client'
    +import type { ThemeData } from '@vuepress/plugin-theme-data/client'
    +
    +type MyThemeData = ThemeData<{
    +  foo: string
    +}>
    +
    +export default {
    +  setup() {
    +    const themeData = useThemeData<MyThemeData>()
    +    console.log(themeData.value)
    +  },
    +}
    +

    useThemeLocaleData

    • Details:

      Returns the theme data ref object in current locale.

      The properties of current locale has been merged into the root-level properties.

    • Example:

    import { useThemeLocaleData } from '@vuepress/plugin-theme-data/client'
    +import type { ThemeData } from '@vuepress/plugin-theme-data/client'
    +
    +type MyThemeData = ThemeData<{
    +  foo: string
    +}>
    +
    +export default {
    +  setup() {
    +    const themeLocaleData = useThemeLocaleData<MyThemeData>()
    +    console.log(themeLocaleData.value)
    +  },
    +}
    +
    + + + diff --git a/plugins/toc.html b/plugins/toc.html new file mode 100644 index 0000000000..43ebc14eb4 --- /dev/null +++ b/plugins/toc.html @@ -0,0 +1,110 @@ + + + + + + + + + toc | VuePress Ecosystem + + + + + +

    toc

    @vuepress/plugin-toc

    This plugin will provide a table-of-contents (TOC) component.

    Usage

    npm i -D @vuepress/plugin-toc@next
    +
    import { tocPlugin } from '@vuepress/plugin-toc'
    +
    +export default {
    +  plugins: [
    +    tocPlugin({
    +      // options
    +    }),
    +  ],
    +}
    +

    Differences with Markdown TOC Syntax

    Similar to the Table of Contents Markdown Syntax, the TOC component that provided by this plugin could be used in your markdown content directly:

    <!-- markdown toc syntax -->
    +
    +[[toc]]
    +
    +<!-- vue toc component -->
    +<Toc />
    +

    Both of them can be pre-rendered correctly in build mode. However, there are some differences between them.

    The markdown syntax [[toc]] could only be used in markdown files. It is parsed by markdown-it, and the generated TOC is static content.

    The component <Toc/> could be used in both markdown files and vue files. It is loaded by vue, and the generated TOC is a vue component.

    This plugin could work together with @vuepress/plugin-active-header-links by setting the headerLinkSelector to match the linkClass option. When the page scroll to a certain header anchor, this corresponding link will be added linkActiveClass class name.

    Therefore, this plugin is more useful for theme developers.

    Options

    componentName

    • Type: string

    • Default: 'Toc'

    • Details:

      Specify the name of the TOC component.

    defaultPropsOptions

    • Type: Partial<TocPropsOptions>

    • Default: {}

    • Details:

      Override the default values of the component options prop.

    Component Props

    The TOC component also accepts props for customization.

    <template>
    +  <Toc :headers="headers" :options="options" />
    +</template>
    +

    headers

    • Type: PageHeader[]
    interface PageHeader {
    +  level: number
    +  title: string
    +  slug: string
    +  children: PageHeader[]
    +}
    +
    • Details:

      Specify the headers array to render.

      If this prop is not specified, the headers of current page will be used.

    options

    • Type: Partial<TocPropsOptions>
    interface TocPropsOptions {
    +  containerTag: string
    +  containerClass: string
    +  listClass: string
    +  itemClass: string
    +  linkTag: 'a' | 'RouterLink' | 'RouteLink'
    +  linkClass: string
    +  linkActiveClass: string
    +  linkChildrenActiveClass: string
    +}
    +
    const defaultOptions = {
    +  containerTag: 'nav',
    +  containerClass: 'vuepress-toc',
    +  listClass: 'vuepress-toc-list',
    +  itemClass: 'vuepress-toc-item',
    +  linkTag: 'RouteLink',
    +  linkClass: 'vuepress-toc-link',
    +  linkActiveClass: 'active',
    +  linkChildrenActiveClass: 'active',
    +}
    +
    • Details:

      Customize the TOC component.

      If the containerTag is set to an empty string '', the <nav> container will be removed totally.

    • Example:

      The rendered TOC component with default options looks like:

    <template>
    +  <!-- container -->
    +  <nav class="vuepress-toc">
    +    <!-- list -->
    +    <ul class="vuepress-toc-list">
    +      <!-- item -->
    +      <li class="vuepress-toc-item">
    +        <!-- link -->
    +        <RouteLink class="vuepress-toc-link" to="#foo">Foo</RouteLink>
    +      </li>
    +      <!-- item with children -->
    +      <li class="vuepress-toc-item">
    +        <!-- link (children active) -->
    +        <RouteLink class="vuepress-toc-link active" to="#bar">Bar</RouteLink>
    +        <!-- list (children) -->
    +        <ul class="vuepress-toc-list">
    +          <!-- item -->
    +          <li class="vuepress-toc-item">
    +            <!-- link (active) -->
    +            <RouteLink class="vuepress-toc-link active" to="#bar-child">
    +              Bar Child
    +            </RouteLink>
    +          </li>
    +        </ul>
    +      </li>
    +    </ul>
    +  </nav>
    +</template>
    +
    + + + diff --git a/themes/default/components.html b/themes/default/components.html new file mode 100644 index 0000000000..2048781240 --- /dev/null +++ b/themes/default/components.html @@ -0,0 +1,68 @@ + + + + + + + + + Built-in Components | VuePress Ecosystem + + + + + +

    Built-in Components

    @vuepress/theme-default

    Badge badge

    • Props:

      • type
        • Type: 'tip' | 'warning' | 'danger'
        • Default: 'tip'
      • text
        • Type: string
        • Default: ''
      • vertical
        • Type: 'top' | 'middle' | 'bottom' | undefined
        • Default: undefined
    • Example:

    Input

    - VuePress - <Badge type="tip" text="v2" vertical="top" />
    +- VuePress - <Badge type="warning" text="v2" vertical="middle" />
    +- VuePress - <Badge type="danger" text="v2" vertical="bottom" />
    +

    Output

    • VuePress - v2
    • VuePress - v2
    • VuePress - v2

    CodeGroup

    CodeGroupItem

    • Props:

      • title
        • Type: string
        • Required: true
      • active
        • Type: boolean
        • Default: false
    • Details:

      This component must be placed inside a CodeGroup component.

      Use the active prop to set the initial active item, or the first item will be activated by default.

    • Example:

    Input

    <CodeGroup>
    +  <CodeGroupItem title="pnpm">
    +
    +```bash:no-line-numbers
    +pnpm install
    +```
    +
    +  </CodeGroupItem>
    +
    +  <CodeGroupItem title="yarn">
    +
    +```bash:no-line-numbers
    +yarn install
    +```
    +
    +  </CodeGroupItem>
    +
    +  <CodeGroupItem title="npm" active>
    +
    +```bash:no-line-numbers
    +npm install
    +```
    +
    +  </CodeGroupItem>
    +</CodeGroup>
    +

    Output

    pnpm install
    +
    yarn install
    +
    npm install
    +

    WARNING

    You must add an empty line between the starting tag of <CodeGroupItem> and the code fence, otherwise the code fence will not be parsed correctly by Markdown.

    All content must be valid Markdown first, and then a Vue SFC.

    Learn more: Cookbook > Markdown and Vue SFCopen in new window

    Alternatively, you can use the custom containers.

    + + + diff --git a/themes/default/config.html b/themes/default/config.html new file mode 100644 index 0000000000..15daedf7c7 --- /dev/null +++ b/themes/default/config.html @@ -0,0 +1,177 @@ + + + + + + + + + Config | VuePress Ecosystem + + + + + +

    Config

    Basic Config

    hostname

    • Type: string

    • Details:

      Hostname to be deployed, e.g.: https://example.com

    locales

    • Type: { [path: string]: Partial<DefaultThemeLocaleData> }

    • Default: {}

    • Details:

      Specify locales for i18n support.

      All the options inside the Locale Config section can be used in locales.

      This option will only take effect in default theme, so don't confuse with locales in Site Config.

    • Also see:

    Locale Config

    Config of this section can be used as normal config, and can also be used in the locales option.

    colorMode

    colorModeSwitch

    home

    • Type: string

    • Default: /

    • Details:

      Specify the path of the homepage.

      This will be used for:

      • the logo link of the navbar
      • the back to home link of the 404 page
    • Type: false | (NavbarItem | NavbarGroup | string)[]

    • Default: []

    • Details:

      Configuration of navbar.

      Set to false to disable navbar.

      To configure the navbar items, you can set it to a navbar array, each item of which could be a NavbarItem object, a NavbarGroup object, or a string:

      • A NavbarItem object should have a text field and a link field, could have an optional activeMatch field.
      • A NavbarGroup object should have a text field and a children field. The children field should be a navbar array, too.
      • A string should be the path to the target page file. It will be converted to a NavbarItem object, using the page title as text, and the page route path as link.
    • Example 1:

    export default {
    +  theme: defaultTheme({
    +    navbar: [
    +      // NavbarItem
    +      {
    +        text: 'Foo',
    +        link: '/foo/',
    +      },
    +      // NavbarGroup
    +      {
    +        text: 'Group',
    +        children: ['/group/foo.md', '/group/bar.md'],
    +      },
    +      // string - page file path
    +      '/bar/README.md',
    +    ],
    +  }),
    +}
    +
    • Example 2:
    export default {
    +  theme: defaultTheme({
    +    navbar: [
    +      // nested group - max depth is 2
    +      {
    +        text: 'Group',
    +        children: [
    +          {
    +            text: 'SubGroup',
    +            children: ['/group/sub/foo.md', '/group/sub/bar.md'],
    +          },
    +        ],
    +      },
    +      // control when should the item be active
    +      {
    +        text: 'Group 2',
    +        children: [
    +          {
    +            text: 'Always active',
    +            link: '/',
    +            // this item will always be active
    +            activeMatch: '/',
    +          },
    +          {
    +            text: 'Active on /foo/',
    +            link: '/not-foo/',
    +            // this item will be active when current route path starts with /foo/
    +            // regular expression is supported
    +            activeMatch: '^/foo/',
    +          },
    +        ],
    +      },
    +    ],
    +  }),
    +}
    +
    • Type: null | string

    • Details:

      Specify the url of logo image.

      The logo image will be displayed at the left end of the navbar.

      Set to null to disable logo.

    • Example:

    export default {
    +  theme: defaultTheme({
    +    // public file path
    +    logo: '/hero.png',
    +    // url
    +    logo: 'https://vuejs.org/images/logo.png',
    +  }),
    +}
    +

    logoDark

    logoAlt

    • Type: null | string

    • Details:

      Specify the alt text of the logo image.

      If not specified, defaults to be the same as the site title.

    repo

    • Type: string

    • Details:

      Specify the repository url of your project.

      This will be used as the link of the repository link, which will be displayed as the last item of the navbar.

    export default {
    +  theme: defaultTheme({
    +    // If you set it in the form of `organization/repository`
    +    // we will take it as a GitHub repo
    +    repo: 'vuejs/vuepress',
    +    // You can also set it to a URL directly
    +    repo: 'https://gitlab.com/foo/bar',
    +  }),
    +}
    +
    • Type: false | 'auto' | SidebarConfigArray | SidebarConfigObject

    • Default: 'auto'

    • Details:

      Configuration of sidebar.

      You can override this global option via sidebar frontmatter in your pages.

      Set to false to disable sidebar.

      If you set it to 'auto', the sidebar will be automatically generated from the page headers.

      To configure the sidebar items manually, you can set this option to a sidebar array, each item of which could be a SidebarItem object or a string:

      • A SidebarItem object should have a text field, could have an optional link field, an optional children field and an optional collapsible field. The children field should be a sidebar array. The collapsible field controls whether the item is collapsible.
      • A string should be the path to the target page file. It will be converted to a SidebarItem object, whose text is the page title, link is the page route path, and children is automatically generated from the page headers.

      If you want to set different sidebar for different sub paths, you can set this option to a sidebar object:

      • The key should be the path prefix.
      • The value should be a sidebar array or set to 'heading' to automatically generate the sidebar from the page headers for just the corresponding path.
    • Example 1:

    export default {
    +  theme: defaultTheme({
    +    // sidebar array
    +    // all pages will use the same sidebar
    +    sidebar: [
    +      // SidebarItem
    +      {
    +        text: 'Foo',
    +        link: '/foo/',
    +        children: [
    +          // SidebarItem
    +          {
    +            text: 'github',
    +            link: 'https://github.com',
    +            children: [],
    +          },
    +          // string - page file path
    +          '/foo/bar.md',
    +        ],
    +      },
    +      // string - page file path
    +      '/bar/README.md',
    +    ],
    +  }),
    +}
    +
    • Example 2:
    export default {
    +  theme: defaultTheme({
    +    // sidebar object
    +    // pages under different sub paths will use different sidebar
    +    sidebar: {
    +      '/guide/': [
    +        {
    +          text: 'Guide',
    +          children: ['/guide/introduction.md', '/guide/getting-started.md'],
    +        },
    +      ],
    +      '/reference/': 'heading',
    +    },
    +  }),
    +}
    +
    • Example 3:
    export default {
    +  theme: defaultTheme({
    +    // collapsible sidebar
    +    sidebar: {
    +      '/reference/': [
    +        {
    +          text: 'VuePress Reference',
    +          collapsible: true,
    +          children: ['/reference/cli.md', '/reference/config.md'],
    +        },
    +        {
    +          text: 'Bundlers Reference',
    +          collapsible: true,
    +          children: [
    +            '/reference/bundler/vite.md',
    +            '/reference/bundler/webpack.md',
    +          ],
    +        },
    +      ],
    +    },
    +  }),
    +}
    +

    sidebarDepth

    • Type: number

    • Default: 2

    • Details:

      Set the maximum depth of the sidebar children which are automatically generated from the page headers.

      • Set to 0 to disable all levels of headers.
      • Set to 1 to include <h2> headers.
      • Set to 2 to include <h2> and <h3> headers.
      • ...

      The max value depends on which levels of headers you have extracted via markdown.headers.levelopen in new window.

      The default value of markdown.headers.level is [2, 3], so the default max value of sidebarDepth is 2.

      You can override this global option via sidebarDepth frontmatter in your pages.

    • Type: boolean

    • Default: true

    • Details:

      Enable the edit this page link or not.

      You can override this global option via editLink frontmatter in your pages.

    editLinkPattern

    • Type: string

    • Details:

      Specify the pattern of the edit this page link.

      This will be used for generating the edit this page link.

      If you don't set this option, the pattern will be inferred from the docsRepo option. But if your documentation repository is not hosted on a common platform, for example, GitHub, GitLab, Bitbucket, Gitee, etc., you have to set this option explicitly to make the edit this page link work.

    • Usage:

      PatternDescription
      :repoThe docs repo url, i.e. docsRepo
      :branchThe docs repo branch, i.e. docsBranch
      :pathThe path of the page source file, i.e. docsDir joins the relative path of the page file
    • Example:

    export default {
    +  theme: defaultTheme({
    +    docsRepo: 'https://gitlab.com/owner/name',
    +    docsBranch: 'master',
    +    docsDir: 'docs',
    +    editLinkPattern: ':repo/-/edit/:branch/:path',
    +  }),
    +}
    +

    The generated link will look like 'https://gitlab.com/owner/name/-/edit/master/docs/path/to/file.md'.

    docsRepo

    • Type: string

    • Details:

      Specify the repository url of your documentation source files.

      This will be used for generating the edit this page link.

      If you don't set this option, it will use the repo option by default. But if your documentation source files are in a different repository, you will need to set this option.

    docsBranch

    • Type: string

    • Default: 'main'

    • Details:

      Specify the repository branch of your documentation source files.

      This will be used for generating the edit this page link.

    docsDir

    • Type: string

    • Default: ''

    • Details:

      Specify the directory of your documentation source files in the repository.

      This will be used for generating the edit this page link.

    lastUpdated

    • Type: boolean

    • Default: true

    • Details:

      Enable the last updated timestamp or not.

      You can override this global option via lastUpdated frontmatter in your pages. Notice that if you have already set this option to false, this feature will be disabled totally and could not be enabled in locales nor page frontmatter.

    contributors

    • Type: boolean

    • Default: true

    • Details:

      Enable the contributors list or not.

      You can override this global option via contributors frontmatter in your pages. Notice that if you have already set this option to false, this feature will be disabled totally and could not be enabled in locales nor page frontmatter.

    + + + diff --git a/themes/default/extending.html b/themes/default/extending.html new file mode 100644 index 0000000000..05a5a48f49 --- /dev/null +++ b/themes/default/extending.html @@ -0,0 +1,102 @@ + + + + + + + + + Extending | VuePress Ecosystem + + + + + +

    Extending

    VuePress default theme is widely used by users, so it is designed to be extendable, allowing users to make their own customization with ease.

    Layout Slots

    Default theme's Layout provides some slots:

    • navbar
    • navbar-before
    • navbar-after
    • sidebar
    • sidebar-top
    • sidebar-bottom
    • page
    • page-top
    • page-bottom
    • page-content-top
    • page-content-bottom

    With the help of them, you can add or replace content easily. Here comes an example to introduce how to extend default theme with layout slots.

    Firstly, create a client config file .vuepress/client.ts:

    import { defineClientConfig } from 'vuepress/client'
    +import Layout from './layouts/Layout.vue'
    +
    +export default defineClientConfig({
    +  layouts: {
    +    Layout,
    +  },
    +})
    +

    Next, create the .vuepress/layouts/Layout.vue, and make use of the slots that provided by the Layout of default theme:

    <script setup>
    +import ParentLayout from '@vuepress/theme-default/layouts/Layout.vue'
    +</script>
    +
    +<template>
    +  <ParentLayout>
    +    <template #page-bottom>
    +      <div class="my-footer">This is my custom page footer</div>
    +    </template>
    +  </ParentLayout>
    +</template>
    +
    +<style lang="css">
    +.my-footer {
    +  text-align: center;
    +}
    +</style>
    +

    Then the default Layout layout has been overridden by your own local layout, which will add a custom footer to every normal pages in default theme (excluding homepage):

    extending-a-theme

    Components Replacement

    The layout slots are useful, but sometimes you might find it's not flexible enough. Default theme also provides the ability to replace a single component.

    Default theme has registered aliasopen in new window for every non-global componentsopen in new window with a @theme prefix. For example, the alias of HomeFooter.vue is @theme/HomeFooter.vue.

    Then, if you want to replace the HomeFooter.vue component, just override the alias in your config file .vuepress/config.ts:

    import { defaultTheme } from '@vuepress/theme-default'
    +import { getDirname, path } from 'vuepress/utils'
    +import { defineUserConfig } from 'vuepress'
    +
    +const __dirname = getDirname(import.meta.url)
    +
    +export default defineUserConfig({
    +  theme: defaultTheme(),
    +  alias: {
    +    '@theme/HomeFooter.vue': path.resolve(
    +      __dirname,
    +      './components/MyHomeFooter.vue',
    +    ),
    +  },
    +})
    +

    Developing a Child Theme

    Instead of extending the default theme directly in .vuepress/config.ts and .vuepress/client.ts, you can also develop your own theme extending the default theme:

    import { defaultTheme, type DefaultThemeOptions } from '@vuepress/theme-default'
    +import type { Theme } from 'vuepress/core'
    +import { getDirname, path } from 'vuepress/utils'
    +
    +const __dirname = getDirname(import.meta.url)
    +
    +export const childTheme = (options: DefaultThemeOptions): Theme => {
    +  return {
    +    name: 'vuepress-theme-child',
    +    extends: defaultTheme(options),
    +
    +    // override layouts in child theme's client config file
    +    // notice that you would build ts to js before publishing to npm,
    +    // so this should be the path to the js file
    +    clientConfigFile: path.resolve(__dirname, './client.js'),
    +
    +    // override component alias
    +    alias: {
    +      '@theme/HomeFooter.vue': path.resolve(
    +        __dirname,
    +        './components/MyHomeFooter.vue',
    +      ),
    +    },
    +  }
    +}
    +
    + + + diff --git a/themes/default/frontmatter.html b/themes/default/frontmatter.html new file mode 100644 index 0000000000..fede75ef5d --- /dev/null +++ b/themes/default/frontmatter.html @@ -0,0 +1,96 @@ + + + + + + + + + Frontmatter | VuePress Ecosystem + + + + + +

    Frontmatter

    @vuepress/theme-default

    All Pages

    Frontmatter in this section will take effect in all types of pages.

    externalLinkIcon

    • Type: boolean

    • Details:

      Show navbar on this page or not.

      If you disable navbar in theme config, this frontmatter will not take effect.

    • Also see:

    pageClass

    • Type: string

    • Details:

      Add extra class name to this page.

    • Example:

    ---
    +pageClass: custom-page-class
    +---
    +

    Then you can customize styles of this page in .vuepress/styles/index.scss file:

    .theme-container.custom-page-class {
    +  /* page styles */
    +}
    +

    Home Page

    Frontmatter in this section will only take effect in home pages.

    home

    • Type: boolean

    • Details:

      Specify whether the page is homepage or a normal page.

      If you don't set this frontmatter or set it to false, the page would be a normal page.

    • Example:

    ---
    +home: true
    +---
    +

    heroImage

    • Type: string

    • Details:

      Specify the url of the hero image.

    • Example:

    ---
    +# public file path
    +heroImage: /images/hero.png
    +# url
    +heroImage: https://vuejs.org/images/logo.png
    +---
    +

    heroImageDark

    heroAlt

    • Type: string

    • Details:

      Specify the alt attribute of the hero image.

      This will fallback to the heroText.

    heroHeight

    • Type: number

    • Default: 280

    • Details:

      Specify the height attribute of the hero <img> tag.

      You may need to reduce this value if the height of your hero image is less than the default value.

      Notice that the height is also constrained by CSS. This attribute is to reduce Cumulative Layout Shift (CLS)open in new window that caused by the loading of the hero image.

    heroText

    • Type: string | null

    • Details:

      Specify the the hero text.

      This will fallback to the site titleopen in new window.

      Set to null to disable hero text.

    tagline

    actions

    • Type:
    Array<{
    +  text: string
    +  link: string
    +  type?: 'primary' | 'secondary'
    +}>
    +
    • Details:

      Configuration of the action buttons.

    • Example:

    ---
    +actions:
    +  - text: Get Started
    +    link: /guide/getting-started.html
    +    type: primary
    +  - text: Introduction
    +    link: /guide/introduction.html
    +    type: secondary
    +---
    +

    features

    • Type:
    Array<{
    +  title: string
    +  details: string
    +}>
    +
    • Details:

      Configuration of the features list.

    • Example:

    ---
    +features:
    +  - title: Simplicity First
    +    details: Minimal setup with markdown-centered project structure helps you focus on writing.
    +  - title: Vue-Powered
    +    details: Enjoy the dev experience of Vue, use Vue components in markdown, and develop custom themes with Vue.
    +  - title: Performant
    +    details: VuePress generates pre-rendered static HTML for each page, and runs as an SPA once a page is loaded.
    +---
    +
    • Type: string

    • Details:

      Specify the content of the footer.

    footerHtml

    • Type: boolean

    • Details:

      Allow HTML in footer or not.

      If you set it to true, the footer will be treated as HTML code.

    Normal Page

    Frontmatter in this section will only take effect in normal pages.

    editLinkPattern

    lastUpdated

    contributors

    sidebarDepth

    prev

    • Type: NavLink | string

    • Details:

      Specify the link of the previous page.

      If you don't set this frontmatter, the link will be inferred from the sidebar config.

      To configure the prev link manually, you can set this frontmatter to a NavLink object or a string:

      • A NavLink object should have a text field and a link field.
      • A string should be the path to the target page file. It will be converted to a NavLink object, whose text is the page title, and link is the page route path.
    • Example:

    ---
    +# NavLink
    +prev:
    +  text: Get Started
    +  link: /guide/getting-started.html
    +
    +# NavLink - external url
    +prev:
    +  text: GitHub
    +  link: https://github.com
    +
    +# string - page file path
    +prev: /guide/getting-started.md
    +
    +# string - page file relative path
    +prev: ../../guide/getting-started.md
    +---
    +

    next

    • Type: NavLink | string

    • Details:

      Specify the link of the next page.

      If you don't set this frontmatter, the link will be inferred from the sidebar config.

      The type is the same as prev frontmatter.

    + + + diff --git a/themes/default/index.html b/themes/default/index.html new file mode 100644 index 0000000000..bfcb4f93b8 --- /dev/null +++ b/themes/default/index.html @@ -0,0 +1,45 @@ + + + + + + + + + theme-default | VuePress Ecosystem + + + + + + + + + diff --git a/themes/default/locale.html b/themes/default/locale.html new file mode 100644 index 0000000000..4c08c15acf --- /dev/null +++ b/themes/default/locale.html @@ -0,0 +1,57 @@ + + + + + + + + + Locale Config | VuePress Ecosystem + + + + + +

    Locale Config

    These options configure locale-related texts.

    If your site is served in a different language besides English, you should set these options per locale to provide translations.

    repoLabel

    • Type: string

    • Details:

      Specify the repository label of your project.

      This will be used as the text of the repository link, which will be displayed as the last item of the navbar.

      If you don't set this option explicitly, it will be automatically inferred from the repo option.

    selectLanguageText

    • Type: string

    • Details:

      Specify the text of the select language menu.

      The select language menu will appear next to the repository button in the navbar when you set multiple locales in your site config.

    selectLanguageAriaLabel

    • Type: string

    • Details:

      Specify the aria-label attribute of the select language menu.

      This is mainly for a11y purpose.

    selectLanguageName

    • Type: string

    • Details:

      Specify the name of the language of a locale.

      This option will only take effect inside the locales of your theme config. It will be used as the language name of the locale, which will be displayed in the select language menu.

    • Example:

    export default {
    +  locales: {
    +    '/': {
    +      lang: 'en-US',
    +    },
    +    '/zh/': {
    +      lang: 'zh-CN',
    +    },
    +  },
    +  theme: defaultTheme({
    +    locales: {
    +      '/': {
    +        selectLanguageName: 'English',
    +      },
    +      '/zh/': {
    +        selectLanguageName: '简体中文',
    +      },
    +    },
    +  }),
    +}
    +
    • Type: null | string

    • Details:

      aria-label value for main navigation in navbar.

    • Type: null | string

    • Details:

      aria-label value for next/previous page navigation.

    editLinkText

    • Type: string

    • Default: 'Edit this page'

    • Details:

      Specify the text of the edit this page link.

    lastUpdatedText

    • Type: string

    • Default: 'Last Updated'

    • Details:

      Specify the text of the last updated timestamp label.

    contributorsText

    • Type: string

    • Default: 'Contributors'

    • Details:

      Specify the text of the contributors list label.

    tip

    • Type: string

    • Default: 'TIP'

    • Details:

      Specify the default title of the tip custom containers.

    warning

    • Type: string

    • Default: 'WARNING'

    • Details:

      Specify the default title of the warning custom containers.

    danger

    • Type: string

    • Default: 'DANGER'

    • Details:

      Specify the default title of the danger custom containers.

    notFound

    • Type: string[]

    • Default: ['Not Found']

    • Details:

      Specify the messages of the 404 page.

      The message will be randomly picked from the array when users enter the 404 page.

    backToHome

    • Type: string

    • Default: 'Back to home'

    • Details:

      Specify the text of the back to home link in the 404 page.

    openInNewWindow

    toggleColorMode

    toggleSidebar

    • Type: string

    • Default: 'toggle sidebar'

    • Details:

      Title text for sidebar toggle button.

      This is mainly for a11y purpose.

    + + + diff --git a/themes/default/markdown.html b/themes/default/markdown.html new file mode 100644 index 0000000000..e07244ff20 --- /dev/null +++ b/themes/default/markdown.html @@ -0,0 +1,86 @@ + + + + + + + + + Markdown | VuePress Ecosystem + + + + + +

    Markdown

    @vuepress/theme-default

    Custom Containers

    • Usage:

      ::: <type> [title]
      +[content]
      +:::
      +

      The type is required, and the title and content are optional.

      Supported type :

    • Example 1 (default title):

    Input

    ::: tip
    +This is a tip
    +:::
    +
    +::: warning
    +This is a warning
    +:::
    +
    +::: danger
    +This is a dangerous warning
    +:::
    +
    +::: details
    +This is a details block
    +:::
    +

    Output

    TIP

    This is a tip

    WARNING

    This is a warning

    DANGER

    This is a dangerous warning

    This is a details block

    • Example 2 (custom title):

    Input

    ::: danger STOP
    +Danger zone, do not proceed
    +:::
    +
    +::: details Click me to view the code
    +
    +```ts
    +console.log('Hello, VuePress!')
    +```
    +
    +:::
    +

    Output

    STOP

    Danger zone, do not proceed

    Click me to view the code
    console.log('Hello, VuePress!')
    +
    • Example 3 (code group alias):

    Input

    :::: code-group
    +::: code-group-item FOO
    +
    +```ts
    +const foo = 'foo'
    +```
    +
    +:::
    +
    +::: code-group-item BAR
    +
    +```ts
    +const bar = 'bar'
    +```
    +
    +:::
    +::::
    +

    Output

    const foo = 'foo'
    +
    const bar = 'bar'
    +
    + + + diff --git a/themes/default/plugin.html b/themes/default/plugin.html new file mode 100644 index 0000000000..99cc26e8dd --- /dev/null +++ b/themes/default/plugin.html @@ -0,0 +1,46 @@ + + + + + + + + + Plugins Config | VuePress Ecosystem + + + + + +

    Plugins Config

    You can configure the plugins that used by default theme with themePlugins.

    Default theme is using some plugins by default. You can disable a plugin if you really do not want to use it. Make sure you understand what the plugin is for before disabling it.

    import { defaultTheme } from '@vuepress/theme-default'
    +
    +export default {
    +  theme: defaultTheme({
    +    themePlugins: {
    +      // customize theme plugins here
    +    },
    +  }),
    +}
    +

    themePlugins.backToTop

    • Type: BackToTopPluginOptions | boolean

    • Default: true

    • Details:

      Enable @vuepress/plugin-back-to-top or not.

      Object value is supported as plugin options.

    themePlugins.container

    themePlugins.externalLinkIcon

    themePlugins.copyCode

    • Type: CopyCodePluginOptions | boolean

    • Default: true

    • Details:

      Enable @vuepress/plugin-copy-code or not.

      Object value is supported as plugin options.

    themePlugins.git

    themePlugins.mediumZoom

    themePlugins.nprogress

    themePlugins.seo

    • Type: SeoPluginOptions | boolean

    • Default: true

    • Details:

      Enable @vuepress/plugin-seo or not.

      Object value is supported as plugin options.

    themePlugins.sitemap

    • Type: SitemapPluginOptions | boolean

    • Default: true

    • Details:

      Enable @vuepress/plugin-sitemap or not.

      Object value is supported as plugin options.

    + + + diff --git a/themes/default/styles.html b/themes/default/styles.html new file mode 100644 index 0000000000..4ef1550c64 --- /dev/null +++ b/themes/default/styles.html @@ -0,0 +1,299 @@ + + + + + + + + + Styles | VuePress Ecosystem + + + + + +

    Styles

    @vuepress/theme-default

    The default theme uses SASSopen in new window as the CSS pre-processor.

    Users can customize style variables via a palette file, and add extra styles via a style file.

    Palette File

    The path of the palette file is .vuepress/styles/palette.scss.

    You can make use of it to override predefined SASS variables of the default theme.

    Click to expand SASS variables
    // responsive breakpoints
    +$MQNarrow: 959px !default;
    +$MQMobile: 719px !default;
    +$MQMobileNarrow: 419px !default;
    +

    Style File

    The path of the style file is .vuepress/styles/index.scss.

    You can add extra styles here, or override the default styles:

    :root {
    +  scroll-behavior: smooth;
    +}
    +

    You can also make use of it to override predefined CSS variables of the default theme.

    Click to expand CSS variables
    :root {
    +  // brand colors
    +  --c-brand: #3eaf7c;
    +  --c-brand-light: #4abf8a;
    +
    +  // background colors
    +  --c-bg: #ffffff;
    +  --c-bg-light: #f3f4f5;
    +  --c-bg-lighter: #eeeeee;
    +  --c-bg-dark: #ebebec;
    +  --c-bg-darker: #e6e6e6;
    +  --c-bg-navbar: var(--c-bg);
    +  --c-bg-sidebar: var(--c-bg);
    +  --c-bg-arrow: #cccccc;
    +
    +  // text colors
    +  --c-text: #2c3e50;
    +  --c-text-accent: var(--c-brand);
    +  --c-text-light: #3a5169;
    +  --c-text-lighter: #4e6e8e;
    +  --c-text-lightest: #6a8bad;
    +  --c-text-quote: #999999;
    +
    +  // border colors
    +  --c-border: #eaecef;
    +  --c-border-dark: #dfe2e5;
    +
    +  // custom container colors
    +  --c-tip: #42b983;
    +  --c-tip-bg: var(--c-bg-light);
    +  --c-tip-title: var(--c-text);
    +  --c-tip-text: var(--c-text);
    +  --c-tip-text-accent: var(--c-text-accent);
    +  --c-warning: #ffc310;
    +  --c-warning-bg: #fffae3;
    +  --c-warning-bg-light: #fff3ba;
    +  --c-warning-bg-lighter: #fff0b0;
    +  --c-warning-border-dark: #f7dc91;
    +  --c-warning-details-bg: #fff5ca;
    +  --c-warning-title: #f1b300;
    +  --c-warning-text: #746000;
    +  --c-warning-text-accent: #edb100;
    +  --c-warning-text-light: #c1971c;
    +  --c-warning-text-quote: #ccab49;
    +  --c-danger: #f11e37;
    +  --c-danger-bg: #ffe0e0;
    +  --c-danger-bg-light: #ffcfde;
    +  --c-danger-bg-lighter: #ffc9c9;
    +  --c-danger-border-dark: #f1abab;
    +  --c-danger-details-bg: #ffd4d4;
    +  --c-danger-title: #ed1e2c;
    +  --c-danger-text: #660000;
    +  --c-danger-text-accent: #bd1a1a;
    +  --c-danger-text-light: #b5474d;
    +  --c-danger-text-quote: #c15b5b;
    +  --c-details-bg: #eeeeee;
    +
    +  // badge component colors
    +  --c-badge-tip: var(--c-tip);
    +  --c-badge-warning: #ecc808;
    +  --c-badge-warning-text: var(--c-bg);
    +  --c-badge-danger: #dc2626;
    +  --c-badge-danger-text: var(--c-bg);
    +
    +  // code group colors
    +  --c-code-group-tab-title: rgba(255, 255, 255, 0.9);
    +  --c-code-group-tab-bg: var(--code-bg-color);
    +  --c-code-group-tab-outline: var(var(--c-code-group-tab-title));
    +  --c-code-group-tab-active-border: var(--c-brand);
    +
    +  // transition vars
    +  --t-color: 0.3s ease;
    +  --t-transform: 0.3s ease;
    +
    +  // code blocks vars
    +  --code-bg-color: #282c34;
    +  --code-hl-bg-color: rgba(0, 0, 0, 0.66);
    +  --code-ln-color: #9e9e9e;
    +  --code-ln-wrapper-width: 3.5rem;
    +
    +  // font vars
    +  --font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen,
    +    Ubuntu, Cantarell, 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif;
    +  --font-family-code: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace;
    +
    +  // layout vars
    +  --navbar-height: 3.6rem;
    +  --navbar-padding-v: 0.7rem;
    +  --navbar-padding-h: 1.5rem;
    +  --sidebar-width: 20rem;
    +  --sidebar-width-mobile: calc(var(--sidebar-width) * 0.82);
    +  --content-width: 740px;
    +  --homepage-width: 960px;
    +}
    +
    +// plugin-back-to-top
    +.vp-back-to-top-button {
    +  --back-to-top-color: var(--c-brand);
    +  --back-to-top-color-hover: var(--c-brand-light);
    +  --back-to-top-bg-color: var(--c-bg);
    +}
    +
    +// plugin-catalog
    +.vp-catalog-wrapper {
    +  --catalog-bg-color: var(--c-bg);
    +  --catalog-bg-secondary-color: var(--c-bg-dark);
    +  --catalog-border-color: var(--c-border);
    +  --catalog-active-color: var(--c-brand);
    +  --catalog-hover-color: var(--c-brand-light);
    +}
    +
    +// plugin-docsearch
    +.DocSearch {
    +  --docsearch-primary-color: var(--c-brand);
    +  --docsearch-text-color: var(--c-text);
    +  --docsearch-highlight-color: var(--c-brand);
    +  --docsearch-muted-color: var(--c-text-quote);
    +  --docsearch-container-background: rgba(9, 10, 17, 0.8);
    +  --docsearch-modal-background: var(--c-bg-light);
    +  --docsearch-searchbox-background: var(--c-bg-lighter);
    +  --docsearch-searchbox-focus-background: var(--c-bg);
    +  --docsearch-searchbox-shadow: inset 0 0 0 2px var(--c-brand);
    +  --docsearch-hit-color: var(--c-text-light);
    +  --docsearch-hit-active-color: var(--c-bg);
    +  --docsearch-hit-background: var(--c-bg);
    +  --docsearch-hit-shadow: 0 1px 3px 0 var(--c-border-dark);
    +  --docsearch-footer-background: var(--c-bg);
    +}
    +
    +// plugin-external-link-icon
    +.external-link-icon {
    +  --external-link-icon-color: var(--c-text-quote);
    +}
    +
    +// plugin-medium-zoom
    +.medium-zoom-overlay {
    +  --medium-zoom-bg-color: var(--c-bg);
    +}
    +
    +// plugin-nprogress
    +#nprogress {
    +  --nprogress-color: var(--c-brand);
    +}
    +
    +// plugin-photo-swipe
    +body {
    +  --photo-swipe-bullet: var(--c-bg);
    +  --photo-swipe-bullet-active: var(--c-brand);
    +}
    +
    +// plugin-pwa-popup
    +html {
    +  --pwa-text-color: var(--c-text);
    +  --pwa-bg-color: var(--c-bg);
    +  --pwa-border-color: var(--c-brand);
    +  --pwa-btn-text-color: var(--c-bg);
    +  --pwa-btn-bg-color: var(--c-brand);
    +  --pwa-btn-hover-bg-color: var(--c-brand-light);
    +}
    +
    +html.dark {
    +  --pwa-shadow-color: rgb(0 0 0 / 30%);
    +  --pwa-content-color: #ccc;
    +  --pwa-content-light-color: #999;
    +}
    +
    +// plugin-redirect
    +.language-modal-mask {
    +  --redirect-bg-color: var(--c-bg);
    +  --redirect-bg-color-light: var(--c-bg-light);
    +  --redirect-bg-color-lighter: var(--c-bg-lighter);
    +  --redirect-text-color: var(--c-text);
    +  --redirect-primary-bg-color: var(--c-brand);
    +  --redirect-primary-hover-bg-color: var(--c-brand-light);
    +  --redirect-primary-text-color: var(--c-bg);
    +}
    +
    +// plugin-search
    +.search-box {
    +  --search-bg-color: var(--c-bg);
    +  --search-accent-color: var(--c-brand);
    +  --search-text-color: var(--c-text);
    +  --search-border-color: var(--c-border);
    +
    +  --search-item-text-color: var(--c-text-lighter);
    +  --search-item-focus-bg-color: var(--c-bg-light);
    +}
    +
    Click to expand dark mode CSS variables
    html.dark {
    +  // brand colors
    +  --c-brand: #3aa675;
    +  --c-brand-light: #349469;
    +
    +  // background colors
    +  --c-bg: #22272e;
    +  --c-bg-light: #2b313a;
    +  --c-bg-lighter: #262c34;
    +  --c-bg-dark: #343b44;
    +  --c-bg-darker: #37404c;
    +
    +  // text colors
    +  --c-text: #adbac7;
    +  --c-text-light: #96a7b7;
    +  --c-text-lighter: #8b9eb0;
    +  --c-text-lightest: #8094a8;
    +
    +  // border colors
    +  --c-border: #3e4c5a;
    +  --c-border-dark: #34404c;
    +
    +  // custom container colors
    +  --c-tip: #318a62;
    +  --c-warning: #e0ad15;
    +  --c-warning-bg: #2d2f2d;
    +  --c-warning-bg-light: #423e2a;
    +  --c-warning-bg-lighter: #44442f;
    +  --c-warning-border-dark: #957c35;
    +  --c-warning-details-bg: #39392d;
    +  --c-warning-title: #fdca31;
    +  --c-warning-text: #d8d96d;
    +  --c-warning-text-accent: #ffbf00;
    +  --c-warning-text-light: #ddb84b;
    +  --c-warning-text-quote: #ccab49;
    +  --c-danger: #fc1e38;
    +  --c-danger-bg: #39232c;
    +  --c-danger-bg-light: #4b2b35;
    +  --c-danger-bg-lighter: #553040;
    +  --c-danger-border-dark: #a25151;
    +  --c-danger-details-bg: #482936;
    +  --c-danger-title: #fc2d3b;
    +  --c-danger-text: #ea9ca0;
    +  --c-danger-text-accent: #fd3636;
    +  --c-danger-text-light: #d9777c;
    +  --c-danger-text-quote: #d56b6b;
    +  --c-details-bg: #323843;
    +
    +  // badge component colors
    +  --c-badge-warning: var(--c-warning);
    +  --c-badge-warning-text: #3c2e05;
    +  --c-badge-danger: var(--c-danger);
    +  --c-badge-danger-text: #401416;
    +
    +  // code blocks vars
    +  --code-hl-bg-color: #363b46;
    +}
    +
    +// plugin-docsearch
    +html.dark .DocSearch {
    +  --docsearch-logo-color: var(--c-text);
    +  --docsearch-modal-shadow: inset 1px 1px 0 0 #2c2e40, 0 3px 8px 0 #000309;
    +  --docsearch-key-shadow: inset 0 -2px 0 0 #282d55, inset 0 0 1px 1px #51577d,
    +    0 2px 2px 0 rgba(3, 4, 9, 0.3);
    +  --docsearch-key-gradient: linear-gradient(-225deg, #444950, #1c1e21);
    +  --docsearch-footer-shadow: inset 0 1px 0 0 rgba(73, 76, 106, 0.5),
    +    0 -4px 8px 0 rgba(0, 0, 0, 0.2);
    +}
    +
    + + + diff --git a/themes/index.html b/themes/index.html new file mode 100644 index 0000000000..95202e7b11 --- /dev/null +++ b/themes/index.html @@ -0,0 +1,37 @@ + + + + + + + + + Themes | VuePress Ecosystem + + + + + + + + + diff --git a/tools/helper/client.html b/tools/helper/client.html new file mode 100644 index 0000000000..01e1f9313d --- /dev/null +++ b/tools/helper/client.html @@ -0,0 +1,58 @@ + + + + + + + + + Client Related | VuePress Ecosystem + + + + + +

    Client Related

    Composables APIs

    hasGlobalComponent

    Check whether a component is registered globally.

    TIP

    1. Local import of the component does not affect the result.
    2. When calling outside setup scope, you need to pass the app instance as the second parameter.
    export const hasGlobalComponent: (name: string, app?: App) => boolean
    +
    Example
    // if you globally register `<my-component>`
    +hasGlobalComponent('MyComponent') // true
    +hasGlobalComponent('my-component') // true
    +
    +hasGlobalComponent('MyComponent2') // false
    +

    useLocaleConfig

    Get current locale config from locales settings.

    export const useLocaleConfig: <T extends LocaleData>(
    +  localesConfig: RequiredLocaleConfig<T>,
    +) => ComputedRef<T>
    +
    Example
    const localesCOnfig = {
    +  '/': 'Title',
    +  '/zh/': '标题',
    +}
    +
    +const locale = useLocaleConfig(localesConfig)
    +
    +// under `/page`
    +locale.value // 'Title'
    +
    +// under `/zh/page`
    +locale.value // '标题'
    +
    + + + diff --git a/tools/helper/index.html b/tools/helper/index.html new file mode 100644 index 0000000000..596b3d6fef --- /dev/null +++ b/tools/helper/index.html @@ -0,0 +1,37 @@ + + + + + + + + + @vuepress/helper | VuePress Ecosystem + + + + + +

    @vuepress/helper

    @vuepress/helper

    This package is a helper utility for VuePress developers.

    + + + diff --git a/tools/helper/node/bundler.html b/tools/helper/node/bundler.html new file mode 100644 index 0000000000..b3c30610ff --- /dev/null +++ b/tools/helper/node/bundler.html @@ -0,0 +1,212 @@ + + + + + + + + + Bundler Related | VuePress Ecosystem + + + + + +

    Bundler Related

    Bundler function is for appending or modifying bundler options in theme and plugins.

    All functions should be called in extendsBundlerOptions lifecycle hook.

    TIP

    We are omitting that in examples. The actual code should be like this:

    // import functions you need
    +import { addCustomElement } from '@vuepress/helper'
    +
    +export const yourPlugin = {
    +  // ...
    +  extendsBundlerOptions: (bundlerOptions, app) => {
    +    // add them here
    +    addCustomElement(bundlerOptions, app, 'my-custom-element')
    +  },
    +}
    +

    Common methods

    getBundlerName

    Get current bundler name.

    export const getBundlerName: (app: App) => string
    +
    Example
    // @vuepress/bundler-vite
    +getBundleName(app) === 'vite' // true
    +// @vuepress/bundler-webpack
    +getBundleName(app) === 'webpack' // true
    +

    addCustomElement

    Add a custom element declaration to the current bundler.

    /**
    + * Add tags as customElement
    + *
    + * @param bundlerOptions VuePress Bundler config
    + * @param app VuePress Node App
    + * @param customElements tags recognized as custom element
    + */
    +export const addCustomElement = (
    +  bundlerOptions: unknown,
    +  app: App,
    +  customElement: string[] | string | RegExp
    +) => void;
    +
    Example
    import { addCustomElement } from '@vuepress/helper'
    +
    +addCustomElement(bundlerConfig, app, 'my-custom-element')
    +addCustomElement(bundlerOptions, app, [
    +  'custom-element1',
    +  'custom-element2',
    +  // all tags start with `math-`
    +  /^math-/,
    +])
    +

    customizeDevServer

    Provides contents for specific path in dev server.

    export interface DevServerOptions {
    +  /**
    +   * Path to be responded
    +   */
    +  path: string;
    +  /**
    +   * Respond function
    +   */
    +  response: (request?: IncomingMessage) => Promise<string | Buffer>;
    +
    +  /**
    +   * error msg
    +   */
    +  errMsg?: string;
    +}
    +
    +/**
    + * Handle specific path when running VuePress Dev Server
    + *
    + * @param bundlerOptions VuePress Bundler config
    + * @param app VuePress Node App
    + * @param path Path to be responded
    + * @param response respond function
    + * @param errMsg error msg
    + */
    +export const customizeDevServer: (
    +  bundlerOptions: unknown,
    +  app: App,
    +  {
    +    errMsg:"The server encountered an error",
    +    response: responseHandler,
    +    path,
    +  }: CustomServerOptions
    +) => void;
    +
    Example
    import { useCustomDevServer } from '@vuepress/helper'
    +
    +// handle `/api/` path
    +useCustomDevServer(bundlerOptions, app, {
    +  path: '/api/',
    +  response: async () => getData(),
    +  errMsg: 'Unexpected api error',
    +})
    +
    • addViteOptimizeDepsInclude

      Add modules to Vite optimizeDeps.include list

      TIP

      If a package meets one of the following conditions, you should consider adding it here.

      • It's in CJS format
      • It's dependencies include CJS package
      • It's dynamically imported via import()
    • addViteOptimizeDepsExclude

      Add modules to Vite optimizeDeps.exclude list

      If a package and its dependencies are all pure ESM packages, you should consider adding it here.

    • addViteSsrExternal

      Add modules to Vite ssr.external list

      If a package is a pure ESM package and does not use aliases or define variables, you should consider adding it here.

    • addViteSsrNoExternal

      Add modules to Vite ssr.noExternal list

      If an alias or define is used within a package, you must add it here.

      /**
      + * Add modules to Vite `optimizeDeps.include` list
      + */
      +export const addViteOptimizeDepsInclude: (
      +  bundlerOptions: unknown,
      +  app: App,
      +  module: string | string[],
      +) => void
      +
      +/**
      + * Add modules to Vite `optimizeDeps.exclude` list
      + */
      +export const addViteOptimizeDepsExclude: (
      +  bundlerOptions: unknown,
      +  app: App,
      +  module: string | string[],
      +) => void
      +
      +/**
      + * Add modules to Vite `ssr.external` list
      + */
      +export const addViteSsrExternal: (
      +  bundlerOptions: unknown,
      +  app: App,
      +  module: string | string[],
      +) => void
      +
      +/**
      + * Add modules to Vite `ssr.noExternal` list
      + */
      +export const addViteSsrNoExternal: (
      +  bundlerOptions: unknown,
      +  app: App,
      +  module: string | string[],
      +) => void
      +
      Examples
      import {
      +  addViteOptimizeDepsInclude,
      +  addViteOptimizeDepsExclude,
      +  addViteSsrExternal,
      +  addViteSsrNoExternal,
      +} from '@vuepress/helper'
      +
      +addViteOptimizeDepsInclude(bundlerOptions, app, ['vue', 'vue-router'])
      +addViteOptimizeDepsExclude(bundlerOptions, app, 'packageA')
      +addViteSsrNoExternal(bundlerOptions, app, ['vue', 'vue-router'])
      +addViteSsrExternal(bundlerOptions, app, 'packageA')
      +
    • addViteConfig

      A function for you to add vite config

      export const addViteConfig: (
      +  bundlerOptions: unknown,
      +  app: App,
      +  config: Record<string, unknown>,
      +) => void
      +
      Example
      import { addViteConfig } from '@vuepress/helper'
      +
      +addViteConfig(bundlerOptions, app, {
      +  build: {
      +    charset: 'utf8',
      +  },
      +})
      +
    • mergeViteConfig

      A function for you to merge vite config.

      WARNING

      Your users may choose to use other bundler so it's pretty bad to declare vite as deps!

      export const mergeViteConfig: (
      +  defaults: Record<string, any>,
      +  overrides: Record<string, any>,
      +) => Record<string, any>
      +
      Example
      import { mergeViteConfig } from '@vuepress/helper'
      +
      +config.viteOptions = mergeViteConfig(config.viteOptions, {
      +  build: {
      +    charset: 'utf8',
      +  },
      +})
      +
    • chainWebpack

      Chain webpack config.

      export const chainWebpack: (
      +  bundlerOptions: unknown,
      +  app: App,
      +  chainWebpack: (
      +    config: WebpackChainConfig,
      +    isServer: boolean,
      +    isBuild: boolean,
      +  ) => void,
      +) => void
      +
      Example
      import { chainWebpack } from '@vuepress/helper'
      +
      +chainWebpack(bundlerOptions, app, (config, isServer, isBuild) => {
      +  // do some customize here
      +})
      +
    • configWebpack

      Config Webpack

      export const configWebpack: (
      +  bundlerOptions: unknown,
      +  app: App,
      +  configureWebpack: (
      +    config: WebpackConfiguration,
      +    isServer: boolean,
      +    isBuild: boolean,
      +  ) => void,
      +) => void
      +
      Example
      import { configWebpack } from '@vuepress/helper'
      +
      +configWebpack(bundlerOptions, app, (config, isServer, isBuild) => {
      +  // do some customize here
      +})
      +
    + + + diff --git a/tools/helper/node/index.html b/tools/helper/node/index.html new file mode 100644 index 0000000000..5198d84010 --- /dev/null +++ b/tools/helper/node/index.html @@ -0,0 +1,37 @@ + + + + + + + + + Node | VuePress Ecosystem + + + + + + + + + diff --git a/tools/helper/node/page.html b/tools/helper/node/page.html new file mode 100644 index 0000000000..68de32ac7a --- /dev/null +++ b/tools/helper/node/page.html @@ -0,0 +1,113 @@ + + + + + + + + + Page Related | VuePress Ecosystem + + + + + +

    Page Related

    These functions generate common information for your pages.

    getPageExcerpt

    Get the excerpt of the page.

    export interface PageExcerptOptions {
    +  /**
    +   * Excerpt separator
    +   *
    +   * @default "<!-- more -->"
    +   */
    +  separator?: string
    +
    +  /**
    +   * Length of excerpt
    +   *
    +   * @description Excerpt length will be the minimal possible length reaching this value
    +   *
    +   * @default 300
    +   */
    +  length?: number
    +
    +  /**
    +   * Tags which is considered as custom elements
    +   *
    +   * @description This is used to determine whether a tag is a custom element since all unknown tags are removed in excerpt.
    +   */
    +  isCustomElement?: (tagName: string) => boolean
    +
    +  /**
    +   * Whether keep page title (first h1) in excerpt
    +   *
    +   * @default false
    +   */
    +  keepPageTitle?: boolean
    +
    +  /**
    +   * Whether preserve tags like line numbers and highlight lines for code blocks
    +   *
    +   * @default false
    +   */
    +  keepFenceDom?: boolean
    +}
    +
    +export const getPageExcerpt: (
    +  app: App,
    +  page: Page,
    +  options?: PageExcerptOptions,
    +) => string
    +

    getPageText

    Get plain text of the page.

    export interface PageTextOptions {
    +  /**
    +   * Whether convert text to single line content
    +   *
    +   * @default false
    +   */
    +  singleLine?: boolean
    +
    +  /**
    +   * Length of text
    +   *
    +   * @description Text length will be the minimal possible length reaching this value
    +   *
    +   * @default 300
    +   */
    +  length?: number
    +
    +  /**
    +   * Tags to be removed
    +   *
    +   * @description Table and code blocks are removed by default.
    +   *
    +   * @default ['table', 'pre']
    +   */
    +  removedTags?: string[]
    +}
    +
    +export const getPageText: (
    +  app: App,
    +  page: Page,
    +  options?: PageTextOptions,
    +) => string
    +
    + + + diff --git a/tools/helper/shared.html b/tools/helper/shared.html new file mode 100644 index 0000000000..0e48f12b72 --- /dev/null +++ b/tools/helper/shared.html @@ -0,0 +1,146 @@ + + + + + + + + + Shared Methods | VuePress Ecosystem + + + + + +

    Shared Methods

    The following functions are available on both Node.js and Client.

    Encode/decode and zip/unzip data.

    This is useful in markdown plugins when you want to encode string content and pass it to the component through props.

    You may simply achieve this with encodeURIComponent and decodeURIComponent, but it can be very large if the content contains lots of special characters.

    So we provide encodeData and decodeData to zip and encode content.

    export const encodeData: (
    +  data: string,
    +  level: DeflateOptions['level'] = 6,
    +) => string
    +
    +export const decodeData: (compressed: string) => string
    +
    const content = `
    +{
    +  "type": "bar",
    +  "data": {
    +    "labels": ["Red", "Blue", "Yellow", "Green", "Purple", "Orange"],
    +    "datasets": [
    +      {
    +        "label": "# of Votes",
    +        "data": [12, 19, 3, 5, 2, 3],
    +        "backgroundColor": [
    +          "rgba(255, 99, 132, 0.2)",
    +          "rgba(54, 162, 235, 0.2)",
    +          "rgba(255, 206, 86, 0.2)",
    +          "rgba(75, 192, 192, 0.2)",
    +          "rgba(153, 102, 255, 0.2)",
    +          "rgba(255, 159, 64, 0.2)"
    +        ],
    +        "borderColor": [
    +          "rgba(255, 99, 132, 1)",
    +          "rgba(54, 162, 235, 1)",
    +          "rgba(255, 206, 86, 1)",
    +          "rgba(75, 192, 192, 1)",
    +          "rgba(153, 102, 255, 1)",
    +          "rgba(255, 159, 64, 1)"
    +        ],
    +        "borderWidth": 1
    +      }
    +    ]
    +  },
    +  "options": {
    +    "scales": {
    +      "y": {
    +        "beginAtZero": true
    +      }
    +    }
    +  }
    +}
    +`
    +
    +const prop = encodeData(content) // "eJyNUsFOwzAMve8rrHABKZqWlg5WxAE4cARxAMHEIV1NmQhNlaaCCe3fcdKtW0sLWGpjxy/v+UV512mlcIyfhTa2hHP4GgHYVYExsEQaxqlMpZWxbwAomaAqY5izO0wZB3apKnTrIyqlP1x2bRBzl9xWplC+eWNkniF7dmw1X4nWsfgaNtwNP2kfgH6Be22x9CPUUQ8yFwEHMeMQcog4UBFuiF0kcvGWGV3l6ZVW2uw0XDCTJfIwiOjYjAhESIcn4+BoT2MLio6pP6V+EBJ6AOSZgsmUwyl9A6ATwoiZn3lYTkTkRkycnuP8TU9ENPqUxuuA9i9BmxTNPy9A/G2/F9I23wtpW++FdIwPKzW2W5Afph+WqX2NQWz313XicT7XhV3qnB5f/ejKhVTYVACrXUqUmC3zC/uERsdgTYUdVr/Qb302+gZxe7S/"
    +
    +decodeData(prop) // will be the original content
    +
    +// if you use `encodeURIComponent`, it will be much longer
    +encodeURIComponent(content) // '%0A%7B%0A%20%20%22type%22%3A%20%22bar%22%2C%0A%20%20%22data%22%3A%20%7B%0A%20%20%20%20%22labels%22%3A%20%5B%22Red%22%2C%20%22Blue%22%2C%20%22Yellow%22%2C%20%22Green%22%2C%20%22Purple%22%2C%20%22Orange%22%5D%2C%0A%20%20%20%20%22datasets%22%3A%20%5B%0A%20%20%20%20%20%20%7B%0A%20%20%20%20%20%20%20%20%22label%22%3A%20%22%23%20of%20Votes%22%2C%0A%20%20%20%20%20%20%20%20%22data%22%3A%20%5B12%2C%2019%2C%203%2C%205%2C%202%2C%203%5D%2C%0A%20%20%20%20%20%20%20%20%22backgroundColor%22%3A%20%5B%0A%20%20%20%20%20%20%20%20%20%20%22rgba(255%2C%2099%2C%20132%2C%200.2)%22%2C%0A%20%20%20%20%20%20%20%20%20%20%22rgba(54%2C%20162%2C%20235%2C%200.2)%22%2C%0A%20%20%20%20%20%20%20%20%20%20%22rgba(255%2C%20206%2C%2086%2C%200.2)%22%2C%0A%20%20%20%20%20%20%20%20%20%20%22rgba(75%2C%20192%2C%20192%2C%200.2)%22%2C%0A%20%20%20%20%20%20%20%20%20%20%22rgba(153%2C%20102%2C%20255%2C%200.2)%22%2C%0A%20%20%20%20%20%20%20%20%20%20%22rgba(255%2C%20159%2C%2064%2C%200.2)%22%0A%20%20%20%20%20%20%20%20%5D%2C%0A%20%20%20%20%20%20%20%20%22borderColor%22%3A%20%5B%0A%20%20%20%20%20%20%20%20%20%20%22rgba(255%2C%2099%2C%20132%2C%201)%22%2C%0A%20%20%20%20%20%20%20%20%20%20%22rgba(54%2C%20162%2C%20235%2C%201)%22%2C%0A%20%20%20%20%20%20%20%20%20%20%22rgba(255%2C%20206%2C%2086%2C%201)%22%2C%0A%20%20%20%20%20%20%20%20%20%20%22rgba(75%2C%20192%2C%20192%2C%201)%22%2C%0A%20%20%20%20%20%20%20%20%20%20%22rgba(153%2C%20102%2C%20255%2C%201)%22%2C%0A%20%20%20%20%20%20%20%20%20%20%22rgba(255%2C%20159%2C%2064%2C%201)%22%0A%20%20%20%20%20%20%20%20%5D%2C%0A%20%20%20%20%20%20%20%20%22borderWidth%22%3A%201%0A%20%20%20%20%20%20%7D%0A%20%20%20%20%5D%0A%20%20%7D%2C%0A%20%20%22options%22%3A%20%7B%0A%20%20%20%20%22scales%22%3A%20%7B%0A%20%20%20%20%20%20%22y%22%3A%20%7B%0A%20%20%20%20%20%20%20%20%22beginAtZero%22%3A%20true%0A%20%20%20%20%20%20%7D%0A%20%20%20%20%7D%0A%20%20%7D%0A%7D%0A'
    +

    Type Helper

    • isDef(x): Check if x is defined.
    • isBoolean(x): Check if x is a boolean.
    • isString(x): Check if x is a string.
    • isNumber(x): Check if x is a number.
    • isPlainObject(x): Check if x is a plain object.
    • isArray(x): Check if x is an array.
    • isFunction(x): Check if x is a function.
    • isRegExp(x): Check if x is a regular expression.
    • startsWith(a, b): Check if string a starts with string b.
    • endsWith(a, b): Check if string a ends with string b.

    Return false if a is not a string.

    对象相关

    • keys(x): Return an array of keys of object x.

    • values(x): Return an array of values of object x.

    • entries(x): Convert object x to an array of key-value pairs.

    • fromEntries(x): Convert an array of key-value pairs x to an object.

    • deepAssign(x, y, ...): A deep version of Object.assign.

      Example
      // or @vuepress/helper/client
      +import { deepAssign } from '@vuepress/helper'
      +
      +const defaultOptions = {
      +  optionA: {
      +    optionA1: 'defaultOptionA1',
      +    optionA2: 'defaultOptionA2',
      +    optionA3: 'defaultOptionA3',
      +  },
      +  optionB: true,
      +  optionC: 'optionC',
      +}
      +
      +const userOptions = {
      +  optionA: {
      +    optionA1: 'optionA1',
      +    optionA2: 'optionA2',
      +  },
      +  optionB: false,
      +}
      +
      +deepAssign(defaultOptions, userOptions)
      +// {
      +//   optionA: {
      +//     optionA1: "optionA1",
      +//     optionA2: "optionA2",
      +//     optionA3: "defaultOptionA3",
      +//   },
      +//   optionB: false,
      +//   optionC: "optionC",
      +// }
      +
    • getDate(x): Convert input x to a date. It can support Date, timestamp, and date string. The support degree of date string depends on the Date.parse support degree of the environment. Return null when it cannot be converted to a date.

      Example
      getDate('2021-01-01') // a Date object represents 2021-01-01
      +getDate(1609459200000) // a Date object represents 2021-01-01
      +getDate('2021-01-01T00:00:00.000Z') // a Date object represents 2021-01-01
      +getDate('2021/01/01') // a Date object represents 2021-01-01 (might be null in some browsers)
      +getDate('invalid date') // null
      +getDate(undefined) // null
      +getDate(-32) // null
      +
    • dateSorter: Sort the values that can be converted to dates from new to old, and the values that cannot be converted to dates will be at the end.

      Example
      const arr = [
      +  '2020-01-01',
      +  1609459200000,
      +  '2022-01-01T00:00:00.000Z',
      +  '2023/01/01',
      +  'invalid date',
      +  undefined,
      +  -32,
      +]
      +
      +arr.sort(dateSorter)
      +// [
      +//   '2023/01/01',
      +//   '2022-01-01T00:00:00.000Z',
      +//   1609459200000,
      +//   '2020-01-01',
      +//   'invalid date',
      +//   undefined,
      +//   -32,
      +// ]
      +
    • isLinkHttp(x): Check if x is a valid HTTP URL.
    • isLinkWithProtocol(x): Check if x is a valid URL with protocol.
    • isLinkExternal(x): Check if x is a valid external URL.
    • isLinkAbsolute(x): Check if x is a valid absolute URL.
    • ensureEndingSlash(x): Ensure x ends with a slash.
    • ensureLeadingSlash(x): Ensure x starts with a slash.
    • removeEndingSlash(x): Ensure x does not end with a slash.
    • removeLeadingSlash(x): Ensure x does not start with a slash.
    + + + diff --git a/tools/index.html b/tools/index.html new file mode 100644 index 0000000000..7d4e82a8ef --- /dev/null +++ b/tools/index.html @@ -0,0 +1,37 @@ + + + + + + + + + Tools | VuePress Ecosystem + + + + + + + + + diff --git a/zh/index.html b/zh/index.html new file mode 100644 index 0000000000..ea9dab6ed6 --- /dev/null +++ b/zh/index.html @@ -0,0 +1,37 @@ + + + + + + + + + 首页 | VuePress 生态系统 + + + + + + + + + diff --git a/zh/plugins/active-header-links.html b/zh/plugins/active-header-links.html new file mode 100644 index 0000000000..1b8059d3ec --- /dev/null +++ b/zh/plugins/active-header-links.html @@ -0,0 +1,47 @@ + + + + + + + + + active-header-links | VuePress 生态系统 + + + + + +

    active-header-links

    @vuepress/plugin-active-header-links

    该插件会监听页面滚动事件。当页面滚动至某个 标题锚点 后,如果存在对应的 标题链接 ,那么该插件会将路由 Hash 更改为该 标题锚点

    该插件主要用于开发主题,并且已经集成到默认主题中。大部分情况下你不需要直接使用它。

    使用方法

    npm i -D @vuepress/plugin-active-header-links@next
    +
    import { activeHeaderLinksPlugin } from '@vuepress/plugin-active-header-links'
    +
    +export default {
    +  plugins: [
    +    activeHeaderLinksPlugin({
    +      // 配置项
    +    }),
    +  ],
    +}
    +

    配置项

    headerLinkSelector

    • 类型: string

    • 默认值: 'a.sidebar-item'

    • 详情:

      标题链接 的选择器。

      如果一个 标题锚点 没有对应的 标题链接 ,那么即使滚动到这个 标题锚点 ,该插件也不会更改路由 Hash 。

    headerAnchorSelector

    delay

    • 类型: number

    • 默认值: 200

    • 详情:

      滚动事件监听器的 Debounce 延迟。

    offset

    • 类型: number

    • 默认值: 5

    • 详情:

      即便直接点击 标题锚点 的链接, scrollTop 也可能不会完全等于 标题锚点offsetTop ,所以我们添加一个 Offset 偏移量来避免这个误差。

    + + + diff --git a/zh/plugins/back-to-top.html b/zh/plugins/back-to-top.html new file mode 100644 index 0000000000..07b7b70055 --- /dev/null +++ b/zh/plugins/back-to-top.html @@ -0,0 +1,51 @@ + + + + + + + + + back-to-top | VuePress 生态系统 + + + + + +

    back-to-top

    @vuepress/plugin-back-to-top

    该插件会给你的站点添加一个 返回顶部 按钮。当页面向下滚动时,该按钮会显示在页面的右下角,点击它就会滚动到页面顶部。

    该插件已经集成到默认主题中。

    使用方法

    npm i -D @vuepress/plugin-back-to-top@next
    +
    import { backToTopPlugin } from '@vuepress/plugin-back-to-top'
    +
    +export default {
    +  plugins: [backToTopPlugin()],
    +}
    +

    选项

    threshold

    • 类型:数字
    • 默认值:100
    • 详情:显示返回顶部按钮的滚动阈值距离(以像素为单位)

    progress

    • 类型:布尔值
    • 默认值:true
    • 详情:是否在图标周围显示进度条

    样式

    你可以通过 CSS 变量来自定义 返回顶部 按钮的样式:

    :root {
    +  --back-to-top-z-index: 5;
    +  --back-to-top-icon: url("back-to-top.svg");
    +  --back-to-top-bg-color: #fff;
    +  --back-to-top-color: #3eaf7c;
    +  --back-to-top-color-hover: #71cda3;
    +  --back-to-top-shadow: rgb(0 0 0 / 20%);
    +}
    +
    + + + diff --git a/zh/plugins/baidu-analytics.html b/zh/plugins/baidu-analytics.html new file mode 100644 index 0000000000..8c2c58c675 --- /dev/null +++ b/zh/plugins/baidu-analytics.html @@ -0,0 +1,47 @@ + + + + + + + + + baidu-analytics | VuePress 生态系统 + + + + + +

    baidu-analytics

    @vuepress/plugin-baidu-analytics

    百度统计在新窗口打开 集成到 VuePress 中。

    使用方法

    npm i -D @vuepress/plugin-baidu-analytics@next
    +
    import { baiduAnalyticsPlugin } from '@vuepress/plugin-baidu-analytics'
    +
    +export default {
    +  plugins: [
    +    baiduAnalyticsPlugin({
    +      // 配置项
    +    }),
    +  ],
    +}
    +

    上报事件

    在使用该插件之后,一个全局的 hmt 数组会被挂载到 window 对象上,你可以使用它进行 自定义事件的上报在新窗口打开

    选项

    id

    • 类型: string
    • 详情: 百度统计的 ID ,即 hm.js URL 中的查询参数。
    + + + diff --git a/zh/plugins/blog/config.html b/zh/plugins/blog/config.html new file mode 100644 index 0000000000..ee1879d496 --- /dev/null +++ b/zh/plugins/blog/config.html @@ -0,0 +1,179 @@ + + + + + + + + + 配置 | VuePress 生态系统 + + + + + +

    配置

    插件选项

    getInfo

    • 类型: (page: Page) => Record<string, unknown>

    • 必填: 否

    • 参考:

    • 详情:

      获取文章信息的函数。

      获取到的信息会被稍后注入至路由元数据,以便你可以在客户端中通过组合式 API 获取。

    filter

    • 类型: (page: Page) => boolean

    • 默认值: (page) => Boolean(page.filePathRelative) && !page.frontmatter.home

    • 参考:

    • 详情:

      页面过滤器,此函数用于鉴别页面是否作为文章。

      默认情况下,所有从 Markdown 源文件中生成的非主页页面,会被作为文章。

    category

    type

    slugify

    • 类型: (name: string) => string
    • 默认值: (name) => name.replace(/ _/g, '-').replace(/[:?*|\\/<>]/g, "").toLowerCase()
    • 详情:Slugify 函数,用于转换 key 在路由中注册的形式。

    excerpt

    • 类型: boolean
    • 默认值: true
    • 详情:是否生成摘要。

    excerptSeparator

    • 类型: string
    • 默认值: <!-- more -->
    • 详情:摘要分隔符。

    excerptLength

    • 类型: number

    • 默认值: 300

    • 参考:

    • 详情:

      自动生成的摘要的长度。

      提示

      摘要的长度会尽可能的接近这个值。如果设置为 0,意味着不自动生成摘要。

    excerptFilter

    • 类型: (page: Page) => boolean

    • 默认值: filter 选项

    • 参考:

    • 详情: 页面过滤器,此函数用于鉴别插件是否需要生成摘要。

      提示

      你可以使用此函数来跳过你不需要生成摘要的页面。例如:如果用户在 frontmatter 中设置了 excerptdescription,你可能希望直接使用它们。

    isCustomElement

    • 类型: (tagName: string) => boolean

    • 默认值: () => false

    • 参考:

    • 详情: 被认为是自定义元素的标签。

      用于判断一个标签是否是自定义元素,因为在摘要中,所有的未知标签都会被移除。

    metaScope

    • 类型: string

    • 默认值: "_blog"

    • 详情: 注入文章信息至路由元数据时使用的键名。

      提示

      设置为空字符串会直接注入路由元数据 (而不是一个键下)。

    hotReload

    • 类型: boolean

    • 默认值: 是否使用 --debug 标记

    • 详情: 是否在开发服务器中启用实时热重载。

      致主题开发者

      默认情况下它是禁用的,因为它确实会对具有很多分类和类别的站点产生性能影响,并且在编辑 Markdown 时会减慢热重载的速度。

      如果用户正在添加或组织类别或标签,你可以告诉他们启用此功能,其余的时间最好禁用它。

      此外,你可以尝试检测用户项目中的页面数并决定是否启用它。

    博客分类配置

    博客分类配置应为一个数组,每一项控制一个分类规则。

    interface BlogCategoryOptions {
    +  /**
    +   * 唯一的分类名称
    +   */
    +  key: string
    +
    +  /**
    +   * 从页面中获取分类的函数
    +   */
    +  getter: (page: Page) => string[]
    +
    +  /**
    +   * 页面排序器
    +   */
    +  sorter?: (pageA: Page, pageB: Page) => number
    +
    +  /**
    +   * 待注册的页面路径图案
    +   *
    +   * @description `:key` 将会被替换为原 key 的 slugify 结果
    +   *
    +   * @default `/:key/`
    +   */
    +  path?: string | false
    +
    +  /**
    +   * 页面布局组件名称
    +   *
    +   * @default 'Layout'
    +   */
    +  layout?: string
    +
    +  /**
    +   * Front Matter 配置
    +   */
    +  frontmatter?: (localePath: string) => Record<string, string>
    +
    +  /**
    +   * 待注册的项目页面路径图案或自定义函数
    +   *
    +   * @description 当填入字符串的时候, `:key` 和 `:name` 会被自动替换为原始的 key、name 的 slugify 结果。
    +   *
    +   * @default `/:key/:name/`
    +   */
    +  itemPath?: string | ((name: string) => string) | false
    +
    +  /**
    +   * 项目页面布局组件名称
    +   *
    +   * @default 'Layout'
    +   */
    +  itemLayout?: string
    +
    +  /**
    +   * 项目 Front Matter 配置
    +   */
    +  itemFrontmatter?: (name: string, localePath: string) => Record<string, string>
    +}
    +

    博客类型配置

    博客类型配置应为一个数组,每一项控制一个类型规则。

    interface BlogTypeOptions {
    +  /**
    +   * 唯一的类型名称
    +   */
    +  key: string
    +
    +  /**
    +   * 一个过滤函数来决定页面是否满足此类型
    +   */
    +  filter: (page: Page) => boolean
    +
    +  /**
    +   * 页面排序器
    +   */
    +  sorter?: (pageA: Page, pageB: Page) => number
    +
    +  /**
    +   * 待注册的页面路径
    +   *
    +   * @default '/:key/'
    +   */
    +  path?: string | false
    +
    +  /**
    +   * 页面布局组件名称
    +   *
    +   * @default 'Layout'
    +   */
    +  layout?: string
    +
    +  /**
    +   * Front Matter 配置
    +   */
    +  frontmatter?: (localePath: string) => Record<string, string>
    +}
    +

    可组合式 API

    你可以从 @vuepress/plugin-blog/client 导入下列 API:

    • 博客分类

      const useBlogCategory: <
      +  T extends Record<string, unknown> = Record<string, unknown>,
      +>(
      +  key?: string,
      +) => ComputedRef<BlogCategoryData<T>>
      +

      参数 key 为需要获取的键名。如果未传入 key,会尝试使用与当前路径匹配的 key。

    • 博客类型

      const useBlogType: <
      +  T extends Record<string, unknown> = Record<string, unknown>,
      +>(
      +  key?: string,
      +) => ComputedRef<BlogTypeData<T>>
      +

      参数 key 为需要获取的键名。如果未传入 key,会尝试使用与当前路径匹配的 key。

    详细的返回值如下:

    interface Article<T extends Record<string, unknown> = Record<string, unknown>> {
    +  /** 文章路径 */
    +  path: string
    +  /** 文章信息 */
    +  info: T
    +}
    +
    +interface BlogCategoryData<
    +  T extends Record<string, unknown> = Record<string, unknown>,
    +> {
    +  /** 分类路径 */
    +  path: string
    +
    +  /**
    +   * 仅当当前路径和某个子项目匹配时可用
    +   */
    +  currentItems?: Article<T>[]
    +
    +  /** 分类映射 */
    +  map: {
    +    /** 当前分类下全局唯一的 key */
    +    [key: string]: {
    +      /** 对应键值的分类路径 */
    +      path: string
    +      /** 对应键值的项目 */
    +      items: Article<T>[]
    +    }
    +  }
    +}
    +
    +interface BlogTypeData<
    +  T extends Record<string, unknown> = Record<string, unknown>,
    +> {
    +  /** 类别路径 */
    +  path: string
    +
    +  /** 当前类别下的项目 */
    +  items: Article<T>[]
    +}
    +
    + + + diff --git a/zh/plugins/blog/guide.html b/zh/plugins/blog/guide.html new file mode 100644 index 0000000000..4ce8e8fad9 --- /dev/null +++ b/zh/plugins/blog/guide.html @@ -0,0 +1,251 @@ + + + + + + + + + 指南 | VuePress 生态系统 + + + + + +

    使用 @vuepress/plugin-blog,你可以轻松地将博客功能引入主题。

    收集文章并生成信息

    起步时,插件会首选过滤并选择那些需要作为文章的页面。这将剔除你不想要的页面,并在后续处理中排除它们。

    默认情况下,所有从 Markdown 文件生成但不是主页的页面,都将被视作文章。

    你可能需要设置 filter 选项来完全自定义要收集的页面。 filter 接受一个形状为 (page: Page) => boolean 的函数。

    接着,你应该设置 getInfo 选项为一个接受 Page 作为参数并返回包含所需信息的对象的函数。这样稍后,你可以从组合 API 中获取这些信息。

    案例
    import { blogPlugin } from '@vuepress/plugin-blog'
    +
    +export default {
    +  name: 'vuepress-theme-xxx',
    +  plugins: [
    +    blogPlugin({
    +      filter: ({ filePathRelative, frontmatter }) => {
    +        // 舍弃那些不是从 Markdown 文件生成的页面
    +        if (!filePathRelative) return false
    +
    +        // 舍弃 `archives` 文件夹的页面
    +        if (filePathRelative.startsWith('archives/')) return false
    +
    +        // 舍弃那些没有使用默认布局的页面
    +        if (frontmatter.home || frontmatter.layout) return false
    +
    +        return true
    +      },
    +
    +      getInfo: ({ frontmatter, git = {}, data = {} }) => {
    +        // 获取页面信息
    +        const info: Record<string, any> = {
    +          author: frontmatter.author || '',
    +          categories: frontmatter.categories || [],
    +          date: frontmatter.date || git.createdTime || null,
    +          tags: frontmatter.tags || [],
    +          excerpt: data.excerpt || '',
    +        }
    +
    +        return info
    +      },
    +    }),
    +    // 其他插件 ...
    +  ],
    +}
    +

    自定义类别和类型

    基本上,你的博客中需要两种“类型”:

    • 类别:

      “类别”是用文章的标签 (或类别) 对它们进行分组。

      例如,每篇文章可能都有对应的“分类”和“标签”。

    • 类型:

      “类型”是过滤不同条件的文章。

      例如,你的帖子中可能有日记或笔记。当帖子带有写作日期信息时,它可以称为“时间线项目”。

    了解这两种类型的描述后,你可以设置 categorytype 选项,它们都接受一个数组,每个元素代表一个配置。

    让我们从此处 2 个例子开始。

    假设你想为每篇文章设置标签,并且你正在通过 frontmatter.tag 设置它们。同时,你想要在 /tag/ 中使用 TagMap 布局的标签页面,并在/tag/标签名称 中使用 TagList 布局对标签按名称进行分组,你可能需要这样的配置:

    import { blogPlugin } from '@vuepress/plugin-blog'
    +
    +export default {
    +  name: 'vuepress-theme-xxx',
    +  plugins: [
    +    blogPlugin({
    +      // 其他配置 ...
    +      category: [
    +        {
    +          key: 'tag',
    +          getter: ({ frontmatter }) => frontmatter.tag || [],
    +          path: '/tag/',
    +          layout: 'TagMap',
    +          frontmatter: () => ({ title: '标签页' }),
    +          itemPath: '/tag/:name/',
    +          itemLayout: 'TagList',
    +          itemFrontmatter: (name) => ({ title: `${name}标签` }),
    +        },
    +      ],
    +    }),
    +    // 其他插件 ...
    +  ],
    +}
    +

    此外,你可能希望为你的一些文章加注星标,并将其展示给访问者。当你在 frontmatter 中设置 star: true 来标记它们时,你可能需要这样的配置来在 /star/ 路径中以 StarList 布局显示它们:

    import { blogPlugin } from '@vuepress/plugin-blog'
    +
    +export default {
    +  name: 'vuepress-theme-xxx',
    +  plugins: [
    +    blogPlugin({
    +      // 其他配置 ...
    +      type: [
    +        {
    +          key: 'star',
    +          filter: ({ frontmatter }) => frontmatter.star,
    +          path: '/star/',
    +          layout: 'StarList',
    +          frontmatter: () => ({ title: '星标文章' }),
    +        },
    +      ],
    +    }),
    +    // 其他插件 ...
    +  ],
    +}
    +

    看,设置这两种类型很容易。有关完整选项,请参阅 博客分类配置博客分类配置

    在客户端使用组合 API

    当生成每个页面时,插件将在 frontmatter.blog 中设置如下信息

    interface BlogFrontmatterOptions {
    +  /** 当前页面的类型 */
    +  type: 'category' | 'type'
    +  /** 在当前分类或类别下全局唯一的 key */
    +  key: string
    +  /**
    +   * 当前的分类名称
    +   *
    +   * @description 仅在分类子项目页面中可用
    +   */
    +  name?: string
    +}
    +

    所以你可以直接调用 useBlogCategory()useBlogType(),结果将是当前路由绑定的类别或类型。

    此外,你可以通过传递所需的 key 作为参数,来将获得绑定到该 key 的信息。

    对于上方的 Node 配置而言,你可以在客户端通过如下方式获取 tag 和 star 的信息:

    TagMap 布局:

    <template>
    +  <div>
    +    <h1>Tag page</h1>
    +    <ul>
    +      <li v-for="({ items, path }, name) in categoryMap.map">
    +        <RouteLink :key="name" :to="path" class="category">
    +          {{ name }}
    +          <span class="category-num">
    +            {{ items.length }}
    +          </span>
    +        </RouteLink>
    +      </li>
    +    </ul>
    +  </div>
    +</template>
    +<script setup lang="ts">
    +import { useBlogCategory } from '@vuepress/plugin-blog'
    +import { RouteLink } from 'vuepress/client'
    +
    +const categoryMap = useBlogCategory('tag')
    +</script>
    +

    TagList 布局:

    <template>
    +  <div>
    +    <h1>Tag page</h1>
    +    <div class="category-wrapper">
    +      <RouteLink
    +        v-for="({ items, path }, name) in categoryMap.map"
    +        :key="name"
    +        :to="path"
    +        class="category"
    +      >
    +        {{ name }}
    +        <span class="category-num">
    +          {{ items.length }}
    +        </span>
    +      </RouteLink>
    +    </div>
    +    <div class="article-wrapper" v-if="categoryMap.currentItems">
    +      <div v-if="!categoryMap.currentItems.length">Nothing in here.</div>
    +      <article
    +        v-for="{ info, path } in categoryMap.currentItems"
    +        class="article"
    +        @click="$router.push(path)"
    +      >
    +        <header class="title">
    +          {{
    +            (isTimeline
    +              ? `${new Date(info.date).toLocaleDateString()}: `
    +              : '') + info.title
    +          }}
    +        </header>
    +        <hr />
    +        <div class="article-info">
    +          <span v-if="info.author" class="author"
    +            >Author: {{ info.author }}</span
    +          >
    +          <span v-if="info.date && !isTimeline" class="date"
    +            >Date: {{ new Date(info.date).toLocaleDateString() }}</span
    +          >
    +          <span v-if="info.category" class="category"
    +            >Category: {{ info.category.join(', ') }}</span
    +          >
    +          <span v-if="info.tag" class="tag"
    +            >Tag: {{ info.tag.join(', ') }}</span
    +          >
    +        </div>
    +        <div v-if="info.excerpt" class="excerpt" v-html="info.excerpt" />
    +      </article>
    +    </div>
    +  </div>
    +</template>
    +<script setup lang="ts">
    +import { useBlogCategory } from '@vuepress/plugin-blog'
    +import { RouteLink } from 'vuepress/client'
    +
    +const categoryMap = useBlogCategory('tag')
    +</script>
    +

    StarList 布局:

    <template>
    +  <div class="article-wrapper" v-if="stars.items">
    +    <div v-if="!stars.items.length">Nothing in here.</div>
    +    <article
    +      v-for="{ info, path } in stars.items"
    +      class="article"
    +      @click="$router.push(path)"
    +    >
    +      <header class="title">
    +        {{
    +          (isTimeline ? `${new Date(info.date).toLocaleDateString()}: ` : '') +
    +          info.title
    +        }}
    +      </header>
    +      <hr />
    +      <div class="article-info">
    +        <span v-if="info.author" class="author">Author: {{ info.author }}</span>
    +        <span v-if="info.date && !isTimeline" class="date"
    +          >Date: {{ new Date(info.date).toLocaleDateString() }}</span
    +        >
    +        <span v-if="info.category" class="category"
    +          >Category: {{ info.category.join(', ') }}</span
    +        >
    +        <span v-if="info.tag" class="tag">Tag: {{ info.tag.join(', ') }}</span>
    +      </div>
    +      <div v-if="info.excerpt" class="excerpt" v-html="info.excerpt" />
    +    </article>
    +  </div>
    +</template>
    +<script setup lang="ts">
    +import { useBlogType } from '@vuepress/plugin-blog/client'
    +
    +import ArticleList from '../components/ArticleList.vue'
    +import ParentLayout from '@vuepress/theme-default/layouts/Layout.vue'
    +
    +const stars = useBlogType('star')
    +</script>
    +

    有关返回类型,请参阅 Composition API 返回类型

    多语言支持

    该插件添加了原生多语言支持,因此你的设置将自动应用于每种语言。

    例如,如果用户进行了以下 locales 配置,并且你正在设置上面的“star”示例:

    export default {
    +  locales: {
    +    '/': {
    +      lang: 'en-US',
    +    },
    +    '/zh/': {
    +      lang: 'zh-CN',
    +    },
    +  },
    +}
    +

    那么 /zh/star//star/ 都将可用,并且只会显示对应语言下的文章。

    摘要生成

    这个插件提供了一个内置的摘要生成器,可以通过将 excerpt 选项设置为 true 来启用。

    摘要介绍

    摘要是一个 HTML 片段,被用于在博客列表中显示文章的简短描述,所以摘要有如下限制:

    • 摘要不支持任何未知标签以及 Vue 语法,所以此类内容会在生成时被移除。如果你有自定义组件 (非 Vue 组件),请配置 isCustomElement 选项。
    • 由于摘要是一个 HTML 片段,所以你将无法通过相对路径或别名引入任何图片,这些图片会被直接移除。如果你想要保留图片,请使用基于 .vuepress/public 的绝对路径或完整路径以确保它们可以在其他地址被访问。

    摘要生成器将尝试从 Frontmatter 内容中找到有效的摘要分隔符,如果找到,它将使用分隔符之前的内容,分隔符默认为 <!-- more -->,并且你可以通过 excerptSeparator 选项来自定义它。

    如果找不到有效的分隔符,它将从 Markdown 文件的开头开始解析内容,直到长度达到预设值时停止。该值默认为 300,你可以通过设置 excerptLength 选项来自定义它。

    要选择哪个页面应该生成摘要,你可以使用 excerptFilter 选项。

    示例

    通常,如果用户设置了 frontmatter.description,你可能希望使用它们,因此如果 frontmatter.description 不为空,你可以让过滤器函数返回 false

    + + + diff --git a/zh/plugins/blog/index.html b/zh/plugins/blog/index.html new file mode 100644 index 0000000000..098b55fc38 --- /dev/null +++ b/zh/plugins/blog/index.html @@ -0,0 +1,47 @@ + + + + + + + + + blog | VuePress 生态系统 + + + + + +

    blog

    @vuepress/plugin-blog

    使用

    npm i -D @vuepress/plugin-blog@next
    +
    import { blogPlugin } from '@vuepress/plugin-blog'
    +
    +export default {
    +  plugins: [
    +    blogPlugin({
    +      // 选项
    +    }),
    +  ],
    +}
    +
    + + + diff --git a/zh/plugins/catalog.html b/zh/plugins/catalog.html new file mode 100644 index 0000000000..692dc0e50c --- /dev/null +++ b/zh/plugins/catalog.html @@ -0,0 +1,129 @@ + + + + + + + + + catalog | VuePress 生态系统 + + + + + +

    catalog

    @vuepress/plugin-catalog

    此插件可以自动生成目录页面,也提供目录组件。

    使用方法

    npm i -D @vuepress/plugin-catalog@next
    +
    import { catalogPlugin } from '@vuepress/plugin-catalog'
    +
    +export default {
    +  plugins: [
    +    catalogPlugin({
    +      // 你的选项
    +    }),
    +  ],
    +}
    +

    首先,你应该在路由元信息中设置目录信息:

    import { catalogPlugin } from '@vuepress/plugin-catalog'
    +
    +export default {
    +  extendsPage: (page) => {
    +    // 在 routeMeta 中设置目录信息
    +    page.routeMeta = {
    +      // 目录标题
    +      title: page.title,
    +      // ... 其他信息
    +    }
    +  },
    +}
    +

    你可以之后导入 defineCatalogInfoGetter 并在 客户端配置文件在新窗口打开 中使用它来从元信息中提取目录信息。

    import { defineCatalogInfoGetter } from '@vuepress/plugin-catalog/client'
    +
    +export default {
    +  setup: () => {
    +    defineCatalogInfoGetter((meta) =>
    +      meta.title ? { title: meta.title } : null,
    +    )
    +  },
    +}
    +

    目录信息应包含:

    • title: 目录标题
    • order: 目录顺序 (可选)
    • content: 目录内容组件 (可选)

    通过 order 排序

    插件将按以下方式通过 order 对页面进行排序:

    // 从小到大依次排列正数
    +order 1 的项目
    +order 2 的项目
    +...
    +order 10 的项目
    +...
    +// 无 order 的项目
    +无 order 的项目
    +无 order 的项目
    +...
    +// 从小到大依次排列负数
    +order -10 的项目
    +// ...
    +order -2 的项目
    +order -1 的项目
    +

    选项

    level 仅限内置组件

    • 类型:1 | 2 | 3
    • 默认值:3
    • 详情:目录项级别的最大深度。

    index 仅限内置组件

    • 类型:boolean
    • 默认值:false
    • 详情:目录是否显示索引

    frontmatter

    • 类型:(path: string) => Record<string, any>

    • 详情:生成页面的 Frontmatter 获取器。

    • 示例:

      import { catalogPlugin } from '@vuepress/plugin-catalog'
      +
      +export default {
      +  plugins: [
      +    catalogPlugin({
      +      frontmatter: (path) => ({
      +        // 你想要的 frontmatter
      +        // 你可以自定义标题、作者、时间等
      +      }),
      +    }),
      +  ],
      +}
      +

    exclude

    • 类型:(RegExp | string)[]

    • 默认值:[]

    • 详情:

      生成中需要排除的目录页路径。

      • "/foo/" 意味着仅排除 /foo/ 文件夹的目录页生成。
      • /^\/foo\// 意味着排除 /foo/ 文件夹及其子文件夹的目录页生成。

      404 页面会被自动排除。

    component

    • 类型:string
    • 详情:用作目录的组件名称。

    locales

    • 类型:CatalogPluginLocaleConfig

      interface CatalogPluginLocaleData {
      +  /**
      +   * 目录标题
      +   */
      +  title: string
      +
      +  /**
      +   * 空目录提示
      +   */
      +  empty: string
      +}
      +
      +interface CatalogPluginLocaleConfig {
      +  [localePath: string]: CatalogPluginLocaleData
      +}
      +
    • 必填: 否

    • 详情:目录组件国际化配置。

    内置支持语言
    • 简体中文 (zh-CN)
    • 繁体中文 (zh-TW)
    • 英文(美国) (en-US)
    • 德语 (de-DE)
    • 德语(澳大利亚) (de-AT)
    • 俄语 (ru-RU)
    • 乌克兰语 (uk-UA)
    • 越南语 (vi-VN)
    • 葡萄牙语(巴西) (pt-BR)
    • 波兰语 (pl-PL)
    • 法语 (fr-FR)
    • 西班牙语 (es-ES)
    • 斯洛伐克 (sk-SK)
    • 日语 (ja-JP)
    • 土耳其语 (tr-TR)
    • 韩语 (ko-KR)
    • 芬兰语 (fi-FI)
    • 印尼语 (id-ID)
    • 荷兰语 (nl-NL)
    • 印尼语 (id-ID)
    • 荷兰语 (nl-NL)

    客户端选项

    defineCatalogInfoGetter

    interface CatalogInfo {
    +  /** 目录标题 */
    +  title: string
    +  /** 目录顺序 */
    +  order?: number
    +  /** 目录内容 */
    +  content?: Component
    +}
    +
    +type CatalogInfoGetter = (meta: Record<string, unknown>) => CatalogInfo | null
    +
    +const defineCatalogInfoGetter: (options: CatalogInfoGetter) => void
    +

    自定义如何从 meta 中提取目录信息。

    组件

    Catalog

    • 详情:

      该插件默认会全局注册一个 <Catalog /> 组件(除非你设置了 component 选项)。

      你可以在主题布局中或直接在 Markdown 文件中使用 <Catalog />

      组件支持四个属性:

      • level:更改显示层次深度(最大仅支持 3 层),默认为 3
      • base:显示指定文件夹的目录,默认显示当前文件夹目录。
      • index:为目录项添加索引号,默认无标号。
      • hideHeading:隐藏组件标题,默认会显示 目录 标题。

    样式

    你可以通过 CSS 变量来自定义目录样式:

    :root {
    +  --catalog-bg-color: #fff;
    +  --catalog-bg-secondary-color: #f8f8f8;
    +  --catalog-border-color: #e5e5e5;
    +  --catalog-active-color: #3eaf7c;
    +  --catalog-hover-color: #71cda3;
    +}
    +
    + + + diff --git a/zh/plugins/container.html b/zh/plugins/container.html new file mode 100644 index 0000000000..999424b749 --- /dev/null +++ b/zh/plugins/container.html @@ -0,0 +1,75 @@ + + + + + + + + + container | VuePress 生态系统 + + + + + +

    container

    @vuepress/plugin-container

    为你的 VuePress 站点注册自定义容器。

    该插件简化了 markdown-it-container在新窗口打开 的使用方法,但同时也保留了其原本的能力。

    默认主题的 自定义容器 就是由该插件支持的。

    使用方法

    npm i -D @vuepress/plugin-container@next
    +
    import { containerPlugin } from '@vuepress/plugin-container'
    +
    +export default {
    +  plugins: [
    +    containerPlugin({
    +      // 配置项
    +    }),
    +  ],
    +}
    +

    容器语法

    ::: <type> [info]
    +[content]
    +:::
    +
    • type 是必需的,应通过 type 配置项来指定。
    • info 是可选的,其默认值可以通过 localesdefaultInfo 配置项来指定。
    • content 可是任何合法的 Markdown 内容。

    提示

    该插件可以被多次使用,以便支持不同类型的容器。

    配置项

    type

    locales

    • 类型: Record<string, { defaultInfo: string }>

    • 详情:

      容器在不同 locales 下的默认 info

      如果没有指定该配置项,默认 info 会使用大写的 type

    • 示例:

    export default {
    +  plugins: [
    +    containerPlugin({
    +      type: 'tip',
    +      locales: {
    +        '/': {
    +          defaultInfo: 'TIP',
    +        },
    +        '/zh/': {
    +          defaultInfo: '提示',
    +        },
    +      },
    +    }),
    +  ],
    +}
    +

    before

    • 类型: (info: string) => string

    • 默认值:

    (info: string): string =>
    +  `<div class="custom-container ${type}">${info ? `<p class="custom-container-title">${info}</p>` : ''}\n`
    +
    • 详情:

      一个用于渲染容器起始标签的函数。

      第一个参数是 容器语法info 部分。

      如果你没有设置 after 配置项,则该配置项也不会生效。

    after

    • 类型: (info: string) => string

    • 默认值:

    (): string => '</div>\n'
    +
    • 详情:

      一个用于渲染容器结束标签的函数。

      第一个参数是 容器语法info 部分。

      如果你没有设置 before 配置项,则该配置项也不会生效。

    render

    • 类型:
    type MarkdownItContainerRenderFunction = (
    +  tokens: Token[],
    +  index: number,
    +  options: any,
    +  env: MarkdownEnv,
    +  self: Renderer,
    +) => string
    +

    validate

    marker

    + + + diff --git a/zh/plugins/copy-code.html b/zh/plugins/copy-code.html new file mode 100644 index 0000000000..a2d8ec1a36 --- /dev/null +++ b/zh/plugins/copy-code.html @@ -0,0 +1,97 @@ + + + + + + + + + copy-code | VuePress 生态系统 + + + + + +

    copy-code

    @vuepress/plugin-copy-code

    此插件会自动在 PC 设备上为每个代码块右上角添加复制按钮。

    默认选择器匹配 @vuepress/theme-default,所以在你自己的主题中集成时可能需要调整它。

    使用

    npm i -D @vuepress/plugin-copy-code@next
    +
    import { copyCodePlugin } from '@vuepress/plugin-copy-code'
    +
    +export default {
    +  plugins: [
    +    copyCodePlugin({
    +      // options
    +    }),
    +  ],
    +}
    +

    选项

    selector

    • 类型:string | string[]

    • 默认值:'.theme-default-content div[class*="language-"] pre'

    • 详情:

      代码块选择器

    showInMobile

    • 类型:boolean

    • 默认值:false

    • 详情:

      是否展示在移动端

    duration

    • 类型:number

    • 默认值:2000

    • 详情:

      提示消息显示时间,设置为 0 会禁用提示。

    delay

    • 类型:number

    • 默认值:800

    • 详情:

      注册复制按钮的延时,单位 ms。

      如果你使用的主题有切换动画,建议配置此选项为 切换动画时长 + 200

    locales

    • 类型:CopyCodePluginLocaleConfig

      interface CopyCodePluginLocaleData {
      +  /**
      +   * 复制文字
      +   */
      +  copy: string
      +
      +  /**
      +   * 已复制文字
      +   */
      +  copied: string
      +}
      +
      +interface CopyCodePluginLocaleConfig {
      +  [localePath: string]: CopyCodePluginLocaleData
      +}
      +
    • 必填:否

    • 详情:

      复制按钮插件的国际化配置。

    • 示例:

      import { copyCodePlugin } from '@vuepress/plugin-copy-code'
      +
      +export default {
      +  locales: {
      +    '/': {
      +      // 这是一个支持的语言
      +      lang: 'zh-CN',
      +    },
      +    '/xx/': {
      +      // 这是一个没有收到插件支持的语言
      +      lang: 'mm-NN',
      +    },
      +  },
      +
      +  plugins: [
      +    copyCodePlugin({
      +      locales: {
      +        '/': {
      +          // 覆盖复制按钮标签文字
      +          copy: '复制此段代码',
      +        },
      +
      +        '/xx/': {
      +          // 在这里完整设置 `mm-NN` 的多语言配置
      +        },
      +      },
      +    }),
      +  ],
      +}
      +
    内置支持语言
    • 简体中文 (zh-CN)
    • 繁体中文 (zh-TW)
    • 英文(美国) (en-US)
    • 德语 (de-DE)
    • 德语(澳大利亚) (de-AT)
    • 俄语 (ru-RU)
    • 乌克兰语 (uk-UA)
    • 越南语 (vi-VN)
    • 葡萄牙语(巴西) (pt-BR)
    • 波兰语 (pl-PL)
    • 法语 (fr-FR)
    • 西班牙语 (es-ES)
    • 斯洛伐克 (sk-SK)
    • 日语 (ja-JP)
    • 土耳其语 (tr-TR)
    • 韩语 (ko-KR)
    • 芬兰语 (fi-FI)
    • 印尼语 (id-ID)
    • 荷兰语 (nl-NL)

    样式

    你可以通过 CSS 变量来自定义复制按钮的样式:

    :root {
    +  --code-copy-icon: url("copy-button.svg");
    +  --code-copied-icon: url("copied-button.svg");
    +  --copy-code-color: #9e9e9e;
    +  --copy-code-hover: rgb(0 0 0 / 50%);
    +}
    +
    + + + diff --git a/zh/plugins/copyright.html b/zh/plugins/copyright.html new file mode 100644 index 0000000000..73d39317c6 --- /dev/null +++ b/zh/plugins/copyright.html @@ -0,0 +1,102 @@ + + + + + + + + + copyright | VuePress 生态系统 + + + + + +

    copyright

    @vuepress/plugin-copyright

    此插件可以在访问者从你的站点复制内容时,自动追加版权信息,也可以禁止站点的复制或者选择。

    使用

    npm i -D @vuepress/plugin-copyright@next
    +
    import { copyrightPlugin } from '@vuepress/plugin-copyright'
    +
    +export default {
    +  plugins: [
    +    copyrightPlugin({
    +      // options
    +    }),
    +  ],
    +}
    +

    启用版权信息

    此插件默认全局禁用。你可以:

    • 在特定页面的 frontmatter 中设置 copy: true 手动开启。
    • 在插件选项中设置 global: true 让其全局生效,并在页面的 frontmatter 中设置 copy: false 禁用它。

    处于不打扰用户的考虑,默认配置下仅当复制长度超过 100 时才会追加版权信息。如果你希望改变这个触发值,你可以插件选项中设置 triggerLength,或在页面 frontmatter 单独设置 copy.triggerLength

    你可以通过插件的 authorlicense 选项设置全局作者和协议信息。

    如果文档的不同部分拥有不同的作者和协议,你可以通过 authorGetterlicenseGetter 传入一个使用当前页面对象作为参数的函数 (page: Page) => string 并通过它返回相应信息。

    插件会默认通过模板从作者、协议和页面链接生成版权信息,并在复制时追加。如果你认为这不够灵活,你可以设置 copyrightGetter 返回一个完全由你自定义的版权信息,或返回 null 以使用默认模板。

    禁用复制和选择

    如果你希望禁止用户复制较长内容,你可以在插件选项中设置 maxLength 控制这个临界值,或在页面 frontmatter 单独设置 copy.maxLength

    • 如果你不希望用户复制你的整个站点或特定页面文字,你可以在插件选项中设置 disableCopy 或在页面 frontmatter 中设置 copy.disableCopy 来禁用复制,后者具有更高优先级。
    • 如果你不希望用户选择你的整个站点或特定页面文字,你可以在插件选项中设置 disableSelection 或在页面 frontmatter 中设置 copy.disableSelection 来禁用文字选择。此选项具有更高优先级

    选项

    author

    • 类型:string
    • 详情:默认作者信息

    license

    • 类型:string
    • 详情:默认协议信息

    authorGetter

    • 类型:(page: Page) => string | null
    • 详情:作者信息获取器

    licenseGetter

    • 类型:(page: Page) => string | null
    • 详情:协议信息获取器

    copyrightGetter

    • 类型:(page: Page) => string | null
    • 详情:协议信息获取器

    triggerLength

    • 类型:number
    • 默认值:100
    • 详情:触发附加版权的最小内容长度

    maxLength

    • 类型:number
    • 默认值:0
    • 详情:允许复制的最大内容长度,0 意味着无限制。

    global

    • 类型:boolean
    • 默认值:false
    • 详情:是否全局启用

    disableCopy

    • 类型:boolean
    • 默认值:false
    • 详情:禁用复制

    disableSelection

    • 类型:boolean
    • 默认值:false
    • 详情:禁用选择

    canonical

    • 类型:string

    • 详情:

      首选部署位置。

    例子

    如果你在 https://myblog.comhttps://blog.com/username/ 下部署相同的内容,你可能希望选择一个站点作为首选链接。

    • 如果你倾向于使用第一个,你应该将 canonical 设置为 https://myblog.com
    • 如果你倾向于使用第二个,你应该将 canonical 设置为 https://blog.com/username/

    这样,在另一个站点触发的版权信息也会指向你的首选站点。

    locales

    • 类型:CopyrightPluginLocaleConfig

      interface CopyrightPluginLocaleData {
      +  /**
      +   * 作者文字
      +   *
      +   * @description `:author` 将会被作者替换
      +   */
      +  author: string
      +
      +  /**
      +   * 协议文字
      +   *
      +   * @description `:license` 会被当前协议替换
      +   */
      +  license: string
      +
      +  /**
      +   * 链接文字
      +   *
      +   * @description `:link` 会替换为当前页面链接
      +   */
      +  link: string
      +}
      +
      +interface CopyrightPluginLocaleConfig {
      +  [localePath: string]: CopyrightPluginLocaleData
      +}
      +
    • 详情:版权插件的国际化配置。

    • 示例:

      import { copyrightPlugin } from '@vuepress/plugin-copyright'
      +
      +export default {
      +  locales: {
      +    '/': {
      +      // this is a supported language
      +      lang: 'en-US',
      +    },
      +    '/xx/': {
      +      // the plugin does not support this language
      +      lang: 'mm-NN',
      +    },
      +  },
      +
      +  plugins: [
      +    copyrightPlugin({
      +      locales: {
      +        '/': {
      +          // Override link text
      +          link: 'Original posted at :link',
      +        },
      +
      +        '/xx/': {
      +          // Complete locale config for `mm-NN` language here
      +        },
      +      },
      +    }),
      +  ],
      +}
      +
    内置支持语言
    • 简体中文 (zh-CN)
    • 繁体中文 (zh-TW)
    • 英文(美国) (en-US)
    • 德语 (de-DE)
    • 德语(澳大利亚) (de-AT)
    • 俄语 (ru-RU)
    • 乌克兰语 (uk-UA)
    • 越南语 (vi-VN)
    • 葡萄牙语(巴西) (pt-BR)
    • 波兰语 (pl-PL)
    • 法语 (fr-FR)
    • 西班牙语 (es-ES)
    • 斯洛伐克 (sk-SK)
    • 日语 (ja-JP)
    • 土耳其语 (tr-TR)
    • 韩语 (ko-KR)
    • 芬兰语 (fi-FI)
    • 印尼语 (id-ID)
    • 荷兰语 (nl-NL)

    Frontmatter

    copy.triggerLength

    • 类型:number
    • 默认值:100
    • 详情: 触发附加版权的最小内容长度

    copy.maxLength

    • 类型:number
    • 默认值:0
    • 详情: 允许复制的最大内容长度,0 意味着无限制。

    copy.disableCopy

    • 类型:boolean
    • 默认值:false
    • 详情:禁用复制

    copy.disableSelection

    • 类型:boolean
    • 默认值:false
    • 详情:禁用选择
    + + + diff --git a/zh/plugins/docsearch.html b/zh/plugins/docsearch.html new file mode 100644 index 0000000000..bbddeab988 --- /dev/null +++ b/zh/plugins/docsearch.html @@ -0,0 +1,239 @@ + + + + + + + + + docsearch | VuePress 生态系统 + + + + + +

    docsearch

    @vuepress/plugin-docsearch

    Algolia DocSearch在新窗口打开 集成到 VuePress 中,为你的文档网站提供搜索功能。

    提示

    当你正确配置该插件后,默认主题会把 DocSearch 按钮添加到导航栏。

    该插件不一定能在其他主题中直接使用,因此你应参考主题本身的文档来获取更多信息。

    使用方法

    npm i -D @vuepress/plugin-docsearch@next
    +
    import { docsearchPlugin } from '@vuepress/plugin-docsearch'
    +
    +export default {
    +  plugins: [
    +    docsearchPlugin({
    +      // 配置项
    +    }),
    +  ],
    +}
    +

    获取搜索索引

    你需要 提交你的网站 URL在新窗口打开 来加入 DocSearch 项目。当你的索引成功创建后, DocSearch 团队会将 apiKeyindexName 发送到你的邮箱。接下来,你就可以配置该插件,在 VuePress 中启用 DocSearch 了。

    或者,你也可以 运行你自己的爬虫在新窗口打开 来创建索引,然后使用你自己的 appId, apiKeyindexName 来配置该插件。

    官方爬虫配置示例
    new Crawler({
    +  appId: 'YOUR_APP_ID',
    +  apiKey: 'YOUR_API_KEY',
    +  rateLimit: 8,
    +  startUrls: [
    +    // 这是 Algolia 开始抓取网站的初始地址
    +    // 如果你的网站被分为数个独立部分,你可能需要在此设置多个入口链接
    +    'https://YOUR_WEBSITE_URL/',
    +  ],
    +  sitemaps: [
    +    // 如果你在使用 Sitemap 插件 (如: @vuepress-plugin/sitemap),你可以提供 Sitemap 链接
    +    'https://YOUR_WEBSITE_URL/sitemap.xml',
    +  ],
    +  ignoreCanonicalTo: false,
    +  exclusionPatterns: [
    +    // 你可以通过它阻止 Algolia 抓取某些 URL
    +  ],
    +  discoveryPatterns: [
    +    // 这是 Algolia 抓取 URL 的范围
    +    'https://YOUR_WEBSITE_URL/**',
    +  ],
    +  // 爬虫执行的计划时间,可根据文档更新频率设置
    +  schedule: 'at 02:00 every 1 day',
    +  actions: [
    +    // 你可以拥有多个 action,特别是你在一个域名下部署多个文档时
    +    {
    +      // 使用适当的名称为索引命名
    +      indexName: 'YOUR_INDEX_NAME',
    +      // 索引生效的路径
    +      pathsToMatch: ['https://YOUR_WEBSITE_URL/**'],
    +      // 控制 Algolia 如何抓取你的站点
    +      recordExtractor: ({ $, helpers }) => {
    +        // @vuepress/theme-default 的选项
    +        return helpers.docsearch({
    +          recordProps: {
    +            lvl0: {
    +              selectors: '.sidebar-heading.active',
    +              defaultValue: 'Documentation',
    +            },
    +            lvl1: '.theme-default-content h1',
    +            lvl2: '.theme-default-content h2',
    +            lvl3: '.theme-default-content h3',
    +            lvl4: '.theme-default-content h4',
    +            lvl5: '.theme-default-content h5',
    +            lvl6: '.theme-default-content h6',
    +            content: '.theme-default-content p, .theme-default-content li',
    +          },
    +          indexHeadings: true,
    +        })
    +      },
    +    },
    +  ],
    +  initialIndexSettings: {
    +    // 控制索引如何被初始化,这仅当索引尚未生成时有效
    +    // 你可能需要在修改后手动删除并重新生成新的索引
    +    YOUR_INDEX_NAME: {
    +      attributesForFaceting: ['type', 'lang'],
    +      attributesToRetrieve: ['hierarchy', 'content', 'anchor', 'url'],
    +      attributesToHighlight: ['hierarchy', 'hierarchy_camel', 'content'],
    +      attributesToSnippet: ['content:10'],
    +      camelCaseAttributes: ['hierarchy', 'hierarchy_radio', 'content'],
    +      searchableAttributes: [
    +        'unordered(hierarchy_radio_camel.lvl0)',
    +        'unordered(hierarchy_radio.lvl0)',
    +        'unordered(hierarchy_radio_camel.lvl1)',
    +        'unordered(hierarchy_radio.lvl1)',
    +        'unordered(hierarchy_radio_camel.lvl2)',
    +        'unordered(hierarchy_radio.lvl2)',
    +        'unordered(hierarchy_radio_camel.lvl3)',
    +        'unordered(hierarchy_radio.lvl3)',
    +        'unordered(hierarchy_radio_camel.lvl4)',
    +        'unordered(hierarchy_radio.lvl4)',
    +        'unordered(hierarchy_radio_camel.lvl5)',
    +        'unordered(hierarchy_radio.lvl5)',
    +        'unordered(hierarchy_radio_camel.lvl6)',
    +        'unordered(hierarchy_radio.lvl6)',
    +        'unordered(hierarchy_camel.lvl0)',
    +        'unordered(hierarchy.lvl0)',
    +        'unordered(hierarchy_camel.lvl1)',
    +        'unordered(hierarchy.lvl1)',
    +        'unordered(hierarchy_camel.lvl2)',
    +        'unordered(hierarchy.lvl2)',
    +        'unordered(hierarchy_camel.lvl3)',
    +        'unordered(hierarchy.lvl3)',
    +        'unordered(hierarchy_camel.lvl4)',
    +        'unordered(hierarchy.lvl4)',
    +        'unordered(hierarchy_camel.lvl5)',
    +        'unordered(hierarchy.lvl5)',
    +        'unordered(hierarchy_camel.lvl6)',
    +        'unordered(hierarchy.lvl6)',
    +        'content',
    +      ],
    +      distinct: true,
    +      attributeForDistinct: 'url',
    +      customRanking: [
    +        'desc(weight.pageRank)',
    +        'desc(weight.level)',
    +        'asc(weight.position)',
    +      ],
    +      ranking: [
    +        'words',
    +        'filters',
    +        'typo',
    +        'attribute',
    +        'proximity',
    +        'exact',
    +        'custom',
    +      ],
    +      highlightPreTag: '<span class="algolia-docsearch-suggestion--highlight">',
    +      highlightPostTag: '</span>',
    +      minWordSizefor1Typo: 3,
    +      minWordSizefor2Typos: 7,
    +      allowTyposOnNumericTokens: false,
    +      minProximity: 1,
    +      ignorePlurals: true,
    +      advancedSyntax: true,
    +      attributeCriteriaComputedByMinProximity: true,
    +      removeWordsIfNoResults: 'allOptional',
    +    },
    +  },
    +})
    +

































     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     







     
































































    上述 recordProps 是用于默认主题的配置,你可以根据你使用的主题来修改它们。

    注意 initialIndexSettings.YOUR_INDEX_NAME.attributesForFaceting 字段必须包含 'lang',否则该插件将无法正常工作。

    提示

    如果你使用的不是默认主题,或者在使用 Docsearch 的时候遇到了任何问题,你也可以检查上述的爬虫配置示例,然后前往 Algolia Crawler在新窗口打开 仓库,在你项目侧边栏中的 Editor 页面中修改你的配置。

    配置项

    apiKey

    indexName

    appId

    searchParameters

    placeholder

    disableUserPersonalization

    initialQuery

    translations

    locales

    • 类型: Record<string, DocsearchPluginOptions>

    • 详情:

      在不同 locales 下对该插件进行不同的配置。

      该插件的所有其他选项都可以在 locale 中进行配置。

    • 示例:

    export default {
    +  plugins: [
    +    docsearchPlugin({
    +      appId: '<APP_ID>',
    +      apiKey: '<API_KEY>',
    +      indexName: '<INDEX_NAME>',
    +      locales: {
    +        '/': {
    +          placeholder: 'Search Documentation',
    +          translations: {
    +            button: {
    +              buttonText: 'Search Documentation',
    +            },
    +          },
    +        },
    +        '/zh/': {
    +          placeholder: '搜索文档',
    +          translations: {
    +            button: {
    +              buttonText: '搜索文档',
    +            },
    +          },
    +        },
    +      },
    +    }),
    +  ],
    +}
    +

    indexBase

    • 类型: string

    • 默认值: base

    • 详情:

      搜索索引基础路径。

      如果你需要把你的站点部署到不同的域名上,你不需要把它们全都提交到 Docsearch 上来分别生成搜索索引。你可以选择其中一个域名作为 索引域名 ,并且仅将 索引域名 提交到 DocSearch 上来爬去搜索索引。然后,你就可以在不同的部署域名下复用索引。

      如果你不同部署域名下的 base 是不一样的,你就需要将这个配置设置成 索引域名base ,这样其他的部署域名就可以正确复用索引了。

    injectStyles

    • 类型: boolean

    • 默认值: true

    • 详情:

      是否注入 DocSearch 的默认样式。

      如果你认为 DocSearch 的默认样式和你的站点不兼容,你可以尝试覆盖默认样式,或者将该选项设置为 false 来完全移除默认样式。

      当该选项被禁用时,你需要为 DocSearch 引入你自己的样式。同时要注意,你也无法再使用 样式 章节中提到的样式自定义能力。

    样式

    你可以通过 @docsearch/css在新窗口打开 提供的 CSS 变量来自定义样式:

    :root {
    +  --docsearch-primary-color: rgb(84, 104, 255);
    +  --docsearch-text-color: rgb(28, 30, 33);
    +  --docsearch-spacing: 12px;
    +  --docsearch-icon-stroke-width: 1.4;
    +  --docsearch-highlight-color: var(--docsearch-primary-color);
    +  --docsearch-muted-color: rgb(150, 159, 175);
    +  --docsearch-container-background: rgba(101, 108, 133, 0.8);
    +  --docsearch-logo-color: rgba(84, 104, 255);
    +
    +  /* modal */
    +  --docsearch-modal-width: 560px;
    +  --docsearch-modal-height: 600px;
    +  --docsearch-modal-background: rgb(245, 246, 247);
    +  --docsearch-modal-shadow: inset 1px 1px 0 0 rgba(255, 255, 255, 0.5), 0 3px
    +      8px 0 rgba(85, 90, 100, 1);
    +
    +  /* searchbox */
    +  --docsearch-searchbox-height: 56px;
    +  --docsearch-searchbox-background: rgb(235, 237, 240);
    +  --docsearch-searchbox-focus-background: #fff;
    +  --docsearch-searchbox-shadow: inset 0 0 0 2px var(--docsearch-primary-color);
    +
    +  /* hit */
    +  --docsearch-hit-height: 56px;
    +  --docsearch-hit-color: rgb(68, 73, 80);
    +  --docsearch-hit-active-color: #fff;
    +  --docsearch-hit-background: #fff;
    +  --docsearch-hit-shadow: 0 1px 3px 0 rgb(212, 217, 225);
    +
    +  /* key */
    +  --docsearch-key-gradient: linear-gradient(
    +    -225deg,
    +    rgb(213, 219, 228) 0%,
    +    rgb(248, 248, 248) 100%
    +  );
    +  --docsearch-key-shadow: inset 0 -2px 0 0 rgb(205, 205, 230), inset 0 0 1px 1px
    +      #fff, 0 1px 2px 1px rgba(30, 35, 90, 0.4);
    +
    +  /* footer */
    +  --docsearch-footer-height: 44px;
    +  --docsearch-footer-background: #fff;
    +  --docsearch-footer-shadow: 0 -1px 0 0 rgb(224, 227, 232), 0 -3px 6px 0 rgba(69, 98, 155, 0.12);
    +}
    +

    组件

    Docsearch

    • 详情:

      该插件会全局注册一个 <Docsearch /> 组件,你可以不传入任何 Props 来使用它。

      将该组件放置在你想要显示 docsearch 按钮的地方。例如,默认主题将这个组件放在了导航栏的末尾。

    提示

    该组件主要用于主题开发。在大多数情况下你不需要直接使用该组件。

    + + + diff --git a/zh/plugins/external-link-icon.html b/zh/plugins/external-link-icon.html new file mode 100644 index 0000000000..8359e497e9 --- /dev/null +++ b/zh/plugins/external-link-icon.html @@ -0,0 +1,64 @@ + + + + + + + + + external-link-icon | VuePress 生态系统 + + + + + +

    external-link-icon

    @vuepress/plugin-external-link-icon

    该插件会为你 Markdown 内容中的外部链接添加一个图标,即 在新窗口打开

    该插件已经集成到默认主题中。

    使用方法

    npm i -D @vuepress/plugin-external-link-icon@next
    +
    import { externalLinkIconPlugin } from '@vuepress/plugin-external-link-icon'
    +
    +export default {
    +  plugins: [
    +    externalLinkIconPlugin({
    +      // 配置项
    +    }),
    +  ],
    +}
    +

    选项

    locales

    • 类型: Record<string, { openInNewWindow: string }>

    • 详情:

      外部链接图标在不同 locales 下的 A11y 文字。

      如果没有指定该配置项,它会降级使用默认文字。

    • 示例:

    export default {
    +  plugins: [
    +    externalLinkIconPlugin({
    +      locales: {
    +        '/': {
    +          openInNewWindow: 'open in new window',
    +        },
    +        '/zh/': {
    +          openInNewWindow: '在新窗口打开',
    +        },
    +      },
    +    }),
    +  ],
    +}
    +

    Frontmatter

    externalLinkIcon

    • 类型: boolean

    • 详情:

      是否在当前页面的外部链接的后面添加外部链接图标。

    样式

    你可以通过 CSS 变量来自定义外部链接图标的样式:

    :root {
    +  --external-link-icon-color: #aaa;
    +}
    +

    组件

    ExternalLinkIcon

    • 详情:

      该插件会全局注册一个 <ExternalLinkIcon /> 组件,你可以不传入任何 Props 来使用它。

    提示

    该组件主要用于主题开发。在大多数情况下你不需要直接使用该组件。

    + + + diff --git a/zh/plugins/feed/channel.html b/zh/plugins/feed/channel.html new file mode 100644 index 0000000000..3fdb0d339e --- /dev/null +++ b/zh/plugins/feed/channel.html @@ -0,0 +1,51 @@ + + + + + + + + + 频道设置 | VuePress 生态系统 + + + + + +

    频道设置

    channel 插件选项用于配置 feed 的频道。

    channel.title

    • 类型:string
    • 默认值:SiteConfig.title

    频道的标题

    • 类型:string
    • 默认值:部署的网址 (通过 options.hostnamecontext.base 生成)

    频道地址

    channel.description

    • 类型:string
    • 默认值:SiteConfig.description

    频道描述信息

    channel.language

    • 类型:string
    • 默认值:
      • siteConfig.locales['/'].locales
      • 如果上述未提供,回退到 "en-US"

    频道使用的语言

    • 类型:string
    • 默认值:
      • 尝试读取 channel 选项中的 author.name 生成 Copyright by $author
    • 建议自行设置:

    频道版权信息

    channel.pubDate

    • 类型:string (需是合法的 Date ISOString)
    • 默认值:每次插件构建时刻
    • 建议自行设置:

    频道内容的发布时间

    channel.lastUpdated

    • 类型:string (需是合法的 Date ISOString)
    • 默认值:每次插件构建时刻

    频道内容的上次更新时间

    channel.ttl

    • 类型:number
    • 建议自行设置:

    内容有效时间,即获取后保持缓存而不进行新获取的时间

    channel.image

    • 类型:string
    • 建议自行设置:

    这是一个会在频道中使用的图片,建议设置正方形图片、尺寸最好不小于 512×512。

    channel.icon

    • 类型:string
    • 建议自行设置:

    一个代表频道的图标,建议设置正方形图片、尺寸最好不小于 128×128,背景色透明。

    channel.author

    • 类型:FeedAuthor
    • 建议自行设置:

    频道的作者。

    FeedAuthor 格式
    interface FeedAuthor {
    +  /** 作者姓名 */
    +  name: string
    +  /** 作者电子邮箱 */
    +  email?: string
    +  /** 作者网站 */
    +  url?: string
    +  /**
    +   * 作者头像地址
    +   *
    +   * 正方形,最好不小于 128×128,透明背景
    +   */
    +  avatar?: string
    +}
    +

    channel.hub

    • 类型:string

    Websub 的链接。Websub 需要服务器后端,与 VuePress 主旨不符,如无特殊需要忽略即可。

    WebSub

    有关信息,详见 Websub在新窗口打开

    + + + diff --git a/zh/plugins/feed/config.html b/zh/plugins/feed/config.html new file mode 100644 index 0000000000..1df4a58bb8 --- /dev/null +++ b/zh/plugins/feed/config.html @@ -0,0 +1,54 @@ + + + + + + + + + 插件配置 | VuePress 生态系统 + + + + + +

    插件配置

    hostname

    • 类型:string
    • 必填:是

    部署网站的域名。

    atom

    • 类型:boolean
    • 默认值:false

    是否启用 Atom 格式输出。

    json

    • 类型:boolean
    • 默认值:false

    是否启用 JSON 格式输出。

    rss

    • 类型:boolean
    • 默认值:false

    是否启用 RSS 格式输出。

    image

    • 类型:string

    一个大的图片,用作 feed 展示。

    icon

    • 类型:string

    一个小的图标,显示在订阅列表中。

    count

    • 类型:number
    • 默认值:100

    设置 feed 的最大项目数量。在所有页面排序好后,插件会截取前 count 个项目。

    如果你的站点文章很多,你应该考虑设置这个选项以减少 feed 文件大小。

    preservedElements

    • 类型:(RegExp | string)[] | (tagName:string) => boolean

    应在 Feed 中保留的自定义元素或组件。

    默认情况下,所有未知标签均会被移除。

    filter

    • 类型:(page: Page)=> boolean

    • 默认值:

      ;({ frontmatter, filePathRelative }: Page): boolean =>
      +  !(
      +    frontmatter.home ||
      +    !filePathRelative ||
      +    frontmatter.article === false ||
      +    frontmatter.feed === false
      +  )
      +

    自定义的过滤函数,用于过滤哪些项目在 feed 中显示。

    sorter

    • 类型: (pageA: Page, pageB: Page)=> number

    • 默认值:

      // dateSorter 来源于 @vuepress/helper
      +;(pageA, pageB): number =>
      +  dateSorter(
      +    pageA.data.git?.createdTime
      +      ? new Date(pageA.data.git?.createdTime)
      +      : pageA.frontmatter.date,
      +    pageB.data.git?.createdTime
      +      ? new Date(pageB.data.git?.createdTime)
      +      : pageB.frontmatter.date,
      +  )
      +

    Feed 项目的排序器。

    默认的排序行为是通过 Git 的文件添加日期 (需要 @vuepress/plugin-git)。

    提示

    你应该启用 @vuepress/plugin-git 来获取最新创建的页面作为 feed 项目。否则,feed 项目将按照 VuePress 中页面的默认顺序排序。

    channel

    channel 选项用于配置 Feed 频道。

    可用选项详见 配置 → 频道设置

    devServer

    • 类型:boolean
    • 默认值:false

    是否在开发服务器中启用

    提示

    由于性能原因,我们不提供热更新。重启开发服务器以同步你的变更。

    devHostname

    • 类型:string
    • 默认值:"http://localhost:${port}"

    开发服务器使用的主机名

    atomOutputFilename

    • 类型:string
    • 默认值:"atom.xml"

    Atom 格式输出路径,相对于输出路径。

    atomXslTemplate

    • 类型:string
    • 默认值:@vuepress/plugin-feed/templates/atom.xsl 的内容

    Atom xsl 模板文件没人陪美国

    atomXslFilename

    • 类型:string
    • 默认值:"atom.xsl"

    Atom xsl 输出路径,相对于输出路径。

    jsonOutputFilename

    • 类型:string
    • 默认值:"feed.json"

    JSON 格式输出路径,相对于输出路径。

    rssOutputFilename

    • 类型:string
    • 默认值:"rss.xml"

    RSS 格式输出路径,相对于输出路径。

    rssXslTemplate

    • 类型:string
    • 默认值:@vuepress/plugin-feed/templates/rss.xsl 的内容

    RSS xsl 模板文件内容。

    rssXslFilename

    • 类型:string
    • 默认值:"rss.xsl"

    RSS xsl 输出路径,相对于输出路径。

    getter

    Feed 生成控制器,详见 Feed 生成器

    此插件内置了生成器,只有当你想完全控制 feed 生成时才需要设置此选项。

    locales

    • 类型:Record<string, BaseFeedOptions>

    你可以将它用于每个语言环境的特定选项。

    hostname 外,上述任何选项均受支持。

    + + + diff --git a/zh/plugins/feed/frontmatter.html b/zh/plugins/feed/frontmatter.html new file mode 100644 index 0000000000..4243d186b5 --- /dev/null +++ b/zh/plugins/feed/frontmatter.html @@ -0,0 +1,87 @@ + + + + + + + + + Frontmatter 配置 | VuePress 生态系统 + + + + + +

    Frontmatter 配置

    你可以通过配置每个页面的 Frontmatter,来对每个 Feed 项目生成进行单独的控制。

    添加与移除

    默认情况下,所有文章均会被添加至 feed 流。如果你想在 feed 中移除特定页面,你可以在 frontmatter 中设置 feed: false

    读取的 Frontmatter 信息

    title

    • 类型:string

    由 VuePress 自动生成,默认为页面的 h1 内容

    description

    • 类型:string

    页面描述

    date

    • 类型:Date

    页面的发布日期

    article

    • 类型:boolean

    该页面是否是文章

    如果此项设置为 false,则该页不会包含在最终的 feed 中。

    • 类型:string

    页面版权信息

    cover / image / banner

    • 类型:string

    页面的封面/分享图,需为完整链接或绝对链接。

    Frontmatter 选项

    feed.title

    • 类型:string

    Feed 项目的标题

    feed.description

    • 类型:string

    Feed 项目的描述

    feed.content

    • 类型:string

    Feed 项目的内容

    feed.author

    • 类型:FeedAuthor[] | FeedAuthor

    Feed 项目的作者

    FeedAuthor 格式
    interface FeedAuthor {
    +  /**
    +   * 作者名字
    +   */
    +  name?: string
    +
    +  /**
    +   * 作者邮件
    +   */
    +  email?: string
    +
    +  /**
    +   * 作者网站
    +   *
    +   * @description json format only
    +   */
    +  url?: string
    +
    +  /**
    +   * 作者头像
    +   *
    +   * @description json format only
    +   */
    +  avatar?: string
    +}
    +

    feed.contributor

    • 类型:FeedContributor[] | FeedContributor

    Feed 项目的贡献者

    FeedContributor 格式
    interface FeedContributor {
    +  /**
    +   * 作者名字
    +   */
    +  name?: string
    +
    +  /**
    +   * 作者邮件
    +   */
    +  email?: string
    +
    +  /**
    +   * 作者网站
    +   *
    +   * @description json format only
    +   */
    +  url?: string
    +
    +  /**
    +   * 作者头像
    +   *
    +   * @description json format only
    +   */
    +  avatar?: string
    +}
    +

    feed.guid

    • 类型:string

    Feed 项目的标识符,用于标识 Feed 项目。

    你应该确保每个 Feed 项目有全局唯一的 guid。

    + + + diff --git a/zh/plugins/feed/getter.html b/zh/plugins/feed/getter.html new file mode 100644 index 0000000000..612a5a3c10 --- /dev/null +++ b/zh/plugins/feed/getter.html @@ -0,0 +1,127 @@ + + + + + + + + + Feed 获取器 | VuePress 生态系统 + + + + + +

    Feed 获取器

    你可以通过控制插件选项中的 getter 来完全控制 Feed 项目的生成。

    getter.title

    • 类型:(page: Page) => string

    项目标题获取器

    • 类型:(page: Page) => string

    项目链接获取器

    getter.description

    • 类型:(page: Page) => string | undefined

    项目描述获取器

    提示

    因为 Atom 在摘要中支持 HTML,所以如果可能的话,你可以在这里返回 HTML 内容,但内容必须以标记 html: 开头。

    getter.content

    • 类型:(page: Page) => string

    项目内容获取器

    getter.author

    • 类型:(page: Page) => FeedAuthor[]

    项目作者获取器。

    获取器应在作者信息缺失时返回空数组。

    FeedAuthor 格式
    interface FeedAuthor {
    +  /**
    +   * 作者名字
    +   */
    +  name?: string
    +
    +  /**
    +   * 作者邮件
    +   */
    +  email?: string
    +
    +  /**
    +   * 作者网站
    +   *
    +   * @description json format only
    +   */
    +  url?: string
    +
    +  /**
    +   * 作者头像
    +   *
    +   * @description json format only
    +   */
    +  avatar?: string
    +}
    +

    getter.category

    • 类型:(page: Page) => FeedCategory[] | undefined

    项目分类获取器。

    FeedCategory 格式
    interface FeedCategory {
    +  /**
    +   * 分类名称
    +   */
    +  name: string
    +
    +  /**
    +   * 标识分类法的字符串
    +   *
    +   * @description rss format only
    +   */
    +  domain?: string
    +
    +  /**
    +   * URI 标识的分类 scheme
    +   *
    +   * @description atom format only
    +   */
    +  scheme?: string
    +}
    +

    getter.enclosure

    • 类型:(page: Page) => FeedEnclosure | undefined

    项目附件获取器。

    FeedEnclosure 格式
    interface FeedEnclosure {
    +  /**
    +   * Enclosure 地址
    +   */
    +  url: string
    +
    +  /**
    +   * 类型
    +   *
    +   * @description 应为一个标准的 MIME 类型,rss format only
    +   */
    +  type: string
    +
    +  /**
    +   * 按照字节数计算的大小
    +   *
    +   * @description rss format only
    +   */
    +  length?: number
    +}
    +

    getter.publishDate

    • 类型:(page: Page) => Date | undefined

    项目发布日期获取器

    getter.lastUpdateDate

    • 类型:(page: Page) => Date

    项目最后更新日期获取器

    getter.image

    • 类型:(page: Page) => string

    项目图片获取器

    确保返回一个完整的 URL。

    getter.contributor

    • 类型:(page: Page) => FeedContributor[]

    项目贡献者获取器

    获取器应在贡献者信息缺失时返回空数组。

    FeedContributor 格式
    interface FeedContributor {
    +  /**
    +   * 作者名字
    +   */
    +  name?: string
    +
    +  /**
    +   * 作者邮件
    +   */
    +  email?: string
    +
    +  /**
    +   * 作者网站
    +   *
    +   * @description json format only
    +   */
    +  url?: string
    +
    +  /**
    +   * 作者头像
    +   *
    +   * @description json format only
    +   */
    +  avatar?: string
    +}
    +
    • 类型:(page: Page) => string | undefined

    项目版权获取器

    + + + diff --git a/zh/plugins/feed/guide.html b/zh/plugins/feed/guide.html new file mode 100644 index 0000000000..ea1131850c --- /dev/null +++ b/zh/plugins/feed/guide.html @@ -0,0 +1,37 @@ + + + + + + + + + 指南 | VuePress 生态系统 + + + + + +

    指南

    使用

    插件可为你生成以下三种格式的 feed 文件:

    • Atom 1.0
    • JSON 1.1
    • RSS 2.0

    请按照需要生成的格式,在插件选项中设置 atom, jsonrsstrue

    为了正确生成 Feed 链接,你需要在插件选项中设置 hostname

    可读的预览

    当你在浏览器中打开 Feed 文件时,我们会通过 xsl 模板将 atom 和 rss feed xml 魔法般地转换为可读的 html。你可以查看本站的 atom在新窗口打开rss在新窗口打开 feed 作为案例!

    如果你想在开发服务器中预览 Feed,你需要在插件选项中设置 devServer: true。如果你没有使用默认的 http://localhost:{port},你还需要设置 devHostname

    频道设置

    你可以通过设置 channel 选项来自自定义 Feed 频道的各项信息。

    我们推荐进行如下设置:

    • 将建立 Feed 的日期转换为 ISOString 写入到 channel.pubDate
    • 通过 channel.ttl 中设置内容的更新周期(单位: 分钟)
    • 通过 channel.copyright 设置版权信息
    • 通过 channel.author 设置频道作者。

    详细的选项及其默认值详见 配置 → 频道设置

    Feed 生成

    默认情况下,所有文章均会被添加至 feed 流。

    你可以在 frontmatter 中配置 feed 和其他选项控制每个页面的 Feed 项目内容,详见 Frontmatter 选项 了解它们如何被转换。

    你可以通过配置插件选项中的 getter 完全控制 Feed 项目的生成逻辑。 详细的选项及其默认值详见 配置 → Feed 获取器

    多语言配置

    插件会针对每个语言生成单独的 Feed。

    你可以通过插件选项中的 locales 分别对不同语言提供不同的默认设置。

    + + + diff --git a/zh/plugins/feed/index.html b/zh/plugins/feed/index.html new file mode 100644 index 0000000000..91b61cde62 --- /dev/null +++ b/zh/plugins/feed/index.html @@ -0,0 +1,47 @@ + + + + + + + + + feed | VuePress 生态系统 + + + + + +

    feed

    @vuepress/plugin-feed

    使用

    npm i -D @vuepress/plugin-feed@next
    +
    import { feedPlugin } from '@vuepress/plugin-feed'
    +
    +export default {
    +  plugins: [
    +    feedPlugin({
    +      // 选项
    +    }),
    +  ],
    +}
    +
    + + + diff --git a/zh/plugins/git.html b/zh/plugins/git.html new file mode 100644 index 0000000000..745008851f --- /dev/null +++ b/zh/plugins/git.html @@ -0,0 +1,66 @@ + + + + + + + + + git | VuePress 生态系统 + + + + + +

    git

    @vuepress/plugin-git

    该插件会收集你的页面的 Git 信息,包括创建和更新时间、贡献者等。

    默认主题的 lastUpdatedcontributors 就是由该插件支持的。

    该插件主要用于开发主题,大部分情况下你不需要直接使用它。

    使用方法

    npm i -D @vuepress/plugin-git@next
    +
    import { gitPlugin } from '@vuepress/plugin-git'
    +
    +export default {
    +  plugins: [
    +    gitPlugin({
    +      // 配置项
    +    }),
    +  ],
    +}
    +

    Git 仓库

    该插件要求你的项目在 Git 仓库在新窗口打开 下,这样它才能从提交历史记录中收集信息。

    在构建站点时,你应该确保所有的提交记录是可以获取到的。举例来说, CI 工作流通常会在克隆你的仓库时添加 --depth 1在新窗口打开 参数来避免拉取全部的提交记录,因此你需要禁用这个功能,以便该插件在 CI 可以中正常使用。

    注意

    该插件会显著降低准备数据的速度,特别是在你的页面数量很多的时候。你可以考虑在 dev 模式下禁用该插件来获取更好的开发体验。

    配置项

    createdTime

    • 类型: boolean

    • 默认值: true

    • 详情:

      是否收集页面的创建时间。

    updatedTime

    • 类型: boolean

    • 默认值: true

    • 详情:

      是否收集页面的更新时间。

    contributors

    • 类型: boolean

    • 默认值: true

    • 详情:

      是否收集页面的贡献者。

    Frontmatter

    gitInclude

    • 类型: string[]

    • 详情:

      文件相对路径组成的数组,该数组中的文件会在计算页面数据时被包含在内。

    • 示例:

    ---
    +gitInclude:
    +  - relative/path/to/file1
    +  - relative/path/to/file2
    +---
    +

    页面数据

    该插件会向页面数据中添加一个 git 字段。

    在使用该插件后,可以在页面数据中获取该插件收集到的 Git 信息:

    import type { GitPluginPageData } from '@vuepress/plugin-git'
    +import { usePageData } from 'vuepress/client'
    +
    +export default {
    +  setup() {
    +    const page = usePageData<GitPluginPageData>()
    +    console.log(page.value.git)
    +  },
    +}
    +

    git.createdTime

    • 类型: number

    • 详情:

      页面第一次提交的 Unix 毫秒时间戳。

      该属性将取当前页面及 gitInclude 中所列文件的第一次提交的时间戳的最小值。

    git.updatedTime

    • 类型: number

    • 详情:

      页面最后一次提交的 Unix 毫秒时间戳。

      该属性将取当前页面及 gitInclude 中所列文件的最后一次提交的时间戳的最大值。

    git.contributors

    • 类型: GitContributor[]
    interface GitContributor {
    +  name: string
    +  email: string
    +  commits: number
    +}
    +
    • 详情:

      页面的贡献者信息。

      该属性将会包含 gitInclude 所列文件的贡献者。

    + + + diff --git a/zh/plugins/google-analytics.html b/zh/plugins/google-analytics.html new file mode 100644 index 0000000000..5d7228accc --- /dev/null +++ b/zh/plugins/google-analytics.html @@ -0,0 +1,62 @@ + + + + + + + + + google-analytics | VuePress 生态系统 + + + + + +

    google-analytics

    @vuepress/plugin-google-analytics

    Google Analytics在新窗口打开 集成到 VuePress 中。

    该插件会通过引入 gtag.js在新窗口打开 来启用 Google Analytics 4在新窗口打开

    使用方法

    npm i -D @vuepress/plugin-google-analytics@next
    +
    import { googleAnalyticsPlugin } from '@vuepress/plugin-google-analytics'
    +
    +export default {
    +  plugins: [
    +    googleAnalyticsPlugin({
    +      // 配置项
    +    }),
    +  ],
    +}
    +

    上报事件

    Google Analytics 会 自动收集部分事件在新窗口打开 ,比如 page_view, first_visit 等。

    因此,如果你只是想收集站点的一些基础数据,你只需要正确设置 Measurement ID ,不需要再额外做其他事情。

    在引入该插件之后,一个全局的 gtag() 函数会被挂载到 window 对象上,你可以使用它进行 自定义事件的上报在新窗口打开

    选项

    id

    • 类型: string

    • 详情:

      Google Analytics 4 的 Measurement ID ,应以 'G-' 开头。

      你可以通过 这里在新窗口打开 的指引来找到你的 Measurement ID 。注意区分 Google Analytics 4 的 Measurement ID (即 "G-" 开头的 ID) 和 Universal Analytics 的 Tracking ID (即 "UA-" 开头的 ID)。

    • 示例:

    export default {
    +  plugins: [
    +    googleAnalyticsPlugin({
    +      id: 'G-XXXXXXXXXX',
    +    }),
    +  ],
    +}
    +

    debug

    export default {
    +  plugins: [
    +    googleAnalyticsPlugin({
    +      id: 'G-XXXXXXXXXX',
    +      debug: true,
    +    }),
    +  ],
    +}
    +
    + + + diff --git a/zh/plugins/index.html b/zh/plugins/index.html new file mode 100644 index 0000000000..973a45b287 --- /dev/null +++ b/zh/plugins/index.html @@ -0,0 +1,37 @@ + + + + + + + + + 插件 | VuePress 生态系统 + + + + + +
    + + + diff --git a/zh/plugins/medium-zoom.html b/zh/plugins/medium-zoom.html new file mode 100644 index 0000000000..601e88512f --- /dev/null +++ b/zh/plugins/medium-zoom.html @@ -0,0 +1,67 @@ + + + + + + + + + medium-zoom | VuePress 生态系统 + + + + + +

    medium-zoom

    @vuepress/plugin-medium-zoom

    medium-zoom在新窗口打开 集成到 VuePress 中,为图片提供可缩放的功能。

    该插件已经集成到默认主题中。

    使用方法

    npm i -D @vuepress/plugin-medium-zoom@next
    +
    import { mediumZoomPlugin } from '@vuepress/plugin-medium-zoom'
    +
    +export default {
    +  plugins: [
    +    mediumZoomPlugin({
    +      // 配置项
    +    }),
    +  ],
    +}
    +

    配置项

    selector

    • 类型: string

    • 默认值: ':not(a) > img'

    • 详情:

      可缩放的图片的选择器。

      默认情况下,该插件会使 <a> 标签以外的所有图片都支持缩放。

    delay

    • 类型: number

    • 默认值: 500

    • 详情:

      以毫秒为单位的延迟。

      在切换路由进入一个新页面时,该插件会在一定延迟后才使页面内的图片支持缩放。

    zoomOptions

    样式

    你可以通过 zoomOptions 对大部分的缩放样式进行自定义,不过作为补充,该插件同样提供了一些 CSS 变量:

    :root {
    +  --medium-zoom-z-index: 100;
    +  --medium-zoom-bg-color: #ffffff;
    +  --medium-zoom-opacity: 1;
    +}
    +

    Composition API

    useMediumZoom

    • 详情:

      返回该插件使用的 Zoom 实例,便于你直接使用实例上的 methods在新窗口打开

      该插件会在切换路由进入当前页面时使图片支持缩放。但如果你要动态添加新图片,那么你可能就需要这个方法来让这些新图片也支持缩放。

      该插件在 Zoom 实例上额外添加了一个 refresh 方法,它将使用 selector 作为默认参数,先调用 zoom.detach() 再调用 zoom.attach() ,便于你快速刷新当前页面图片的缩放状态。

    • 示例:

    import { nextTick } from 'vue'
    +import { useMediumZoom } from '@vuepress/plugin-medium-zoom/client'
    +
    +export default {
    +  setup() {
    +    const zoom = useMediumZoom()
    +
    +    // ... 进行了一些操作,在当前页面添加了新的图片
    +
    +    // 此时你可能需要手动调用 `refresh` 来让这些新图片支持缩放
    +    nextTick(() => {
    +      zoom.refresh()
    +    })
    +  },
    +}
    +
    + + + diff --git a/zh/plugins/nprogress.html b/zh/plugins/nprogress.html new file mode 100644 index 0000000000..04e95e2aac --- /dev/null +++ b/zh/plugins/nprogress.html @@ -0,0 +1,47 @@ + + + + + + + + + nprogress | VuePress 生态系统 + + + + + +

    nprogress 插件

    @vuepress/plugin-nprogress

    nprogress在新窗口打开 集成到 VuePress 中,在切换到另一个页面时会展示进度条。

    该插件已经集成到默认主题中。

    使用方法

    npm i -D @vuepress/plugin-nprogress@next
    +
    import { nprogressPlugin } from '@vuepress/plugin-nprogress'
    +
    +export default {
    +  plugins: [nprogressPlugin()],
    +}
    +

    样式

    你可以通过 CSS 变量来自定义进度条的样式:

    :root {
    +  --nprogress-color: #29d;
    +  --nprogress-z-index: 1031;
    +}
    +
    + + + diff --git a/zh/plugins/palette.html b/zh/plugins/palette.html new file mode 100644 index 0000000000..426e0cc756 --- /dev/null +++ b/zh/plugins/palette.html @@ -0,0 +1,75 @@ + + + + + + + + + palette | VuePress 生态系统 + + + + + +

    palette

    @vuepress/plugin-palette

    为你的主题提供调色板功能。

    该插件主要用于开发主题,并且已经集成到默认主题中。大部分情况下你不需要直接使用它。

    对于主题作者,该插件可以帮助你提供用户自定义样式的能力。

    使用方法

    npm i -D @vuepress/plugin-palette@next
    +
    import { palettePlugin } from '@vuepress/plugin-palette'
    +
    +export default {
    +  plugins: [
    +    palettePlugin({
    +      // 配置项
    +    }),
    +  ],
    +}
    +

    调色板和样式

    该插件会提供一个 @vuepress/plugin-palette/palette (调色板文件)和一个 @vuepress/plugin-palette/style (样式文件),用于在你的主题样式中引入。

    调色板文件用于定义样式变量,因此它一般会在你主题样式的开头引入。举例来说,用户可以在调色板中定义 CSS 变量在新窗口打开SASS 变量在新窗口打开LESS 变量在新窗口打开Stylus 变量在新窗口打开 ,然后你可以在你的主题样式中使用这些变量。

    样式文件用于覆盖默认样式或添加额外样式,因此它一般会在你主题样式的末尾引入。

    使用

    在你的主题中使用该插件,假设你使用 SASS 作为 CSS 预处理器:

    export default {
    +  // ...
    +  plugins: [palettePlugin({ preset: 'sass' })],
    +}
    +

    使用调色板

    在你主题需要使用对应变量的地方引入该插件的调色板文件,比如在 Layout.vue 中:

    <template>
    +  <h1 class="palette-title">你好,调色板!</h1>
    +</template>
    +
    +<style lang="scss">
    +/* 从该插件的调色板中引入变量 */
    +@import '@vuepress/plugin-palette/palette';
    +
    +/* 设置变量的默认值 */
    +$color: red !default;
    +
    +/* 在你的样式中使用变量 */
    +.palette-title {
    +  color: $color;
    +}
    +</style>
    +

    然后,用户就可以在 .vuepress/styles/palette.scss 中自定义变量:

    $color: green;
    +

    使用样式

    在你主题的样式之后引入该插件的样式文件,比如在 clientConfigFile 中:

    // 引入你主题本身的样式文件
    +import 'path/to/your/theme/style'
    +// 引入该插件的样式文件
    +import '@vuepress/plugin-palette/style'
    +

    然后,用户就可以在 .vuepress/styles/index.scss 中添加额外样式,并可以覆盖你主题本身的样式:

    h1 {
    +  font-size: 2.5rem;
    +}
    +

    配置项

    preset

    • 类型: 'css' | 'sass' | 'less' | 'stylus'

    • 默认值: 'css'

    • 详情:

      设置其他选项的预设。

      如果你没有对该插件进行进阶定制化的需要,建议只设置该配置项并忽略其他选项。

    userPaletteFile

    • 类型: string

    • 默认值:

      • css: '.vuepress/styles/palette.css'
      • sass: '.vuepress/styles/palette.scss'
      • less: '.vuepress/styles/palette.less'
      • stylus: '.vuepress/styles/palette.styl'
    • 详情:

      用户调色板文件的路径,是针对源文件目录的相对路径。

      默认值依赖于 preset 配置项。

      该文件用于用户定义样式变量,建议保持默认值作为约定的文件路径。

    tempPaletteFile

    • 类型: string

    • 默认值:

      • css: 'styles/palette.css'
      • sass: 'styles/palette.scss'
      • less: 'styles/palette.less'
      • stylus: 'styles/palette.styl'
    • 详情:

      生成的调色板临时文件的路径,是针对临时文件文件目录的相对路径。

      默认值依赖于 preset 配置项。

      你应该使用 '@vuepress/plugin-palette/palette' 别名来引入调色板文件,因此在绝大多数情况下你不需要修改该配置项。

    userStyleFile

    • 类型: string

    • 默认值:

      • css: '.vuepress/styles/index.css'
      • sass: '.vuepress/styles/index.scss'
      • less: '.vuepress/styles/index.less'
      • stylus: '.vuepress/styles/index.styl'
    • 详情:

      用户样式文件的路径,是针对源文件目录的相对路径。

      默认值依赖于 preset 配置项。

      该文件用于用户覆盖默认样式和添加额外样式,建议保持默认值作为约定的文件路径。

    tempStyleFile

    • 类型: string

    • 默认值:

      • css: 'styles/index.css'
      • sass: 'styles/index.scss'
      • less: 'styles/index.less'
      • stylus: 'styles/index.styl'
    • 详情:

      生成的样式临时文件的路径,是针对临时文件文件目录的相对路径。

      默认值依赖于 preset 配置项。

      你应该使用 '@vuepress/plugin-palette/style' 别名来引入样式文件,因此在绝大多数情况下你不需要修改该配置项。

    importCode

    • 类型: (filePath: string) => string

    • 默认值:

      • css: (filePath) => `@import '${filePath}';\n`
      • sass: (filePath) => `@forward 'file:///${filePath}';\n`
      • less: (filePath) => `@import '${filePath}';\n`
      • stylus: (filePath) => `@require '${filePath}';\n`
    • 详情:

      用于生成引入代码的函数。

      默认值依赖于 preset 配置项。

      该配置项用于生成 tempPaletteFiletempStyleFile ,在绝大多数情况下你不需要修改该配置项。

    + + + diff --git a/zh/plugins/photo-swipe.html b/zh/plugins/photo-swipe.html new file mode 100644 index 0000000000..3983c64c16 --- /dev/null +++ b/zh/plugins/photo-swipe.html @@ -0,0 +1,181 @@ + + + + + + + + + photo-swipe | VuePress 生态系统 + + + + + +

    photo-swipe

    @vuepress/plugin-photo-swipe

    此插件会使页面正文内的图片在点击时进入浏览模式浏览。

    使用方法

    npm i -D @vuepress/plugin-photo-swipe@next
    +
    import { photoSwipePlugin } from '@vuepress/plugin-photo-swipe'
    +
    +export default {
    +  plugins: [
    +    photoSwipePlugin({
    +      // 选项
    +    }),
    +  ],
    +}
    +

    在图片预览模式中,你可以:

    • 左右滑动按顺序浏览页面内其他的图片
    • 查看图片的描述
    • 对图片进行缩放
    • 全屏浏览图片
    • 下载图片
    • 分享图片

    提示

    • 除了点击右上角的 "×" 退出浏览模式外,在上下滚动超过一定距离后,会自动退出图片浏览模式。
    • 在移动端,或使用 PC 触控板,你可以使用平移、缩放手势在浏览模式中平移、缩放图片。

    选项

    selector

    • 类型:string | string[]
    • 默认值:".theme-default-content :not(a) > img:not([no-view])"
    • 详情:图片选择器

    scrollToClose

    • 类型:boolean
    • 默认值:true
    • 详情:是否在滚动时关闭当前图片。

    delay

    • 类型:number

    • 默认值:800

    • 详情:

      操作页面 DOM 的延时,单位 ms。

      提示

      如果你使用的主题有切换动画,建议配置此选项为 切换动画时长 + 200

    locales

    • 类型:PhotoSwipePluginLocaleConfig

      interface PhotoSwipePluginLocaleData {
      +  /**
      +   * 关闭按钮标签文字
      +   */
      +  close: string
      +
      +  /**
      +   * 全屏按钮标签文字
      +   */
      +  fullscreen: string
      +
      +  /**
      +   * 分享按钮标签文字
      +   */
      +  share: string
      +
      +  /**
      +   * 缩放按钮标签文字
      +   */
      +  zoom: string
      +
      +  /**
      +   * 上一张图片按钮标签文字
      +   */
      +  prev: string
      +
      +  /**
      +   * 下一张图片按钮标签文字
      +   */
      +  next: string
      +
      +  /**
      +   * 功能按钮配置
      +   */
      +  buttons: PhotoSwipeDefaultUI.ShareButtonData[]
      +}
      +
      +interface PhotoSwipePluginLocaleConfig {
      +  [localePath: string]: PhotoSwipePluginLocaleData
      +}
      +
    • 详情:Photo Swipe 插件的国际化配置。

    • 示例:

      import { defineUserConfig } from 'vuepress'
      +import { photoSwipePlugin } from '@vuepress/plugin-photo-swipe'
      +
      +export default defineUserConfig({
      +  locales: {
      +    '/': {
      +      // 这是一个支持的语言
      +      lang: 'zh-CN',
      +    },
      +    '/xx/': {
      +      // 这是一个没有收到插件支持的语言
      +      lang: 'mm-NN',
      +    },
      +  },
      +
      +  plugins: [
      +    photoSwipePlugin({
      +      locales: {
      +        '/': {
      +          // 覆盖分享标签文字
      +          share: '分享给伙伴',
      +        },
      +
      +        '/xx/': {
      +          // 在这里完整设置 `mm-NN` 的多语言配置
      +        },
      +      },
      +    }),
      +  ],
      +})
      +
    内置支持语言
    • 简体中文 (zh-CN)
    • 繁体中文 (zh-TW)
    • 英文(美国) (en-US)
    • 德语 (de-DE)
    • 德语(澳大利亚) (de-AT)
    • 俄语 (ru-RU)
    • 乌克兰语 (uk-UA)
    • 越南语 (vi-VN)
    • 葡萄牙语(巴西) (pt-BR)
    • 波兰语 (pl-PL)
    • 法语 (fr-FR)
    • 西班牙语 (es-ES)
    • 斯洛伐克 (sk-SK)
    • 日语 (ja-JP)
    • 土耳其语 (tr-TR)
    • 韩语 (ko-KR)
    • 芬兰语 (fi-FI)
    • 印尼语 (id-ID)
    • 荷兰语 (nl-NL)

    Frontmatter

    photoswipe

    • 类型: string | false

    • 详情:

      当前页面的图片选择器或 false 以在当前页面中禁用 photo-swipe。

    客户端配置

    definePhotoSwipeConfig

    传递给 photo-swipe在新窗口打开 的额外选项。

    import { definePhotoSwipeConfig } from '@vuepress/plugin-photo-swipe/client'
    +
    +definePhotoSwipeConfig({
    +  // 在此设置 photoswipe 选项
    +})
    +
    +export default {}
    +

    API

    你可以通过 API 来调用 photoswipe。

    createPhotoSwipe 允许你以编程的方式查看图片链接:

    <script setup lang="ts">
    +import { onMounted, onUnmounted } from 'vue';
    +import { createPhotoSwipe } from "@vuepress/plugin-photo-swipe/client";
    +
    +let state: PhotoSwipeState | null = null;
    +
    +const openPhotoSwipe = (index: number) => {
    +  state?.open(index - 1);
    +};
    +
    +onMounted(async () => {
    +  // 通过图片链接创建一个新的 photoswipe 实例
    +  state=  await createPhotoSwipe(
    +    [
    +      'https://exmaple.com/image1.png'
    +      'https://exmaple.com/image2.png'
    +      'https://exmaple.com/image3.png'
    +    ],
    +    {
    +      // photoswipe 选项
    +    }
    +  );
    +});
    +
    +onUnmounted(() => {
    +  state?.destroy()
    +})
    +</script>
    +
    +<template>
    +  <button v-for="i in 3" @click="openPhotoSwipe(i)">open photo {{ i }}</button>
    +</template>
    +

    registerPhotoSwipe 允许你为给定的图片元素注册 photoswipe:

    <script setup lang="ts">
    +import { onMounted, onUnmounted } from 'vue'
    +import { registerPhotoSwipe } from '@vuepress/plugin-photo-swipe/client'
    +
    +let destroy: () => void | null = null
    +
    +onMounted(async () => {
    +  await nextTick()
    +
    +  const images = Array.from(document.querySelectorAll('img'))
    +
    +  // 通过图片元素创建一个新的 photoswipe 实例
    +  state = await registerPhotoSwipe(images, {
    +    // photoswipe 选项
    +  })
    +})
    +
    +onUnmounted(() => {
    +  destroy?.()
    +})
    +</script>
    +

    样式

    你可以通过 CSS 变量来自定义部分样式:

    :root {
    +  --photo-swipe-bullet: #fff;
    +  --photo-swipe-bullet-active: #3eaf7c;
    +}
    +
    + + + diff --git a/zh/plugins/prismjs.html b/zh/plugins/prismjs.html new file mode 100644 index 0000000000..bdfeea00e1 --- /dev/null +++ b/zh/plugins/prismjs.html @@ -0,0 +1,47 @@ + + + + + + + + + prismjs | VuePress 生态系统 + + + + + +

    prismjs

    @vuepress/plugin-prismjs

    该插件使用 Prism.js在新窗口打开 来为 Markdown 代码块启用代码高亮。

    该插件已经集成到默认主题中。

    需要注意的是,该插件仅会给代码块添加 HTML 标记,而不会添加样式。当你在一个自定义主题中使用它时,可能需要自己选择并引入 Prism.js 样式主题。

    使用方法

    npm i -D @vuepress/plugin-prismjs@next
    +
    import { prismjsPlugin } from '@vuepress/plugin-prismjs'
    +
    +export default {
    +  plugins: [
    +    prismjsPlugin({
    +      // 配置项
    +    }),
    +  ],
    +}
    +

    配置项

    preloadLanguages

    • 类型: string[]

    • 默认值: ['markdown', 'jsdoc', 'yaml']

    • 详情:

      需要预加载的语言。

      默认情况下,语言会在解析 Markdown 文件时按需加载。

      然而, Prism.js 在动态加载语言时可能会遇到 一些潜在的问题在新窗口打开 。为了避免这些问题,你可以使用该配置项来预加载一些语言。

    + + + diff --git a/zh/plugins/pwa/config.html b/zh/plugins/pwa/config.html new file mode 100644 index 0000000000..0df099325f --- /dev/null +++ b/zh/plugins/pwa/config.html @@ -0,0 +1,176 @@ + + + + + + + + + 配置 | VuePress 生态系统 + + + + + +

    配置

    选项

    showInstall

    • 类型:boolean

    • 默认值:false

    • 详情:

      是否在 Service Worker 首次成功注册时显示 PWA 安装按钮

    manifest

    • 类型:ManifestOption

    • 详情:

      填充一个将被解析为 manifest.webmanifest 的对象。

      提示

      如果未设置某些选项,它们会回退到插件预设值。

      • name: siteConfig.title || siteConfig.locales['/'].title || "Site"
      • short_name: siteConfig.title || siteConfig.locales['/'].title || "Site"
      • description: siteConfig.description || siteConfig.locales['/'].description || "A site built with vuepress"
      • lang: siteConfig.locales['/'].lang || "en-US"
      • start_url: context.base
      • scope: context.base
      • display: "standalone"
      • theme_color: "#46bd87"
      • background_color: "#ffffff"
      • orientation: "portrait-primary"
      • prefer_related_applications: false
    • 参考:

    favicon

    • 类型:string

    • 详情:

      favicon.ico 地址,填入绝对路径。

      注意

      我们建议你为你的站点生成 favicon。

    themeColor

    • 类型:string
    • 默认值:"#46bd87"
    • 详情:PWA 的主题色。

    cacheHTML

    • 类型:boolean
    • 默认值:false
    • 详情:是否缓存主页和 404 错误页之外的 HTML 文件

    cacheImage

    • 类型:boolean
    • 默认值:false
    • 详情:是否缓存图片。

    maxSize

    • 类型:number

    • 默认值:2048

    • 详情:

      允许缓存的最大大小 (以 KB 为单位)

      注意

      此选项具有最高优先级,任何超过此值的文件都会被排除。

      所以你如果生成了很大的 HTML 或 JS 文件,请考虑调高此值,否则你的 PWA 可能无法在离线模式下正常运行。

    maxImageSize

    • 类型:number

    • 默认值:1024

    • 详情:

      图片允许缓存的最大大小 (以 KB 为单位)

      该选项不能大于 maxSize 选项

    update

    • 类型:"disabled" | "available" | "hint" | "force"

    • 默认值:"available"

    • 详情:

      发现新内容时的控制逻辑。

      • "disabled": 即使有新的 service worker 也不做任何事情,新的 service work 开始等待后,会在用户下次访问时接管页面,让用户获得新内容。

      • "available": 仅当新的 service worker 可用时才显示更新弹出窗口

      • "hint": 显示更新内容可用提示,并允许用户立即刷新。当新的 SW 成功注册后,将转为更新内容就绪弹窗。

        当你希望用户立即查看新文档时,这很有帮助。

        提示

        如果用户在新 SW 就绪前选择刷新,当前的 Service Worker 将被注销,并且请求将开始向 Web 发出。新的 service worker 将开始安装并在安装后接管页面。

      • "force": 立即注销当前 Service Worker 然后刷新以获取新内容

        警告

        虽然这可以确保用户访问的是最新内容,但这可能会影响访问体验。

      提示

      文档的更新方式由以前的版本控制,因此当前选项仅影响此版本的下一次更新。

    apple

    更高支持苹果的特殊设置,忽略它们是安全的。

    apple.icon

    • 类型:string
    • 详情:填入苹果使用的图标地址,推荐 152×152 大小

    apple.statusBarColor

    • 类型:"black" | "white"
    • 默认值:"black"
    • 详情:Safari 状态栏颜色

    apple.maskIcon

    • 类型:string
    • 详情:Safari 图标

    msTile

    针对微软磁贴的特殊设置,忽略它们是安全的。

    msTile.image

    • 类型:string
    • 详情:磁贴图标

    msTile.color

    • 类型:string
    • 默认值:themeColor
    • 详情:磁贴颜色。

    foundComponent

    • 类型:string
    • 默认值:"PWAFoundPopup"
    • 详情:自定义的提示弹窗组件路径。

    readyComponent

    • 类型:string
    • 默认值:"PWAReadyPopup"
    • 详情:自定义的更新弹窗组件路径。

    appendBase

    • 类型:boolean
    • 默认值:false
    • 详情:是否为选项中所有绝对链接添加 base。

    generateSwConfig

    locales

    • 类型:PWAPluginLocaleConfig

      interface PWAPluginLocaleData {
      +  /**
      +   * 安装按钮文字
      +   */
      +  install: string
      +
      +  /**
      +   * iOS 安装文字
      +   */
      +  iOSInstall: string
      +
      +  /**
      +   * 取消按钮文字
      +   */
      +  cancel: string
      +
      +  /**
      +   * 关闭按钮文字
      +   */
      +  close: string
      +
      +  /**
      +   * 上一张图片文字
      +   */
      +  prevImage: string
      +
      +  /**
      +   * 下一张图片文字
      +   */
      +  nextImage: string
      +
      +  /**
      +   * 安装解释
      +   */
      +  explain: string
      +
      +  /**
      +   * 描述标签文字
      +   */
      +  desc: string
      +
      +  /**
      +   * 特性标签文字
      +   */
      +  feature: string
      +
      +  /**
      +   * 更新内容提示文字
      +   */
      +  hint: string
      +
      +  /**
      +   * 更新内容可用文字
      +   */
      +  update: string
      +}
      +
      +interface PWAPluginLocaleConfig {
      +  [localePath: string]: PWAPluginLocaleData
      +}
      +
    • 详情:

      PWA 插件的国际化配置。

    内置支持语言
    • 简体中文 (zh-CN)
    • 繁体中文 (zh-TW)
    • 英文(美国) (en-US)
    • 德语 (de-DE)
    • 德语(澳大利亚) (de-AT)
    • 俄语 (ru-RU)
    • 乌克兰语 (uk-UA)
    • 越南语 (vi-VN)
    • 葡萄牙语(巴西) (pt-BR)
    • 波兰语 (pl-PL)
    • 法语 (fr-FR)
    • 西班牙语 (es-ES)
    • 斯洛伐克 (sk-SK)
    • 日语 (ja-JP)
    • 土耳其语 (tr-TR)
    • 韩语 (ko-KR)
    • 芬兰语 (fi-FI)
    • 印尼语 (id-ID)
    • 荷兰语 (nl-NL)

    组合式 API

    usePwaEvent

    import { usePwaEvent } from '@vuepress/plugin-pwa/client'
    +
    +export default {
    +  setup() {
    +    const event = usePwaEvent()
    +    event.on('ready', (registration) => {
    +      console.log('Service worker is active.')
    +    })
    +  },
    +}
    +

    工具函数

    forceUpdate

    • 详情:

      当发现新内容时强制刷新页面。

    • 示例:

    import { forceUpdate } from '@vuepress/plugin-pwa/client'
    +import { onMounted } from 'vue'
    +
    +export default {
    +  setup() {
    +    onMounted(() => {
    +      forceUpdate()
    +    })
    +  },
    +}
    +

    registerSW

    • 详情:

      手动注册 Service Worker。

    • 参数:

    参数类型描述
    serviceWorkerPathstringService worker 的路径
    hooksobjectService worker 的钩子
    showStatusboolean在控制台输出状态日志
    interface Hooks {
    +  registrationOptions?: RegistrationOptions
    +  ready?: (registration: ServiceWorkerRegistration) => void
    +  registered?: (registration: ServiceWorkerRegistration) => void
    +  cached?: (registration: ServiceWorkerRegistration) => void
    +  updated?: (registration: ServiceWorkerRegistration) => void
    +  updatefound?: (registration: ServiceWorkerRegistration) => void
    +  offline?: () => void
    +  error?: (error: Error) => void
    +}
    +
    • 示例:
    import { registerSW } from '@vuepress/plugin-pwa/client'
    +import { onMounted } from 'vue'
    +
    +export default {
    +  setup() {
    +    onMounted(() => {
    +      registerSW('/service-worker.js', {
    +        ready(registration) {
    +          console.log('Service worker is active.')
    +        },
    +      })
    +    })
    +  },
    +}
    +

    skipWaiting

    • 详情:

      激活等待中的 Service Worker。

    • 参数:

    参数类型描述
    registrationServiceWorkerRegistration想要激活的 Service Worker 的注册
    • 示例:
    import { usePwaEvent, skipWaiting } from '@vuepress/plugin-pwa/client'
    +
    +export default {
    +  setup() {
    +    const event = usePwaEvent()
    +
    +    event.on('updated', (registration) => {
    +      console.log('The waiting service worker is available.')
    +      // activate the waiting service worker
    +      skipWaiting(registration)
    +    })
    +  },
    +}
    +

    unregisterSW

    • 详情:

      手动注销 Service Worker。

    • 示例:

    import { unregisterSW } from '@vuepress/plugin-pwa/client'
    +import { onMounted } from 'vue'
    +
    +export default {
    +  setup() {
    +    onMounted(() => {
    +      unregisterSW()
    +    })
    +  },
    +}
    +

    样式

    你可以通过 CSS 变量来自定义样式:

    :root {
    +  --pwa-z-index: 10;
    +  --pwa-color: #2c3e50;
    +  --pwa-bg-color: #ffffff;
    +  --pwa-border-color: #3eaf7c;
    +  --pwa-shadow-color: rgb(0 0 0 / 15%);
    +  --pwa-btn-text-color: #ffffff;
    +  --pwa-btn-bg-color: #3eaf7c;
    +  --pwa-btn-hover-bg-color: #4abf8a;
    +  --pwa-content-color: #333;
    +  --pwa-content-light-color: #666;
    +}
    +
    + + + diff --git a/zh/plugins/pwa/guide.html b/zh/plugins/pwa/guide.html new file mode 100644 index 0000000000..0641d516b1 --- /dev/null +++ b/zh/plugins/pwa/guide.html @@ -0,0 +1,59 @@ + + + + + + + + + 指南 | VuePress 生态系统 + + + + + +

    指南

    介绍

    将你的 VuePress 站点变成渐进式网络应用程序 (PWA)[1]

    此插件使用 workbox-build在新窗口打开 生成 Service Worker 文件,并使用 register-service-worker在新窗口打开 注册 Service Worker。

    注意

    如果你启用过该插件,并想要禁用它,你可能需要 `@vuepress/plugin-remove-pwa 来移除现有的 Service Worker 。

    A PWA uses a Service Worker [2] (SW for short) to cache and proxy site content.

    一个 PWA 使用 Service Worker [2:1] (简称 SW) 来获取并托管网站内容。

    网络 App 清单

    为了使你的网站符合 PWA 的要求,一个 网络 App 清单在新窗口打开 文件是必要的,并且你的 PWA 应满足可安装性[3]要求。

    你可以通过设置 manifest 选项来自定义 manifest 文件,或者在 public 文件夹中提供 manifest.webmanifestmanifest.json。前者优先级更高。

    插件会自动为你生成 manifest.webmanifest,并在每个页面的 <head> 中添加清单链接声明,但是 你至少应该通过 manifest.icons 或 PWA 插件中的其他选项设置一个有效的图标。

    注意

    可安装性[3:1]规范要求 manifest 中至少声明一个有效的图标。

    所以如果你不配置 manifest.icons,访问者只能享受到 Service Worker 缓存带来的离线可访问性,而并不能作为 PWA 进行安装。

    此外,该插件默认不处理清单中的任何内容,而是按原样输出。 这意味着,如果你计划部署到子目录,则应自行将 URL 前缀附加到自己的清单 Urls 中。如果你需要的所有东西都在 base 文件夹下,你可以在插件选项中设置 appendBase: true 让插件将 base 自动附加到任何地址。

    缓存控制

    为了更好的控制 Service Worker 可以预缓存的内容,插件提供了相关的缓存控制选项。

    默认缓存

    默认情况下插件会预缓存所有的 JS 和 CSS 文件,但仅缓存主页和 404 页面的 HTML。插件同时还会缓存字体文件 (woff, woff2, eot, ttf, otf) 和 SVG 图标。

    图片缓存

    如果你的站点只有少量重要图片,并希望它们在离线模式下显示,你可以通过设置 cacheImage 选项为 true 来缓存站点图片。

    我们通过文件后缀名识别图片,任何以 .png, .jpg, .jpeg, .gif, .bmp, .webp 结尾的文件都会视为图片。

    HTML 缓存

    当你网站体积不大,并且希望文档完全离线可用时,你可以通过设置 cacheHTMLtrue 来缓存所有 HTML 页面。

    为什么默认不缓存非主页和 404 页面

    虽然说 VuePress 为所有的页面通过 SSG[4] 生成了 HTML 文件,但是这些文件主要用于 SEO[5],并能够让你在后端不做 SPA[6] 配置的情况下能够直接访问任何链接。

    VuePress 本质上是一个 SPA。这意味着你只需要缓存主页并从主页进入即可正常访问所有页面。所以默认不缓存其他 HTML 能够有效减小缓存大小 (可以缩减大约 40% 的体积),加快 SW 更新速度。

    但是这样做也有缺点,如果用户直接从非主页进入网站,首个页面的 HTML 文件仍需要从互联网加载。同时离线环境下,用户只能通过主页进入再自行导航到对应页面,直接访问某个链接会出现无法访问的提示。

    大小控制

    为了防止在预缓存列表中包含大文件,任何 > 2 MB 的文件或 > 1 MB 的图片都将被忽略。 你可以通过 maxSizemaxImageSize 来自定义大小限制 (单位为 KB)。

    更新控制

    我们提供 update 选项控制用户如何接收更新。

    update 选项的默认值是 "available",这意味着当网站内容更新后,新的 SW 会在后台静默安装,并在安装结束后弹窗提示用户新内容就绪。用户可以自主选择是否立即刷新查看新内容。这意味在新 SW 就绪前用户会访问旧版本网站。

    如果你的文档仍在建设期,希望尽早提示用户他可能在阅读已过时的内容,你可以将其设置为 "hint"。这样用户在进入文档后数秒内就可以收到新内容已发布的通知。但这样做的负面效果是如果用户在新 SW 就绪前选择更新,那么他将在新 SW 安装并接管页面前,需要从互联网获取页面的全部资源。

    如果你的文档很稳定,或者你在托管博客,不太关心用户立即接收到最新版本,你可以将其设置为 "disabled",这意味着新的 SW 将在后台完全静默安装并在安装后等待,当旧版本 SW 控制的页面全部关闭后,新 SW 将再下次访问接管并提供用户新内容。此设置可以避免用户在访中被弹窗打扰。

    如果你希望通过 SW 来加速用户在弱网或无网条件下的访问,但同时希望用户时刻访问新内容,你可以将此选项设置为 "force"。这意味着检测到新 SW 后旧 SW 将会被立刻销毁并且页面会被刷新以确保用户浏览最新内容。最大的缺点就是致新 SW 发布后,用户在重新进入网站后的几秒内会遇到预期之外的突然刷新,并且他们将必须通过互联网访问文档并完全重新安装最新的 SW。

    更新提示弹窗

    当检测到新内容 (检测到新的 SW) 时,更新提示弹窗将会出现;当新内容就绪时,更新就绪弹窗将会出现。

    如果你对默认的弹窗不满意,你可以自行编写组件更换。从 @vuepress/plugin-pwa/client 中导入 PWAFoundPopupPWAReadyPopup 并使用其 slot 来自定义弹窗内容,然后将组件路径传递给 foundComponentreadyComponent 选项。

    <script setup lang="ts">
    +import { PWAFoundPopup } from '@vuepress/plugin-pwa/client'
    +</script>
    +<template>
    +  <PWAFoundPopup v-slot="{ found, refresh }">
    +    <div v-if="found">
    +      已找到新内容
    +      <button @click="refresh">刷新</button>
    +    </div>
    +  </PWAFoundPopup>
    +</template>
    +
    <script setup lang="ts">
    +import { PWAReadyPopup } from '@vuepress/plugin-pwa/client'
    +</script>
    +<template>
    +  <PWAReadyPopup v-slot="{ isReady, reload }">
    +    <div v-if="isReady">
    +      新内容已就绪
    +      <button @click="reload">应用</button>
    +    </div>
    +  </PWAReadyPopup>
    +</template>
    +

    其他选项

    插件还提供了其他 PWA 相关选项,比如微软磁贴图标与颜色设置,苹果图标等。 如果你是一个高级用户,你也可以设置 generateSwConfig 来配置 workbox-build。查看 插件选项 了解更多细节。

    相关阅读

    更多内容,请详见:


    1. PWA 介绍

      PWA 全称 Progressive Web app,即渐进式网络应用程序,标准由 W3C 规定。

      它允许网站通过支持该特性的浏览器将网站作为 App 安装在对应平台上。

      访问 https://developer.mozilla.org/zh-CN/docs/Web/Progressive_web_apps在新窗口打开 查看详情。 ↩︎

    2. Service Worker 简要介绍

      1. Service Worker 会在注册过程中获取注册在其中的所有文件并缓存它们。

      2. 注册成功后,Service Worker 激活,并开始代理并控制你的全部请求。

      3. 每当你想要通过浏览器发起访问请求后,Service Worker 将会查看其是否存在与自身缓存列表中,若存在则直接返回缓存好的结果,否则调用自身的 fetch 方法进行获取。你可以通过自定义 fetch 方法,来完全控制网页内资源获取请求的结果,比如在离线时提供一个 fallback 的网页。

      4. 每次用户重新打开网站时,Service Worker 会向自身注册时的地址发出校验命令,如果检测到新版本的 Service Worker,则会更新自身,并开始缓存注册在新 Service Worker 中的资源列表。成功获取内容更新后,Service Worker 将会触发 update 事件。可以通过此事件提示用户,比如将在右下角显示一个弹出窗口,提示用户新内容可用并允许用户触发更新。

      ↩︎ ↩︎
    3. 可安装性

      想要让网站可以注册为 PWA,网站需要自行成功注册有效的 Service Worker,同时拥有合法的 manifest 清单文件并在网站中声明它。

      清单文件应至少包含 name(或 short_name) icons start_url

      在 Safari 中,SW 的最大缓存空间为 50 MB。 ↩︎ ↩︎

    4. SSG: Static Site Generating,静态站点生成。 ↩︎

    5. SEO: Search Engine Optimization,搜索引擎增强,

      详见 SEO 介绍在新窗口打开 ↩︎

    6. SPA: Single Page Application, 单页应用

      大多只有主页,并使用 history mode 处理路由,而不是真的在页面之间导航。 ↩︎

    + + + diff --git a/zh/plugins/pwa/index.html b/zh/plugins/pwa/index.html new file mode 100644 index 0000000000..28e62480c2 --- /dev/null +++ b/zh/plugins/pwa/index.html @@ -0,0 +1,47 @@ + + + + + + + + + pwa | VuePress 生态系统 + + + + + +

    pwa

    @vuepress/plugin-pwa

    使用

    npm i -D @vuepress/plugin-pwa@next
    +
    import { pwaPlugin } from '@vuepress/plugin-pwa'
    +
    +export default {
    +  plugins: [
    +    pwaPlugin({
    +      // 选项
    +    }),
    +  ],
    +}
    +
    + + + diff --git a/zh/plugins/reading-time.html b/zh/plugins/reading-time.html new file mode 100644 index 0000000000..04c440667d --- /dev/null +++ b/zh/plugins/reading-time.html @@ -0,0 +1,131 @@ + + + + + + + + + reading-time | VuePress 生态系统 + + + + + +

    reading-time

    @vuepress/plugin-reading-time

    此插件会为每个页面生成字数统计与预计阅读时间。

    使用方法

    npm i -D @vuepress/plugin-reading-time@next
    +
    import { readingTimePlugin } from '@vuepress/plugin-reading-time'
    +
    +export default {
    +  plugins: [
    +    readingTimePlugin({
    +      // 配置项
    +    }),
    +  ],
    +}
    +

    插件会将相关信息注入到页面数据的 readingTime,其中:

    • readingTime.minutes:为预计阅读时间(分钟)number
    • readingTime.words:字数统计,number

    在 Node 侧获取数据

    对于任何页面,你可以从 page.data.readingTime 获取预计阅读时间与字数统计:

    page.data.readingTime // { minutes: 3.2, words: 934 }
    +

    你可以在 extendsPage 以及其他生命周期获取它做进一步处理:

    export default {
    +  // ...
    +  extendsPage: (page) => {
    +    page.data.readingTime // { minutes: 3.2, words: 934 }
    +  },
    +
    +  onInitialized: (app) => {
    +    app.pages.map((page) => {
    +      page.data.readingTime // { minutes: 3.2, words: 934 }
    +    })
    +  },
    +}
    +

    在客户端侧获取数据

    你可以从 @vuepress/plugin-reading-time/client 导入 useReadingTimeDatauseReadingTimeLocale 来获取当前页面的阅读时间数据和语言环境数据:

    <script setup lang="ts">
    +import {
    +  useReadingTimeData,
    +  useReadingTimeLocale,
    +} from '@vuepress/plugin-reading-time/client'
    +
    +const readingTimeData = useReadingTimeData() // { minutes: 1.1, words: 100 }
    +const readingTimeLocale = useReadingTimeLocale() // { time: "1 分钟", words: "100 字" }
    +</script>
    +

    选项

    wordPerMinute

    • 类型:number
    • 默认值:300
    • 详情: 每分钟阅读字数

    locales

    • 类型:ReadingTimePluginLocaleConfig

      interface ReadingTimePluginLocaleData {
      +  /**
      +   * 字数模板,模板中 `$word` 会被自动替换为字数
      +   */
      +  word: string
      +
      +  /**
      +   * 小于一分钟文字
      +   */
      +  less1Minute: string
      +
      +  /**
      +   * 时间模板
      +   */
      +  time: string
      +}
      +
      +interface ReadingTimePluginLocaleConfig {
      +  [localePath: string]: ReadingTimePluginLocaleData
      +}
      +
    • 必填:否

    • 详情:

      阅读时间插件的国际化配置。

    内置支持语言
    • 简体中文 (zh-CN)
    • 繁体中文 (zh-TW)
    • 英文(美国) (en-US)
    • 德语 (de-DE)
    • 德语(澳大利亚) (de-AT)
    • 俄语 (ru-RU)
    • 乌克兰语 (uk-UA)
    • 越南语 (vi-VN)
    • 葡萄牙语(巴西) (pt-BR)
    • 波兰语 (pl-PL)
    • 法语 (fr-FR)
    • 西班牙语 (es-ES)
    • 斯洛伐克 (sk-SK)
    • 日语 (ja-JP)
    • 土耳其语 (tr-TR)
    • 韩语 (ko-KR)
    • 芬兰语 (fi-FI)
    • 印尼语 (id-ID)
    • 荷兰语 (nl-NL)

    客户端 API

    你可以从 @vuepress/plugin-reading-time/client 导入并使用这些 API:

    即使插件被禁用,这些 API 也不会抛出错误。

    useReadingTimeData

    interface ReadingTime {
    +  /** 分钟为单位的预计阅读时长 */
    +  minutes: number
    +  /** 内容的字数 */
    +  words: number
    +}
    +
    +const useReadingTimeData: () => ComputedRef<ReadingTime | null>
    +

    当插件被禁用时会返回 null

    useReadingTimeLocale

    interface ReadingTimeLocale {
    +  /** 当前语言的预计阅读时间 */
    +  time: string
    +  /** 当前语言的字数文字 */
    +  words: string
    +}
    +
    +const useReadingTimeLocale: () => ComputedRef<ReadingTimeLocale>
    +

    高级使用

    由于此插件主要面向插件和主题开发者,所以提供了 "使用 API":

    import { useReadingTimePlugin } from '@vuepress/plugin-reading-time'
    +
    +export default (options) => (app) => {
    +  useReadingTimePlugin(app, {
    +    // 你的选项
    +  })
    +
    +  return {
    +    name: 'vuepress-plugin-xxx', // or vuepress-theme-xxx
    +  }
    +}
    +

    为什么你应该使用 "使用 API"

    1. 当你多次注册一个插件时,vuepress 会给你一个警告,告诉你只有第一个插件会生效。useReadingTimePlugin 会自动检测插件是否已经注册,避免多次注册。

    2. 如果你在 extendsPage 生命周期访问阅读时间数据,那么 @vuepress/plugin-reading-time 必须在你的主题或插件之前被调用,否则你会得到未定义的 page.data.readingTimeuseReadingTimePlugin 确保了 @vuepress/plugin-reading-time 在你的主题或插件之前被调用。

    我们也提供了一个 removeReadingTimePlugin api 来移除插件。你可以使用它来确保你的调用生效或清除插件:

    import { useReadingTimePlugin } from '@vuepress/plugin-reading-time'
    +
    +export default (options) => (app) => {
    +  // 这会移除任何当前存在的阅读时间插件
    +  removeReadingTimePlugin(app)
    +
    +  // 所以这会生效,即使之前已经注册了一个阅读时间插件
    +  useReadingTimePlugin(app, {
    +    // 你的选项
    +  })
    +
    +  return {
    +    name: 'vuepress-plugin-xxx', // or vuepress-theme-xxx
    +  }
    +}
    +
    + + + diff --git a/zh/plugins/redirect.html b/zh/plugins/redirect.html new file mode 100644 index 0000000000..23f37c5a99 --- /dev/null +++ b/zh/plugins/redirect.html @@ -0,0 +1,117 @@ + + + + + + + + + redirect | VuePress 生态系统 + + + + + +

    redirect

    @vuepress/plugin-rtl

    此插件提供页面与整站重定向功能。

    使用方法

    npm i -D @vuepress/plugin-redirect@next
    +
    import { redirectPlugin } from '@vuepress/plugin-redirect'
    +
    +export default {
    +  plugins: [
    +    redirectPlugin({
    +      // 配置项
    +    }),
    +  ],
    +}
    +

    设置重定向

    如果你改动了已有页面的地址,你可以在 Frontmatter 中使用 redirectFrom 选项设置重定向到此页面的地址,这样可以保证用户在访问旧链接时重定向到新的地址。

    如果你需要将已有的页面重定向到新的页面,可以在 Frontmatter 中使用 redirectTo 选项设置需要重定向到的地址。这样该页面会在访问时重定向到新的地址。

    你还可以通过插件选项中的 config 设置一个重定向映射,详见 config

    自动多语言

    插件可以根据用户的语言首选项,自动将无多语言链接重定向到用户需要的多语言页面。为了实现这一点,你需要留空默认的语言目录 (/),并在插件选项中设置 autoLocale: true。插件会自动根据用户语言跳转到对应的语言页面。

    也就是你需要设置以下目录结构:

    .
    +├── en
    +│   ├── ...
    +│   ├── page.md
    +│   └── README.md
    +├── zh
    +│   ├── ...
    +│   ├── page.md
    +│   └── README.md
    +└── other_languages
    +    ├── ...
    +    ├── page.md
    +    └── README.md
    +

    并将主题选项的 locales 设置为:

    export default {
    +  locales: {
    +    '/en/': {
    +      lang: 'en-US',
    +      // ...
    +    },
    +    '/zh/': {
    +      lang: 'zh-CN',
    +      // ...
    +    },
    +    // other languages
    +  },
    +  // ...
    +}
    +

    这样当用户访问 //page.html 时,他们会自动根据当前浏览器语言重定向到 /en/ /en/page.html/zh/ /zh/page.html

    自定义回退行为

    有些时候,用户可能会在系统设置中添加多个语言。默认情况下,在站点支持首选语言,但首选语言不存在相应页面时,插件会尝试匹配用户设置的备用语言。

    如果不需要回退到用户备用语言,而直接匹配用户首选语言,请在插件选项中设置 localeFallback: false

    自定义缺失行为

    有些时候,当用户访问一个页面时,文档尚未包含用户需要的语言版本 (一个普遍的情况是当前页面尚未完成相关语言的本地化),这样插件需要做出默认行为,你可以通过插件选项中的 defaultBehavior 定制它:

    • "defaultLocale": 重定向到默认语言或首个可用语言页面 (默认行为)
    • "homepage": 重定向到当前语言的主页 (仅在文档包含用户语言时可用)
    • "404": 重定向到当前语言的 404 页 (仅在文档包含用户语言时可用)

    自定义默认路径

    你可以通过设置插件选项中的 defaultLocale 来自定义默认路径。默认情况下,插件会使用 locales 中的第一个键名作为默认路径。

    自动切换语言

    插件支持在多语言文档中,自动根据用户语言首选项,将链接切换到用户需要的多语言页面。为了实现这一点,你需要在插件选项中设置 switchLocale,它可以是以下两个值:

    • direct: 直接切换到用户语言首选项页面,而不询问
    • modal: 在用户语言首选项与当前页面语言不同时,弹出一个对话框询问用户是否切换语言

    自定义多语言配置

    默认情况下,插件会从站点的多语言配置 locales 选项中,读取 语言路径lang 生成多语言配置。有些时候,你可能希望多个语言命中同一个路径,这种情况下,你应该设置插件的 localeConfig 选项。

    比如,你可能希望所有英文用户都匹配到 /en/,并将繁体中文用户匹配到 /zh/ 中,那么你可以设置:

    redirect({
    +  localeConfig: {
    +    '/en/': ['en-US', 'en-UK', 'en'],
    +    '/zh/': ['zh-CN', 'zh-TW', 'zh'],
    +  },
    +})
    +

    重定向站点

    有时你可能会更改 base 或为你的站点使用新域名,因此你可能希望原始站点自动重定向到新站点。

    为了解决这个问题,插件提供了 vp-redirect 脚手架。

    使用:
    +  $ vp-redirect generate [源文件夹]
    +
    +Options:
    +  --hostname <hostname>  重定向到的域名 (例如: https://new.example.com/) (默认: /)
    +  -c, --config <config>  设置配置文件路径
    +  -o, --output <output>  设置输出目录 (默认: .vuepress/redirect)
    +  --cache <cache>        设置缓存文件的目录
    +  -t, --temp <temp>      设置临时文件的目录
    +  --clean-cache          生成前清理缓存文件
    +  --clean-temp           生成前清理临时文件
    +  -h, --help             显示此消息
    +

    你需要传入 VuePress 项目源目录并设置 hostname 选项。重定向助手脚手架将初始化你的 VuePress 项目以获取页面,然后在输出目录生成重定向 html 文件。

    默认情况下,插件将输出到源文件夹下的 .vuepress/redirect 目录。你应该将其上传到你的原始站点以提供重定向。

    选项

    config

    • 类型:Record<string, string> | ((app: App) => Record<string, string>)

    • 详情

      页面重定向映射。

      可直接传入对象或传入参数为 App 的函数返回值一个对象。

      每个键名必须是一个绝对路径,代表重定向的源页面地址。

      每个键值是重定向的目标地址,可以是绝对路径或完整路径。

    • 示例:

      当 base 为 /base/时:

      • /base/foo.html 重定向到 /base/bar.html
      • /base/baz.html 重定向到 https://example.com/qux.html
      redirect({
      +  config: {
      +    '/foo.html': '/bar.html',
      +    '/baz.html': 'https://example.com/qux.html',
      +  },
      +})
      +

      将 post 文件夹的路径重定向到 posts 文件夹

      redirect({
      +  hostname: 'https://example.com',
      +  config: (app) =>
      +    Object.fromEntries(
      +      app.pages
      +        .filter(({ path }) => path.startsWith('/posts/'))
      +        .map(({ path }) => [path.replace(/^\/posts\//, '/post/'), path]),
      +    ),
      +})
      +

    autoLocale

    switchLocale

    • 类型:"direct" | "modal" | false

    • 默认值: false

    • 详情:

      是否根据用户偏好切换到新的语言环境。

      • "direct": 直接重定向到新的语言环境而不询问
      • "modal": 显示一个模式让用户选择是否切换到新的语言环境

    localeConfig

    • 类型:Record<string, string | string[]>
    • 详情:多语言语言配置

    localeFallback

    • 类型:boolean
    • 默认值: true
    • 详情:是否回退到用户定义的其他语言

    defaultBehavior

    • 类型:"defaultLocale" | "homepage" | "404"
    • 默认值: "defaultLocale"
    • 详情:当前链接没有可用的语言版本时的行为

    defaultLocale

    • 类型:string
    • 默认值: 首个语言路径
    • 详情:默认语言路径

    Frontmatter

    redirectFrom

    • 类型:string | string[]
    • 详情:重定向到该页面的地址。

    redirectTo

    • 类型:string
    • 详情:该页面重定向到的地址。

    样式

    你可以通过 CSS 变量来自定义重定向弹窗的样式:

    :root {
    +  --redirect-z-index: 1499;
    +  --redirect-bg-color: #fff;
    +  --redirect-bg-color-light: #f3f4f5;
    +  --redirect-bg-color-lighter: #eeeeee;
    +  --redirect-text-color: #2c3e50;
    +  --redirect-primary-bg-color: #3eaf7c;
    +  --redirect-primary-hover-bg-color: #4abf8a;
    +  --redirect-primary-text-color: #fff;
    +}
    +
    + + + diff --git a/zh/plugins/register-components.html b/zh/plugins/register-components.html new file mode 100644 index 0000000000..744eca8716 --- /dev/null +++ b/zh/plugins/register-components.html @@ -0,0 +1,85 @@ + + + + + + + + + register-components | VuePress 生态系统 + + + + + +

    register-components

    @vuepress/plugin-register-components

    根据组件文件或目录自动注册 Vue 组件。

    使用方法

    npm i -D @vuepress/plugin-register-components@next
    +
    import { registerComponentsPlugin } from '@vuepress/plugin-register-components'
    +
    +export default {
    +  plugins: [
    +    registerComponentsPlugin({
    +      // 配置项
    +    }),
    +  ],
    +}
    +

    配置项

    components

    • 类型: Record<string, string>

    • 默认值: {}

    • 详情:

      一个定义了组件名称和其对应文件路径的对象。

      键会被用作组件名称,值是组件文件的绝对路径。

      如果该配置项中的组件名称和 componentsDir 配置项发生冲突,那么该配置项会有更高的优先级。

    • 示例:

    import { getDirname, path } from 'vuepress/utils'
    +
    +const __dirname = getDirname(import.meta.url)
    +
    +export default {
    +  plugins: [
    +    registerComponentsPlugin({
    +      components: {
    +        FooBar: path.resolve(__dirname, './components/FooBar.vue'),
    +      },
    +    }),
    +  ],
    +}
    +

    componentsDir

    • 类型: string | null

    • 默认值: null

    • 详情:

      组件目录的绝对路径。

      该目录下匹配 componentsPatterns 的文件会被自动注册为 Vue 组件。

    • 示例:

    import { getDirname, path } from 'vuepress/utils'
    +
    +const __dirname = getDirname(import.meta.url)
    +
    +export default {
    +  plugins: [
    +    registerComponentsPlugin({
    +      componentsDir: path.resolve(__dirname, './components'),
    +    }),
    +  ],
    +}
    +

    组件目录:

    components
    +├─ FooBar.vue
    +└─ Baz.vue
    +

    组件会像这样被注册:

    import { defineAsyncComponent } from 'vue'
    +
    +app.component(
    +  'FooBar',
    +  defineAsyncComponent(() => import('/path/to/components/FooBar.vue')),
    +)
    +
    +app.component(
    +  'Baz',
    +  defineAsyncComponent(() => import('/path/to/components/Baz.vue')),
    +)
    +

    componentsPatterns

    getComponentName

    • 类型: (filename: string) => string

    • 默认值: (filename) => path.trimExt(filename.replace(/\/|\\/g, '-'))

    • 详情:

      用于从文件名获取对应组件名称的函数。

      它只会对 componentsDir 目录下匹配了 componentsPatterns 的文件生效。

      注意,这里的 filename 是相对于 componentsPatterns 目录的文件路径。

    + + + diff --git a/zh/plugins/remove-pwa.html b/zh/plugins/remove-pwa.html new file mode 100644 index 0000000000..40e14db1c4 --- /dev/null +++ b/zh/plugins/remove-pwa.html @@ -0,0 +1,47 @@ + + + + + + + + + remove-pwa | VuePress 生态系统 + + + + + +

    remove-pwa

    @vuepress/plugin-remove-pwa

    此插件从你的 VuePress 站点中删除任何相关的 Service Worker,因此如果你在启用后任何 PWA 插件后移除它们,用户仍然可以获得更新。

    如果你启用过 PWA,为什么需要这个插件?

    PWA 插件,如 @vuepress/plugin-pwa 将 Service Worker 注册到你的站点,这将缓存你的站点并使其离线可用。

    但是,如果你删除 PWA 插件,先前的 Service Worker 仍将在那里,但它们永远无法获得更新,因为他们永远无法找到要更新的新 Service Worker。 因此,用户将继续使用你网站的旧版本。

    要解决这个问题:

    1. 一个新的内容为空的 Service Worker 需要生成在原位置。

    2. 新的 Service Worker 应该尝试删除旧 Service Worker 缓存的内容,然后它应该注销自己。

    使用方法

    npm i -D @vuepress/plugin-remove-pwa@next
    +
    import { removePwaPlugin } from '@vuepress/plugin-remove-pwa'
    +
    +export default {
    +  plugins: [
    +    removePwaPlugin({
    +      // options
    +    }),
    +  ],
    +}
    +

    选项

    cachePrefix

    • 类型:string
    • 默认值:'workbox'
    • 详情:Service worker 的缓存前缀。

    swLocation

    • 类型: string
    • 默认值:'service-worker.js'
    • 详情:旧 Service Worker 的位置。
    + + + diff --git a/zh/plugins/rtl.html b/zh/plugins/rtl.html new file mode 100644 index 0000000000..dfaca83e4f --- /dev/null +++ b/zh/plugins/rtl.html @@ -0,0 +1,53 @@ + + + + + + + + + rtl | VuePress 生态系统 + + + + + +

    rtl

    @vuepress/plugin-rtl

    此插件会在配置的语言上设置 rtl 方向。

    使用方法

    npm i -D @vuepress/plugin-rtl@next
    +
    import { rtlPlugin } from '@vuepress/plugin-rtl'
    +
    +export default {
    +  plugins: [
    +    rtlPlugin({
    +      // 配置项
    +      locales: ['/ar/'],
    +    }),
    +  ],
    +}
    +

    选项

    locales

    • 类型:string[]
    • 默认值:['/']
    • 详情: 开启 RTL 布局的多语言路径。

    selector

    • 类型:SelectorOptions

      interface SelectorOptions {
      +  [element: string]: {
      +    [attrs: string]: string
      +  }
      +}
      +
    • 默认值:{ 'html': { dir: 'rtl' } }

    • 详情:

      开启 RTL 的选择器。

      默认设置意味着在 RTL 多语言中,html 元素的 dir 属性将被设置为 rtl

    + + + diff --git a/zh/plugins/search.html b/zh/plugins/search.html new file mode 100644 index 0000000000..b6fc2ae214 --- /dev/null +++ b/zh/plugins/search.html @@ -0,0 +1,111 @@ + + + + + + + + + search | VuePress 生态系统 + + + + + +

    search

    @vuepress/plugin-search

    为你的文档网站提供本地搜索能力。

    提示

    当你正确配置该插件后,默认主题会把搜索框添加到导航栏。

    该插件不一定能在其他主题中直接使用,因此你应参考主题本身的文档来获取更多信息。

    使用方法

    npm i -D @vuepress/plugin-search@next
    +
    import { searchPlugin } from '@vuepress/plugin-search'
    +
    +export default {
    +  plugins: [
    +    searchPlugin({
    +      // 配置项
    +    }),
    +  ],
    +}
    +

    本地搜索索引

    该插件会根据你的页面,在本地生成搜索索引,然后在用户访问站点时加载搜索索引文件。换句话说,这是一个轻量级的内置搜索能力,不会进行任何外部请求。

    然而,当你的站点包含大量页面时,搜索索引文件也会变得非常大,它可能会拖慢你的页面加载速度。在这种情况下,我们建议你使用更成熟的解决方案 - docsearch

    配置项

    locales

    • 类型: Record<string, { placeholder: string }>

    • 详情:

      搜索框在不同 locales 下的文字。

      如果没有指定该配置项,它会降级使用默认文字。

    • 示例:

    export default {
    +  plugins: [
    +    searchPlugin({
    +      locales: {
    +        '/': {
    +          placeholder: 'Search',
    +        },
    +        '/zh/': {
    +          placeholder: '搜索',
    +        },
    +      },
    +    }),
    +  ],
    +}
    +

    hotKeys

    • 类型: (string | HotKeyOptions)[]
    export interface HotKeyOptions {
    +    /**
    +     * Value of `event.key` to trigger the hot key
    +     */
    +    key: string;
    +    /**
    +     * Whether to press `event.altKey` at the same time
    +     *
    +     * @default false
    +     */
    +    alt?: boolean;
    +    /**
    +     * Whether to press `event.ctrlKey` at the same time
    +     *
    +     * @default false
    +     */
    +    ctrl?: boolean;
    +    /**
    +     * Whether to press `event.shiftKey` at the same time
    +     *
    +     * @default false
    +     */
    +    shift?: boolean;
    +}
    +
    • 默认值: ['s', '/']

    • 详情:

      指定热键的 event.key在新窗口打开

      当按下热键时,搜索框会被聚焦。

      将该配置项设为空数组可以禁用热键功能。

    maxSuggestions

    • 类型: number

    • 默认值: 5

    • 详情:

      指定搜索结果的最大条数。

    isSearchable

    • 类型: (page: Page) => boolean

    • 默认值: () => true

    • 详情:

      一个函数,用于判断一个页面是否应该被包含在搜索索引中。

      • 返回 true 来包含该页面。
      • 返回 false 来排除该页面。
    • 示例:

    export default {
    +  plugins: [
    +    searchPlugin({
    +      // 排除首页
    +      isSearchable: (page) => page.path !== '/',
    +    }),
    +  ],
    +}
    +

    getExtraFields

    • 类型: (page: Page) => string[]

    • 默认值: () => []

    • 详情:

      一个函数,用于在页面的搜索索引中添加额外字段。

      默认情况下,该插件会将页面标题和小标题作为搜索索引。该配置项可以帮助你添加更多的可搜索字段。

    • 示例:

    export default {
    +  plugins: [
    +    searchPlugin({
    +      // 允许搜索 Frontmatter 中的 `tags`
    +      getExtraFields: (page) => page.frontmatter.tags ?? [],
    +    }),
    +  ],
    +}
    +

    样式

    你可以通过 CSS 变量来自定义搜索框的样式:

    :root {
    +  --search-bg-color: #ffffff;
    +  --search-accent-color: #3eaf7c;
    +  --search-text-color: #2c3e50;
    +  --search-border-color: #eaecef;
    +  --search-item-text-color: #5d81a5;
    +  --search-item-focus-bg-color: #f3f4f5;
    +  --search-input-width: 8rem;
    +  --search-result-width: 20rem;
    +}
    +

    组件

    • 详情:

      该插件会全局注册一个 <SearchBox /> 组件,你可以不传入任何 Props 来使用它。

      将该组件放置在你想要显示搜索框的地方。例如,默认主题将这个组件放在了导航栏的末尾。

    提示

    该组件主要用于主题开发。在大多数情况下你不需要直接使用该组件。

    + + + diff --git a/zh/plugins/seo/config.html b/zh/plugins/seo/config.html new file mode 100644 index 0000000000..3aa0c12e8f --- /dev/null +++ b/zh/plugins/seo/config.html @@ -0,0 +1,81 @@ + + + + + + + + + 选项 | VuePress 生态系统 + + + + + +

    选项

    hostname

    • 类型:string

    • 必填:是

    • 详情:

      部署域名

    author

    • 类型:Author

      type AuthorName = string
      +
      +interface AuthorInfo {
      +  /**
      +   * 作者姓名
      +   */
      +  name: string
      +
      +  /**
      +   * 作者网站
      +   */
      +  url?: string
      +
      +  /**
      +   * 作者 Email
      +   */
      +  email?: string
      +}
      +
      +type Author = AuthorName | AuthorName[] | AuthorInfo | AuthorInfo[]
      +
    • 详情:

      默认作者

    autoDescription

    • 类型:boolean

    • 默认值:true

    • 详情:

      是否自动生成描述

    canonical

    • 类型:string | ((page: Page) => string | null)

    • 详情:

      首选链接

    fallBackImage

    • 类型:string

    • 详情:

      当找不到图片时的回退图片链接

    restrictions

    • 类型:string

    • 详情:

      内容的年龄分级,格式为 [int]+,如 "13+"

    twitterID

    • 类型:string

    • 详情:

      你的 twitter 用户名

    isArticle

    • 类型:(page: Page) => boolean

    • 详情:

      你可以使用此选项判断一个页面是否是文章。

    ogp

    • 类型:

      function ogp(
      +  /** 插件推断的 OGP 信息 */
      +  ogp: SeoContent,
      +  /** 页面对象 */
      +  page: Page,
      +  /** VuePress App */
      +  app: App,
      +): SeoContent
      +
    • 详情:

      自定义 OGP 生成器

      你可以使用此选项来注入新的或覆盖掉默认生成的 OGP 标签。

    jsonLd

    • 类型:

      function jsonLd(
      +  /** 由插件推断出的 JSON-LD 对象 */
      +  jsonLD: ArticleSchema | BlogPostingSchema | WebPageSchema,
      +  /** 页面对象 */
      +  page: Page,
      +  /** VuePress App */
      +  app: App,
      +): ArticleSchema | BlogPostingSchema | WebPageSchema
      +
    • 详情:

      自定义 JSON-LD 生成器

      你可以使用此选项来注入新的或覆盖掉默认生成的 JSON-LD 标签。

    customHead

    • 类型:

      function customHead(
      +  /** head 标签配置 */
      +  head: HeadConfig[],
      +  /** 页面对象 */
      +  page: Page,
      +  /** VuePress App */
      +  app: App,
      +): void
      +
    • 详情:

      你可以使用此选项来直接注入任意格式的标签到 <head>

    + + + diff --git a/zh/plugins/seo/guide.html b/zh/plugins/seo/guide.html new file mode 100644 index 0000000000..b9a11fedf3 --- /dev/null +++ b/zh/plugins/seo/guide.html @@ -0,0 +1,73 @@ + + + + + + + + + 指南 | VuePress 生态系统 + + + + + +

    指南

    本插件会通过向网站 <head> 注入标签,让你的网站完全支持 开放内容协议 OGP在新窗口打开JSON-LD 1.1在新窗口打开,以全面增强站点的搜索引擎优化性。

    开箱即用

    插件开箱即用,在不做任何配置的情况下,会尽可能通过页面内容,提取对应的信息补全 OGP 与 JSON-LD 所需的必要标签。

    默认情况下,插件会读取站点配置、主题配置与页面的 frontmatter 来尽可能自动生成。诸如站点名称,页面标题,页面类型,写作日期,最后更新日期,文章标签均会自动生成。

    默认的 OGP 生成逻辑

    属性名称
    og:urloptions.hostname + path
    og:site_namesiteConfig.title
    og:titlepage.title
    og:descriptionpage.frontmatter.description || 自动生成 (当插件选项中的 autoDescriptiontrue 时)
    og:type"article"
    og:imageoptions.hostname + page.frontmatter.image || 页面的第一张图片|| 插件选项的 fallbackImage
    og:updated_timepage.git.updatedTime
    og:localepage.lang
    og:locale:alternatesiteData.locales 包含的其他语言
    twitter:card"summary_large_image" (仅在找到图片时)
    twitter:image:altpage.title (仅在找到图片时)
    article:authorpage.frontmatter.author || options.author
    article:tagpage.frontmatter.tags || page.frontmatter.tag
    article:published_timepage.frontmatter.date || page.git.createdTime
    article:modified_timepage.git.updatedTime

    默认的 JSON-LD 生成逻辑

    属性名
    @context"https://schema.org"
    @type"NewsArticle"
    headlinepage.title
    image页面中的图片|| options.hostname + page.frontmatter.image
    datePublishedpage.frontmatter.date || page.git.createdTime
    dateModifiedpage.git.updatedTime
    authorpage.frontmatter.author || options.author

    直接添加 head 标签

    你可以在页面的 frontmatter 中配置 head 选项,自主添加特定标签到页面 <head> 以增强 SEO。

    如:

    ---
    +head:
    +  - - meta
    +    - name: keywords
    +      content: SEO plugin
    +---
    +

    会自动注入 <meta name="keywords" content="SEO plugin" />

    自定义生成过程

    本插件也支持你完全控制生成逻辑。

    页面类型

    对于大多数页面,基本只有文章和网页两种类型,所以插件提供了 isArticle 选项让你提供辨别文章的逻辑。

    选项接受一个 (page: Page) => boolean 格式的函数,默认情况下从 Markdown 文件生成的非主页页面都会被视为文章。

    提示

    如果某个网页的确符合图书、音乐之类的“冷门”类型,你可以通过设置下方三个选项处理它们。

    OGP

    你可以使用插件选项的 ogp 传入一个函数来按照你的需要修改默认 OGP 对象并返回。

    function ogp(
    +  /** 插件推断的 OGP 信息 */
    +  ogp: SeoContent,
    +  /** 页面对象 */
    +  page: Page,
    +  /** VuePress App */
    +  app: App,
    +): SeoContent
    +

    详细的参数结构详见 配置

    比如你在使用某个第三方主题,并按照主题要求为每篇文章在 Front Matter 中设置了 banner,那你可以传入这样的 ogp:

    seoPlugin({
    +  ogp: (ogp, page) => ({
    +    ...ogp,
    +    'og:image': page.frontmatter.banner || ogp['og:image'],
    +  }),
    +})
    +

    JSON-LD

    同 OGP,你可以使用插件选项的 jsonLd 传入一个函数来按照你的需要修改默认 JSON-LD 对象并返回。

    function jsonLd(
    +  /** 由插件推断出的 JSON-LD 对象 */
    +  jsonLD: ArticleSchema | BlogPostingSchema | WebPageSchema,
    +  /** 页面对象 */
    +  page: Page,
    +  /** VuePress App */
    +  app: App,
    +): ArticleSchema | BlogPostingSchema | WebPageSchema
    +

    规范链接

    如果你将内容部署到不同的站点,或不同 URL 下的相同内容,你可能需要设置 canonical 选项为你的页面提供 “规范链接”。 你可以设置一个字符串,这样它会附加在页面路由链接之前,或者添加一个自定义函数 (page: Page) => string | null 返回规范链接。

    例子

    如果你的站点部署在 example.com 的 docs 文件夹下,但同时在下列网址中可用:

    • http://example.com/docs/xxx
    • https://example.com/docs/xxx
    • http://www.example.com/docs/xxx
    • https://www.example.com/docs/xxx (首选)

    要让搜索引擎结果始终是首选,你可能需要将 canonical 设置为 https://www.example.com/docs/,以便搜索引擎知道首选第四个 URL 作为索引结果。

    自定义 head 标签

    有些时候你可能需要符合其他协议或按照其他搜索引擎提供的格式提供对应的 SEO 标签,此时你可以使用 customHead 选项,其类型为:

    function customHead(
    +  /** head 标签配置 */
    +  head: HeadConfig[],
    +  /** 页面对象 */
    +  page: Page,
    +  /** VuePress App */
    +  app: App,
    +): void
    +

    你应该直接修改传入的 head 参数。

    SEO 介绍

    搜索引擎优化 (Search Engine Optimization),是一种透过了解搜索引擎的运作规则来调整网站,以及提高目的网站在有关搜索引擎内排名的方式。由于不少研究发现,搜索引擎的用户往往只会留意搜索结果最前面的几个条目,所以不少网站都希望透过各种形式来影响搜索引擎的排序,让自己的网站可以有优秀的搜索排名。 所谓“针对搜索引擎作最优化的处理”,是指为了要让网站更容易被搜索引擎接受。搜索引擎会将网站彼此间的内容做一些相关性的资料比对,然后再由浏览器将这些内容以最快速且接近最完整的方式,呈现给搜索者。搜索引擎优化就是通过搜索引擎的规则进行优化,为用户打造更好的用户体验,最终的目的就是做好用户体验。

    相关文档

    相关工具

    你可以使用 Google 富媒体结构测试工具在新窗口打开 测试本站点。

    + + + diff --git a/zh/plugins/seo/index.html b/zh/plugins/seo/index.html new file mode 100644 index 0000000000..71a6a7cd52 --- /dev/null +++ b/zh/plugins/seo/index.html @@ -0,0 +1,47 @@ + + + + + + + + + seo | VuePress 生态系统 + + + + + +

    seo

    @vuepress/plugin-seo

    使用

    npm i -D @vuepress/plugin-seo@next
    +
    import { seoPlugin } from '@vuepress/plugin-seo'
    +
    +export default {
    +  plugins: [
    +    seoPlugin({
    +      // 选项
    +    }),
    +  ],
    +}
    +
    + + + diff --git a/zh/plugins/shiki.html b/zh/plugins/shiki.html new file mode 100644 index 0000000000..a376c3a3ac --- /dev/null +++ b/zh/plugins/shiki.html @@ -0,0 +1,48 @@ + + + + + + + + + shiki | VuePress 生态系统 + + + + + +

    shiki

    @vuepress/plugin-shiki

    该插件使用 Shiki在新窗口打开 来为 Markdown 代码块启用代码高亮。

    提示

    Shiki在新窗口打开 是 VSCode 正在使用的代码高亮器。它具有更高的保真度,但可能会比 Prism.js在新窗口打开 要慢一些,特别是在有大量代码块需要处理的时候。

    你可以考虑在 dev 模式下禁用该插件来获取更好的开发体验。

    使用方法

    npm i -D @vuepress/plugin-shiki@next
    +
    import { shikiPlugin } from '@vuepress/plugin-shiki'
    +
    +export default {
    +  plugins: [
    +    shikiPlugin({
    +      // 配置项
    +      langs: ['ts', 'json', 'vue', 'md', 'bash', 'diff'],
    +    }),
    +  ],
    +}
    +

    配置项

    langs

    • 类型: ShikiLang[]

    • 详情:

      Shiki 要解析的代码块的语言。

      该配置项会被传递到 Shiki 的 getHighlighter() 方法中。

      你最好明确传入所有你使用的语言列表,否则 Shiki 会加载所有语言,并可能影响性能。

    • 参考:

    theme

    • 类型: ShikiTheme

    • 默认值: 'nord'

    • 详情:

      Shiki 的主题。

      该配置项会被传递到 Shiki 的 codeToHtml() 方法中。

    • 参考:

    themes

    • 类型:Record<'dark' | 'light', ShikiTheme>

    • 详情:

      Shiki 的暗黑和明亮模式双主题。

      该配置项会被传递到 Shiki 的 codeToHtml() 方法中。

    • 参考:

    + + + diff --git a/zh/plugins/sitemap/config.html b/zh/plugins/sitemap/config.html new file mode 100644 index 0000000000..c56294ecf5 --- /dev/null +++ b/zh/plugins/sitemap/config.html @@ -0,0 +1,37 @@ + + + + + + + + + 配置 | VuePress 生态系统 + + + + + +

    配置

    hostname

    • 类型:string

    • 必填:是

    • 详情:

      当前网站部署到的域名,插件需要此选项才能工作。

    extraUrls

    • 类型:string[]

    • 详情:

      需要额外包含的网址。

      如果你有一些不包含在 VuePress 路由中的链接 (如: 存放在 public 文件夹下的页面或其他插件或工具直接生成的页面),你可能需要设置此项。

    • 示例:['/about.html', '/api/']

    excludePaths

    • 类型:string[]

    • 默认值:['/404.html']

    • 详情:

      不需要收录的页面路径,请以绝对路径开头。

      默认情况下 VuePress 自动生成的所有路径 (除 404 页) 都会被添加进 Sitemap。

    devServer

    • 类型:boolean

    • 默认值:false

    • 详情:

      是否在开发服务器中启用

    提示

    由于性能原因,我们不提供热更新。重启开发服务器以同步你的变更。

    devHostname

    • 类型:string

    • 默认值:"http://localhost:${port}"

    • 详情:

      开发服务器使用的主机名

    sitemapFilename

    • 类型:string

    • 默认值:"sitemap.xml"

    • 详情:

      输出的文件名,相对于输出目录。

    sitemapXSLFilename

    • 类型:string

    • 默认值:"sitemap.xsl"

    • 详情:

      输出的 xsl 文件名,相对于输出目录。

    sitemapXSLTemplate

    • 类型:string

    • 默认值:"@vuepress-plugin/sitemap/templates/sitemap.xsl"

    • 详情:

      用作模板的 XSL 文件内容

    changefreq

    • 类型:"always" | "hourly" | "daily" | "weekly" | "monthly" | "yearly" | "never"

    • 默认值:"daily"

    • 详情:

      页面默认更新频率,会被 Frontmatter 中的 changefreq 选项覆盖。

    priority

    • 类型:number

    • 默认值:0.5

    • 详情:

      页面优先级,范围 01

    modifyTimeGetter

    • 类型:(page: Page, app: App) => string

    • 详情:

      最后修改事件的获得器,需要返回一个 ISO 字符形式的时间,默认会自动通过 Git 插件生成。

    + + + diff --git a/zh/plugins/sitemap/frontmatter.html b/zh/plugins/sitemap/frontmatter.html new file mode 100644 index 0000000000..56a1049ba5 --- /dev/null +++ b/zh/plugins/sitemap/frontmatter.html @@ -0,0 +1,37 @@ + + + + + + + + + Frontmatter | VuePress 生态系统 + + + + + +

    Frontmatter

    sitemap

    • 类型:SitemapFrontmatterOptions | false

    • 详情:

      false 表示将页面排除在 sitemap 之外。

    sitemap.changefreq

    • 类型:"always" | "hourly" | "daily" | "weekly" | "monthly" | "yearly" | "never"

    • 默认值:"daily"

    • 详情:

      页面默认更新频率。它会覆盖插件选项中的 changefreq 选项。

    sitemap.priority

    • 类型:number

    • 默认值:0.5

    • 详情:

      页面优先级,范围 01

    + + + diff --git a/zh/plugins/sitemap/guide.html b/zh/plugins/sitemap/guide.html new file mode 100644 index 0000000000..0b033b13dc --- /dev/null +++ b/zh/plugins/sitemap/guide.html @@ -0,0 +1,45 @@ + + + + + + + + + 指南 | VuePress 生态系统 + + + + + +

    指南

    本插件会为你的网站自动生成 Sitemap。为了使插件正常工作,你需要将部署的域名传递给插件的 hostname 选项。如果你想在开发服务器中预览,请配置 devServer 选项。

    插件会自动根据页面的 Git 的时间戳生成页面的最后更新时间,同时会根据站点的多语言配置声明页面的其他语言版本替代地址。

    控制 Sitemap 链接

    默认情况下,所有除 404 页面以外的网站链接均会被添加进 Sitemap。

    如果你希望在 VuePress 项目页面之外,添加其他页面链接到 Sitemap,请将它们变成数组传入插件的 extraUrls 选项。

    如果你需要排除一些页面路径,你可以将它们变成数组传入到插件的 excludePaths 选项。你也可以在对应页面的 frontmatter 中,设置 sitemapfalse

    输出位置

    你还可以通过插件的 sitemapFilename 选项控制输出的地址,此地址相对于输出目录,默认为 sitemap.xml

    更新周期

    页面默认的更新周期是 daily (每天),如果你希望修改全部的页面周期,请在插件选项中设置 changefreq 。你也可以在页面的 frontmatter 中设置 sitemap.changefreq,页面具有更高的优先级。

    合法的频率有:

    • "always"
    • "hourly"
    • "daily"
    • "weekly"
    • "monthly"
    • "yearly"
    • "never"

    优先级

    你可以在插件中设置 priority 以提供一个默认值。同时你可以通过 frontmatter 中的 sitemap.priority 来为每个页面设置优先级。可接受的值为 01 的浮点数。

    修改时间获取

    你可以通过插件的 modifyTimeGetter 来返回一个 ISO 字符串格式的时间,默认会通过 Git 插件生成。

    以下是一个基于文件最后修改时间的例子。

    // 基于文件最后修改时间
    +({
    +  modifyTimeGetter: (page, app) =>
    +    fs.statSync(app.dir.source(page.filePathRelative)).mtime.toISOString();
    +})
    +

    Sitemap 介绍

    网站地图 (Sitemap) 提供搜索引擎优化 (SEO):

    • 为搜索引擎爬虫提供可以浏览整个网站的链接;
    • 为搜索引擎爬虫提供一些链接,指向动态页面或者采用其他方法比较难以到达的页面;
    • 如果访问者试图访问网站所在域内并不存在的 URL,那么这个访问者就会被转到“无法找到文件”的错误页面,而网站地图可以作为导航页。

    网站地图通过使所有页面可被找到来增强搜索引擎优化的效果。

    大部分搜索引擎只跟踪页面内有限数量的链接,因此当网站非常大的时候,网站地图对于使搜索引擎和访问者可以访问网站中的所有内容就变得必不可少了。

    Sitemaps 是站点管理员向搜索引擎爬虫公布站点可被抓取页面的协议,sitemap 文件内容必须遵循 XML 格式的定义。每个 URL 可以包含更新的周期和时间、URL 在整个站点中的优先级。这样可以让搜索引擎更佳有效的抓取网站内容。

    同步配置 robots.txt

    由于 Sitemap 面向搜索引擎,配合此插件使用时,你最好保证你在 .vuepress/public 文件夹下放置了有效的 robots.txt,以允许搜索引擎收录。一个最简单的 robots.txt 如下 (允许所有搜索引擎访问所有路径)

    User-agent: *
    +
    +Allow: /
    +
    + + + diff --git a/zh/plugins/sitemap/index.html b/zh/plugins/sitemap/index.html new file mode 100644 index 0000000000..05d7c67952 --- /dev/null +++ b/zh/plugins/sitemap/index.html @@ -0,0 +1,47 @@ + + + + + + + + + sitemap | VuePress 生态系统 + + + + + +

    sitemap

    @vuepress/plugin-sitemap

    使用

    npm i -D @vuepress/plugin-sitemap@next
    +
    import { sitemapPlugin } from '@vuepress/plugin-sitemap'
    +
    +export default {
    +  plugins: [
    +    sitemapPlugin({
    +      // 选项
    +    }),
    +  ],
    +}
    +
    + + + diff --git a/zh/plugins/theme-data.html b/zh/plugins/theme-data.html new file mode 100644 index 0000000000..2f4a65ded0 --- /dev/null +++ b/zh/plugins/theme-data.html @@ -0,0 +1,87 @@ + + + + + + + + + theme-data | VuePress 生态系统 + + + + + +

    theme-data

    @vuepress/plugin-theme-data

    为你的主题提供客户端数据,包含 VuePress 的 多语言支持

    该插件主要用于开发主题,并且已经集成到默认主题中。大部分情况下你不需要直接使用它。

    对于主题作者,该插件可以提供与 VuePress 及默认主题相同的多语言支持机制。但是如果你的主题不需要提供多语言支持,或者你想用你自己的方式来实现多语言支持,那么你不需要使用该插件。

    使用方法

    npm i -D @vuepress/plugin-theme-data@next
    +
    import { themeDataPlugin } from '@vuepress/plugin-theme-data'
    +
    +export default {
    +  plugins: [
    +    themeDataPlugin({
    +      // 配置项
    +    }),
    +  ],
    +}
    +

    配置项

    themeData

    • 类型: ThemeData

    • 默认值: {}

    • 详情:

      你希望在 Client 端中使用的主题数据对象。

      你可以通过该配置项,在 Node 端提供主题数据,然后在客户端通过 useThemeDatauseThemeLocaleData 来使用主题数据。

    • 示例:

    export default {
    +  plugins: [
    +    themeDataPlugin({
    +      themeData: {
    +        foo: 'foo',
    +        locales: {
    +          '/zh/': {
    +            foo: 'zh-foo',
    +          },
    +        },
    +      },
    +    }),
    +  ],
    +}
    +

    注意

    主题数据对象在传递到客户端之前,会使用 JSON.stringify() 进行处理,因此你需要保证你提供的是一个可以被 JSON 序列化的对象。

    Composition API

    useThemeData

    • 详情:

      返回主题数据的 Ref 对象。

      数据是通过 themeData 配置项提供的。

    • 示例:

    import { useThemeData } from '@vuepress/plugin-theme-data/client'
    +import type { ThemeData } from '@vuepress/plugin-theme-data/client'
    +
    +type MyThemeData = ThemeData<{
    +  foo: string
    +}>
    +
    +export default {
    +  setup() {
    +    const themeData = useThemeData<MyThemeData>()
    +    console.log(themeData.value)
    +  },
    +}
    +

    useThemeLocaleData

    • 详情:

      返回当前 locale 下主题数据的 Ref 对象。

      当前 locale 中的字段已被合并到顶层字段中。

    • 示例:

    import { useThemeLocaleData } from '@vuepress/plugin-theme-data/client'
    +import type { ThemeData } from '@vuepress/plugin-theme-data/client'
    +
    +type MyThemeData = ThemeData<{
    +  foo: string
    +}>
    +
    +export default {
    +  setup() {
    +    const themeLocaleData = useThemeLocaleData<MyThemeData>()
    +    console.log(themeLocaleData.value)
    +  },
    +}
    +
    + + + diff --git a/zh/plugins/toc.html b/zh/plugins/toc.html new file mode 100644 index 0000000000..e96b714d65 --- /dev/null +++ b/zh/plugins/toc.html @@ -0,0 +1,110 @@ + + + + + + + + + toc | VuePress 生态系统 + + + + + +

    toc

    @vuepress/plugin-toc

    该插件会提供一个目录 (table-of-contents, TOC) 组件。

    使用方法

    npm i -D @vuepress/plugin-toc@next
    +
    import { tocPlugin } from '@vuepress/plugin-toc'
    +
    +export default {
    +  plugins: [
    +    tocPlugin({
    +      // 配置项
    +    }),
    +  ],
    +}
    +

    与 Markdown 目录语法的区别

    Markdown 目录语法 类似,该插件提供的目录组件可以直接在你的 Markdown 内容中使用:

    <!-- Markdown 目录语法 -->
    +
    +[[toc]]
    +
    +<!-- Vue 目录组件 -->
    +<Toc />
    +

    在 Build 模式中,它们都可以被正确地预渲染。然而,它们之间存在一些区别。

    Markdown 语法 [[toc]] 仅能在 Markdown 文件中使用。它是由 markdown-it 解析的,生成的目录是静态内容。

    组件 <Toc/> 既可以用在 Markdown 文件中,也可以用在 Vue 文件中。它是由 Vue 加载的,生成的目录是一个 Vue 组件。

    该插件可以和 @vuepress/plugin-active-header-links 协同工作,你只需要将 headerLinkSelector 与该插件的 linkClass 匹配即可。当页面滚动至某个标题锚点后,对应的链接就会被加上 linkActiveClass 类名。

    因此,该插件对于主题开发者来说更为有用。

    配置项

    componentName

    • 类型: string

    • 默认值: 'Toc'

    • 详情:

      指定目录组件的名称。

    defaultPropsOptions

    • 类型: Partial<TocPropsOptions>

    • 默认值: {}

    • 详情:

      覆盖组件 options Prop 的默认值。

    组件 Props

    目录组件可以通过 Props 来进行自定义。

    <template>
    +  <Toc :headers="headers" :options="options" />
    +</template>
    +

    headers

    • 类型: PageHeader[]
    interface PageHeader {
    +  level: number
    +  title: string
    +  slug: string
    +  children: PageHeader[]
    +}
    +
    • 详情:

      指定要渲染的标题数组。

      如果该 Prop 没有被设置,默认会使用当前页面的标题。

    options

    • 类型: Partial<TocPropsOptions>
    interface TocPropsOptions {
    +  containerTag: string
    +  containerClass: string
    +  listClass: string
    +  itemClass: string
    +  linkTag: 'a' | 'RouterLink' | 'RouteLink'
    +  linkClass: string
    +  linkActiveClass: string
    +  linkChildrenActiveClass: string
    +}
    +
    const defaultOptions = {
    +  containerTag: 'nav',
    +  containerClass: 'vuepress-toc',
    +  listClass: 'vuepress-toc-list',
    +  itemClass: 'vuepress-toc-item',
    +  linkTag: 'RouteLink',
    +  linkClass: 'vuepress-toc-link',
    +  linkActiveClass: 'active',
    +  linkChildrenActiveClass: 'active',
    +}
    +
    • 详情:

      自定义目录组件。

      如果 containerTag 设置为空字符串 '' ,那么最外层的 <nav> Container 会被完全移除。

    • 示例:

      使用默认 options 的目录组件的渲染结果类似以下结构:

    <template>
    +  <!-- container -->
    +  <nav class="vuepress-toc">
    +    <!-- list -->
    +    <ul class="vuepress-toc-list">
    +      <!-- item -->
    +      <li class="vuepress-toc-item">
    +        <!-- link -->
    +        <RouteLink class="vuepress-toc-link" to="#foo">Foo</RouteLink>
    +      </li>
    +      <!-- item with children -->
    +      <li class="vuepress-toc-item">
    +        <!-- link (children active) -->
    +        <RouteLink class="vuepress-toc-link active" to="#bar">Bar</RouteLink>
    +        <!-- list (children) -->
    +        <ul class="vuepress-toc-list">
    +          <!-- item -->
    +          <li class="vuepress-toc-item">
    +            <!-- link (active) -->
    +            <RouteLink class="vuepress-toc-link active" to="#bar-child">
    +              Bar Child
    +            </RouteLink>
    +          </li>
    +        </ul>
    +      </li>
    +    </ul>
    +  </nav>
    +</template>
    +
    + + + diff --git a/zh/themes/default/components.html b/zh/themes/default/components.html new file mode 100644 index 0000000000..7ce4951888 --- /dev/null +++ b/zh/themes/default/components.html @@ -0,0 +1,68 @@ + + + + + + + + + 内置组件 | VuePress 生态系统 + + + + + +

    内置组件

    @vuepress/theme-default

    Badge badge

    • Props:

      • type
        • 类型: 'tip' | 'warning' | 'danger'
        • 默认值: 'tip'
      • text
        • 类型: string
        • 默认值: ''
      • vertical
        • 类型: 'top' | 'middle' | 'bottom' | undefined
        • 默认值: undefined
    • 示例:

    输入

    - VuePress - <Badge type="tip" text="v2" vertical="top" />
    +- VuePress - <Badge type="warning" text="v2" vertical="middle" />
    +- VuePress - <Badge type="danger" text="v2" vertical="bottom" />
    +

    输出

    • VuePress - v2
    • VuePress - v2
    • VuePress - v2

    CodeGroup

    CodeGroupItem

    • Props:

      • title
        • 类型: string
        • 是否必需: true
      • active
        • 类型: boolean
        • 默认值: false
    • 详情:

      该组件必须放置在 CodeGroup 组件的内部。

      可以通过 active Prop 来设置初始激活的元素。如果不设置,默认激活第一个元素。

    • 示例:

    输入

    <CodeGroup>
    +  <CodeGroupItem title="pnpm">
    +
    +```bash:no-line-numbers
    +pnpm install
    +```
    +
    +  </CodeGroupItem>
    +
    +  <CodeGroupItem title="yarn">
    +
    +```bash:no-line-numbers
    +yarn install
    +```
    +
    +  </CodeGroupItem>
    +
    +  <CodeGroupItem title="npm" active>
    +
    +```bash:no-line-numbers
    +npm install
    +```
    +
    +  </CodeGroupItem>
    +</CodeGroup>
    +

    输出

    pnpm install
    +
    yarn install
    +
    npm install
    +

    注意

    你必须在 <CodeGroupItem> 的开始标签和代码块之间添加一个空行,否则代码块无法被 Markdown 正确解析。

    所有内容首先都必须是合法的 Markdown ,然后才是一个 Vue SFC 。

    了解更多: Cookbook > Markdown 与 Vue SFC在新窗口打开

    或者你可以选择使用 自定义容器

    + + + diff --git a/zh/themes/default/config.html b/zh/themes/default/config.html new file mode 100644 index 0000000000..e5956b0fc8 --- /dev/null +++ b/zh/themes/default/config.html @@ -0,0 +1,177 @@ + + + + + + + + + 配置 | VuePress 生态系统 + + + + + +

    配置

    基础配置

    hostname

    • 类型: string

    • 详情:

      部署的域名,例如 https://example.com

    locales

    Locale 配置

    该章节内的配置项可以作为一般配置使用,也可以使用在 locales 内。

    colorMode

    colorModeSwitch

    home

    • 类型: string

    • 默认值: /

    • 详情:

      首页的路径。

      它将被用于:

      • 导航栏中 Logo 的链接
      • 404 页面的 返回首页 链接
    • 类型: false | (NavbarItem | NavbarGroup | string)[]

    • 默认值: []

    • 详情:

      导航栏配置。

      设置为 false 可以禁用导航栏。

      为了配置导航栏元素,你可以将其设置为 导航栏数组 ,其中的每个元素是 NavbarItem 对象、 NavbarGroup 对象、或者字符串:

      • NavbarItem 对象应该有一个 text 字段和一个 link 字段,还有一个可选的 activeMatch 字段。
      • NavbarGroup 对象应该有一个 text 字段和一个 children 字段。 children 字段同样是一个 导航栏数组
      • 字符串应为目标页面文件的路径。它将会被转换为 NavbarItem 对象,将页面标题作为 text ,将页面路由路径作为 link
    • 示例 1:

    export default {
    +  theme: defaultTheme({
    +    navbar: [
    +      // NavbarItem
    +      {
    +        text: 'Foo',
    +        link: '/foo/',
    +      },
    +      // NavbarGroup
    +      {
    +        text: 'Group',
    +        children: ['/group/foo.md', '/group/bar.md'],
    +      },
    +      // 字符串 - 页面文件路径
    +      '/bar/README.md',
    +    ],
    +  }),
    +}
    +
    • 示例 2:
    export default {
    +  theme: defaultTheme({
    +    navbar: [
    +      // 嵌套 Group - 最大深度为 2
    +      {
    +        text: 'Group',
    +        children: [
    +          {
    +            text: 'SubGroup',
    +            children: ['/group/sub/foo.md', '/group/sub/bar.md'],
    +          },
    +        ],
    +      },
    +      // 控制元素何时被激活
    +      {
    +        text: 'Group 2',
    +        children: [
    +          {
    +            text: 'Always active',
    +            link: '/',
    +            // 该元素将一直处于激活状态
    +            activeMatch: '/',
    +          },
    +          {
    +            text: 'Active on /foo/',
    +            link: '/not-foo/',
    +            // 该元素在当前路由路径是 /foo/ 开头时激活
    +            // 支持正则表达式
    +            activeMatch: '^/foo/',
    +          },
    +        ],
    +      },
    +    ],
    +  }),
    +}
    +
    • 类型: null | string

    • 详情:

      Logo 图片的 URL。

      Logo 图片将会显示在导航栏的左端。

      设置为 null 可以禁用 Logo 。

    • 示例:

    export default {
    +  theme: defaultTheme({
    +    // Public 文件路径
    +    logo: '/images/hero.png',
    +    // URL
    +    logo: 'https://vuejs.org/images/logo.png',
    +  }),
    +}
    +

    logoDark

    • 类型: null | string

    • 详情:

      在夜间模式中使用的 Logo 图片的 URL。

      如果你想在夜间模式中使用不同的 Logo 图片,就可以使用该配置项。

      设置为 null 可以在夜间模式下禁用 Logo 。忽略该配置项将会在夜间模式中使用 logo 配置。

    • 参考:

    logoAlt

    • 类型:null | string

    • 详情:

      指定 Logo 图片的替代文字。

      当未指定时,将默认与站点标题相同。

    repo

    • 类型: string

    • 详情:

      项目仓库的 URL。

      它将被用作 仓库链接 的链接。仓库链接 将会显示为导航栏的最后一个元素。

    export default {
    +  theme: defaultTheme({
    +    // 如果你按照 `organization/repository` 的格式设置它
    +    // 我们会将它作为一个 GitHub 仓库
    +    repo: 'vuejs/vuepress',
    +    // 你也可以直接将它设置为一个 URL
    +    repo: 'https://gitlab.com/foo/bar',
    +  }),
    +}
    +
    • 类型: false | 'auto' | SidebarConfigArray | SidebarConfigObject

    • 默认值: 'auto'

    • 详情:

      侧边栏配置。

      你可以通过页面的 sidebar frontmatter 来覆盖这个全局配置。

      设置为 false 可以禁用侧边栏。

      如果你设置为 'auto',侧边栏会根据页面标题自动生成。

      为了手动配置侧边栏元素,你可以将其设置为 侧边栏数组 ,其中的每个元素是一个 SidebarItem 对象或者一个字符串:

      • SidebarItem 对象应该有一个 text 字段,有一个可选的 link 字段、一个可选的 children 字段和一个可选的 collapsible 字段。 children 字段同样是一个 侧边栏数组collapsible 字段来控制它是否可折叠。
      • 字符串应为目标页面文件的路径。它将会被转换为 SidebarItem 对象,将页面标题作为 text ,将页面路由路径作为 link ,并根据页面小标题自动生成 children

      如果你想在不同子路径中使用不同的侧边栏,你可以将该配置项设置为 侧边栏对象

      • Key 为路径前缀。
      • Value 为 侧边栏数组"heading" 以自动为相应路径生成基于标题的侧边栏。
    • 示例 1:

    export default {
    +  theme: defaultTheme({
    +    // 侧边栏数组
    +    // 所有页面会使用相同的侧边栏
    +    sidebar: [
    +      // SidebarItem
    +      {
    +        text: 'Foo',
    +        link: '/foo/',
    +        children: [
    +          // SidebarItem
    +          {
    +            text: 'github',
    +            link: 'https://github.com',
    +            children: [],
    +          },
    +          // 字符串 - 页面文件路径
    +          '/foo/bar.md',
    +        ],
    +      },
    +      // 字符串 - 页面文件路径
    +      '/bar/README.md',
    +    ],
    +  }),
    +}
    +
    • 示例 2:
    export default {
    +  theme: defaultTheme({
    +    // 侧边栏对象
    +    // 不同子路径下的页面会使用不同的侧边栏
    +    sidebar: {
    +      '/guide/': [
    +        {
    +          text: 'Guide',
    +          children: ['/guide/introduction.md', '/guide/getting-started.md'],
    +        },
    +      ],
    +      '/reference/': 'heading',
    +    },
    +  }),
    +}
    +
    • 示例 3:
    export default {
    +  theme: defaultTheme({
    +    // 可折叠的侧边栏
    +    sidebar: {
    +      '/reference/': [
    +        {
    +          text: 'VuePress Reference',
    +          collapsible: true,
    +          children: ['/reference/cli.md', '/reference/config.md'],
    +        },
    +        {
    +          text: 'Bundlers Reference',
    +          collapsible: true,
    +          children: [
    +            '/reference/bundler/vite.md',
    +            '/reference/bundler/webpack.md',
    +          ],
    +        },
    +      ],
    +    },
    +  }),
    +}
    +

    sidebarDepth

    • 类型: number

    • 默认值: 2

    • 详情:

      设置根据页面标题自动生成的侧边栏的最大深度。

      • 设为 0 来禁用所有级别的页面标题。
      • 设为 1 来包含 <h2> 标题。
      • 设为 2 来包含 <h2><h3> 标题。
      • ...

      最大值取决于你通过 markdown.headers.level在新窗口打开 提取了哪些级别的标题。

      由于 markdown.headers.level 的默认值是 [2, 3] ,因此 sidebarDepth 的默认最大值是 2

      你可以通过页面的 sidebarDepth frontmatter 来覆盖这个全局配置。

    • 类型: boolean

    • 默认值: true

    • 详情:

      是否启用 编辑此页 链接。

      你可以通过页面的 editLink frontmatter 来覆盖这个全局配置。

    editLinkPattern

    • 类型: string

    • 详情:

      编辑此页 链接的 Pattern 。

      它将会用于生成 编辑此页 的链接。

      如果你不设置该选项,则会根据 docsRepo 配置项来推断 Pattern 。但是如果你的文档仓库没有托管在常用的平台上,比如 GitHub 、 GitLab 、 Bitbucket 、 Gitee 等,那么你必须设置该选项才能使 编辑此页 链接正常工作。

    • 用法:

      Pattern描述
      :repo文档仓库 URL ,即 docsRepo
      :branch文档仓库分支 ,即 docsBranch
      :path页面源文件的路径,即 docsDir 拼接上页面文件的相对路径
    • 示例:

    export default {
    +  theme: defaultTheme({
    +    docsRepo: 'https://gitlab.com/owner/name',
    +    docsBranch: 'master',
    +    docsDir: 'docs',
    +    editLinkPattern: ':repo/-/edit/:branch/:path',
    +  }),
    +}
    +

    则会生成类似于 'https://gitlab.com/owner/name/-/edit/master/docs/path/to/file.md' 的链接。

    docsRepo

    • 类型: string

    • 详情:

      文档源文件的仓库 URL 。

      它将会用于生成 编辑此页 的链接。

      如果你不设置该选项,则默认会使用 repo 配置项。但是如果你的文档源文件是在一个不同的仓库内,你就需要设置该配置项了。

    docsBranch

    • 类型: string

    • 默认值: 'main'

    • 详情:

      文档源文件的仓库分支。

      它将会用于生成 编辑此页 的链接。

    docsDir

    • 类型: string

    • 默认值: ''

    • 详情:

      文档源文件存放在仓库中的目录名。

      它将会用于生成 编辑此页 的链接。

    lastUpdated

    • 类型: boolean

    • 默认值: true

    • 详情:

      是否启用 最近更新时间戳

      你可以通过页面的 lastUpdated frontmatter 来覆盖这个全局配置。要注意的是,如果你已经将该选项设为了 false ,那么这个功能会被完全禁用,并且无法在 locales 或页面 frontmatter 中启用。

    contributors

    • 类型: boolean

    • 默认值: true

    • 详情:

      是否启用 贡献者列表

      你可以通过页面的 contributors frontmatter 来覆盖这个全局配置。要注意的是,如果你已经将该选项设为了 false ,那么这个功能会被完全禁用,并且无法在 locales 或页面 frontmatter 中启用。

    + + + diff --git a/zh/themes/default/extending.html b/zh/themes/default/extending.html new file mode 100644 index 0000000000..100b569ed2 --- /dev/null +++ b/zh/themes/default/extending.html @@ -0,0 +1,101 @@ + + + + + + + + + 继承 | VuePress 生态系统 + + + + + +

    继承

    VuePress 默认主题有着大量的用户,因此我们对它进行了一些便于继承的设计,以便用户轻松进行定制化。

    布局插槽

    默认主题的 Layout 布局提供了一些插槽:

    • navbar
    • navbar-before
    • navbar-after
    • sidebar
    • sidebar-top
    • sidebar-bottom
    • page
    • page-top
    • page-bottom
    • page-content-top
    • page-content-bottom

    在它们的帮助下,你可以很容易地添加或替换内容。下面通过一个示例来介绍一下如何使用布局插槽来继承默认主题。

    首先,创建一个客户端配置文件 .vuepress/client.ts

    import { defineClientConfig } from 'vuepress/client'
    +import Layout from './layouts/Layout.vue'
    +
    +export default defineClientConfig({
    +  layouts: {
    +    Layout,
    +  },
    +})
    +

    接下来,创建 .vuepress/layouts/Layout.vue ,并使用由默认主题的 Layout 布局提供的插槽:

    <script setup>
    +import ParentLayout from '@vuepress/theme-default/layouts/Layout.vue'
    +</script>
    +
    +<template>
    +  <ParentLayout>
    +    <template #page-bottom>
    +      <div class="my-footer">This is my custom page footer</div>
    +    </template>
    +  </ParentLayout>
    +</template>
    +
    +<style lang="css">
    +.my-footer {
    +  text-align: center;
    +}
    +</style>
    +

    此时默认的 Layout 布局已经被你的本地布局覆盖,将会在除了首页外的所有页面添加一个自定义的页脚:

    extending-a-theme

    组件替换

    布局插槽十分实用,但有时候你可能会觉得它不够灵活。默认主题同样提供了替换单个组件的能力。

    默认主题将所有 非全局的组件在新窗口打开 都注册了一个带 @theme 前缀的 alias在新窗口打开 。例如,HomeFooter.vue 的别名是 @theme/HomeFooter.vue

    接下来,如果你想要替换 HomeFooter.vue 组件,只需要在配置文件 .vuepress/config.ts 中覆盖这个别名即可:

    import { defaultTheme } from '@vuepress/theme-default'
    +import { getDirname, path } from 'vuepress/utils'
    +import { defineUserConfig } from 'vuepress'
    +
    +const __dirname = getDirname(import.meta.url)
    +
    +export default defineUserConfig({
    +  theme: defaultTheme(),
    +  alias: {
    +    '@theme/HomeFooter.vue': path.resolve(
    +      __dirname,
    +      './components/MyHomeFooter.vue',
    +    ),
    +  },
    +})
    +

    开发一个子主题

    除了在 .vuepress/config.ts.vuepress/client.ts 中直接扩展默认主题以外,你可以通过继承默认主题来开发一个你自己的主题:

    import { defaultTheme, type DefaultThemeOptions } from '@vuepress/theme-default'
    +import type { Theme } from 'vuepress/core'
    +import { getDirname, path } from 'vuepress/utils'
    +
    +const __dirname = getDirname(import.meta.url)
    +
    +export const childTheme = (options: DefaultThemeOptions): Theme => {
    +  return {
    +    name: 'vuepress-theme-child',
    +    extends: defaultTheme(options),
    +
    +    // 在子主题的客户端配置文件中覆盖布局
    +    // 注意,你在发布到 NPM 之前会将 TS 构建为 JS ,因此这里需要设置为 JS 文件的路径
    +    clientConfigFile: path.resolve(__dirname, './client.js'),
    +
    +    // 覆盖组件别名
    +    alias: {
    +      '@theme/HomeFooter.vue': path.resolve(
    +        __dirname,
    +        './components/MyHomeFooter.vue',
    +      ),
    +    },
    +  }
    +}
    +
    + + + diff --git a/zh/themes/default/frontmatter.html b/zh/themes/default/frontmatter.html new file mode 100644 index 0000000000..20204d4495 --- /dev/null +++ b/zh/themes/default/frontmatter.html @@ -0,0 +1,96 @@ + + + + + + + + + Frontmatter | VuePress 生态系统 + + + + + +

    Frontmatter

    @vuepress/theme-default

    所有页面

    本章节中的 Frontmatter 会在所有类型的页面中生效。

    externalLinkIcon

    • 类型: boolean

    • 详情:

      是否在当前页面展示导航栏。

      如果你在主题配置中禁用了导航栏,那么该 Frontmatter 将不会生效。

    • 参考:

    pageClass

    • 类型: string

    • 详情:

      为当前页面添加额外的类名。

    • 示例:

    ---
    +pageClass: custom-page-class
    +---
    +

    然后你可以在 .vuepress/styles/index.scss 文件中为这个页面添加自定义样式:

    .theme-container.custom-page-class {
    +  /* 页面样式 */
    +}
    +

    首页

    本章节中的 Frontmatter 只会在首页中生效。

    home

    • 类型: boolean

    • 详情:

      设定该页面是首页还是普通页面。

      如果你不设置该 Frontmatter 或将其设为 false ,则该页面会是一个 普通页面

    • 示例:

      ---
      +home: true
      +---
      +

    heroImage

    heroImageDark

    heroAlt

    • 类型: string

    • 详情:

      首页图片的 alt 属性。

      如果不设置,则默认使用 heroText

    heroHeight

    • 类型: number

    • 默认值: 280

    • 详情:

      首页图片 <img> 标签的 height 属性。

      当你的首页图片高度小于默认值时,你可能需要减小该属性。

      需要注意的是,首页图片的高度同样受到了 CSS 的约束。设置这个属性主要是为了减少由加载首页图片引起的 累积布局偏移 (CLS)在新窗口打开

    heroText

    • 类型: string | null

    • 详情:

      首页的大标题。

      如果不设置,则默认使用站点 title在新窗口打开

      设置为 null 来禁用首页大标题。

    tagline

    • 类型: string | null

    • 详情:

      首页的标语。

      如果不设置,则默认使用站点 description在新窗口打开

      设置为 null 来禁用首页标语。

    actions

    • 类型:
    Array<{
    +  text: string
    +  link: string
    +  type?: 'primary' | 'secondary'
    +}>
    +
    • 详情:

      配置首页按钮。

    • 示例:

    ---
    +actions:
    +  - text: 快速上手
    +    link: /zh/guide/getting-started.html
    +    type: primary
    +  - text: 项目简介
    +    link: /zh/guide/introduction.html
    +    type: secondary
    +---
    +

    features

    • 类型:
    Array<{
    +  title: string
    +  details: string
    +}>
    +
    • 详情:

      配置首页特性列表。

    • 示例:

    ---
    +features:
    +  - title: 简洁至上
    +    details: 以 Markdown 为中心的项目结构,以最少的配置帮助你专注于写作。
    +  - title: Vue 驱动
    +    details: 享受 Vue 的开发体验,可以在 Markdown 中使用 Vue 组件,又可以使用 Vue 来开发自定义主题。
    +  - title: 高性能
    +    details: VuePress 会为每个页面预渲染生成静态的 HTML,同时,每个页面被加载的时候,将作为 SPA 运行。
    +---
    +
    • 类型: string

    • 详情:

      首页的页脚。

    footerHtml

    • 类型: boolean

    • 详情:

      是否允许页脚中使用 HTML 。

      如果设置为 true ,那么 footer 会被作为 HTML 代码处理。

    普通页面

    本章节中的 Frontmatter 只会在普通页面中生效。

    editLinkPattern

    lastUpdated

    contributors

    sidebarDepth

    prev

    • 类型: NavLink | string

    • 详情:

      上一个页面的链接。

      如果你不设置该 Frontmatter ,该链接会自动根据侧边栏配置进行推断。

      为了手动配置上一页面的链接,你可以将其设置为一个 NavLink 对象或者一个字符串:

      • NavLink 对象应该有一个 text 字段和一个 link 字段。
      • 字符串应为目标页面文件的路径。它将会被转换为 NavLink 对象,将页面标题作为 text ,将页面路由路径作为 link
    • 示例:

    ---
    +# NavLink
    +prev:
    +  text: Get Started
    +  link: /guide/getting-started.html
    +
    +# NavLink - 外部 URL
    +prev:
    +  text: GitHub
    +  link: https://github.com
    +
    +# 字符串 - 页面文件路径
    +prev: /guide/getting-started.md
    +
    +# 字符串 - 页面文件相对路径
    +prev: ../../guide/getting-started.md
    +---
    +

    next

    • 类型: NavLink | string

    • 详情:

      下一个页面的链接。

      如果你不设置该 Frontmatter ,该链接会自动根据侧边栏配置进行推断。

      类型和 prev Frontmatter 相同。

    + + + diff --git a/zh/themes/default/index.html b/zh/themes/default/index.html new file mode 100644 index 0000000000..4d83df7f5e --- /dev/null +++ b/zh/themes/default/index.html @@ -0,0 +1,45 @@ + + + + + + + + + 默认主题 | VuePress 生态系统 + + + + + +

    默认主题

    使用方法

    @vuepress/theme-default

    安装默认主题:

    npm i -D @vuepress/theme-default@next
    +

    在配置文件中指定主题:

    import { defaultTheme } from '@vuepress/theme-default'
    +
    +export default {
    +  theme: defaultTheme({
    +    // 在这里添加主题配置
    +  }),
    +}
    +
    + + + diff --git a/zh/themes/default/locale.html b/zh/themes/default/locale.html new file mode 100644 index 0000000000..b820663a8f --- /dev/null +++ b/zh/themes/default/locale.html @@ -0,0 +1,57 @@ + + + + + + + + + 语言配置 | VuePress 生态系统 + + + + + +

    语言配置

    这些选项用于配置与语言相关的文本。

    如果你的站点是以英语以外的其他语言提供服务的,你应该为每个语言设置这些选项来提供翻译。

    repoLabel

    • 类型: string

    • 详情:

      项目仓库的标签。

      它将被用作 仓库链接 的文字。仓库链接 将会显示为导航栏的最后一个元素。

      如果你不明确指定该配置项,它将会根据 repo 配置项自动推断。

    selectLanguageText

    • 类型: string

    • 详情:

      选择语言菜单 的文字。

      如果你在站点配置中设置了多个 locales在新窗口打开 ,那么 选择语言菜单 就会显示在导航栏中仓库按钮的旁边。

    selectLanguageAriaLabel

    • 类型: string

    • 详情:

      选择语言菜单aria-label 属性。

      它主要是为了站点的可访问性 (a11y) 。

    selectLanguageName

    • 类型: string

    • 详情:

      Locale 的语言名称。

      该配置项 仅能在主题配置的 locales 的内部生效 。它将被用作 locale 的语言名称,展示在 选择语言菜单 内。

    • 示例:

    export default {
    +  locales: {
    +    '/': {
    +      lang: 'en-US',
    +    },
    +    '/zh/': {
    +      lang: 'zh-CN',
    +    },
    +  },
    +  theme: defaultTheme({
    +    locales: {
    +      '/': {
    +        selectLanguageName: 'English',
    +      },
    +      '/zh/': {
    +        selectLanguageName: '简体中文',
    +      },
    +    },
    +  }),
    +}
    +
    • 类型:null | string

    • 详情:

      导航栏中主导航 aria-label 属性的值。

    • 类型:null | string

    • 详情:

      下一页/上一页导航 aria-label 属性的值

    editLinkText

    • 类型: string

    • 默认值: 'Edit this page'

    • 详情:

      编辑此页 链接的文字。

    lastUpdatedText

    • 类型: string

    • 默认值: 'Last Updated'

    • 详情:

      最近更新时间戳 标签的文字。

    contributorsText

    • 类型: string

    • 默认值: 'Contributors'

    • 详情:

      贡献者列表 标签的文字。

    tip

    • 类型: string

    • 默认值: 'TIP'

    • 详情:

      Tip 自定义容器 的默认标题。

    warning

    • 类型: string

    • 默认值: 'WARNING'

    • 详情:

      Warning 自定义容器 的默认标题。

    danger

    • 类型: string

    • 默认值: 'DANGER'

    • 详情:

      Danger 自定义容器 的默认标题。

    notFound

    • 类型: string[]

    • 默认值: ['Not Found']

    • 详情:

      404 页面的提示信息。

      当用户进入 404 页面时,会从数组中随机选取一条信息进行展示。

    backToHome

    • 类型: string

    • 默认值: 'Back to home'

    • 详情:

      404 页面中 返回首页 链接的文字。

    openInNewWindow

    toggleColorMode

    • 类型: string

    • 默认值: 'toggle color mode'

    • 详情:

      切换颜色模式按钮的标题文字。

      它主要是为了站点的可访问性 (a11y) 。

    • 参考:

    toggleSidebar

    • 类型: string

    • 默认值: 'toggle sidebar'

    • 详情:

      切换侧边栏按钮的标题文字。

      它主要是为了站点的可访问性 (a11y) 。

    + + + diff --git a/zh/themes/default/markdown.html b/zh/themes/default/markdown.html new file mode 100644 index 0000000000..b7eab4c935 --- /dev/null +++ b/zh/themes/default/markdown.html @@ -0,0 +1,86 @@ + + + + + + + + + Markdown | VuePress 生态系统 + + + + + +

    Markdown

    @vuepress/theme-default

    自定义容器

    • 使用:

      ::: <type> [title]
      +[content]
      +:::
      +

      type 是必需的, titlecontent 是可选的。

      支持的 type 有:

    • 示例 1 (默认标题):

    输入

    ::: tip
    +这是一个提示
    +:::
    +
    +::: warning
    +这是一个警告
    +:::
    +
    +::: danger
    +这是一个危险警告
    +:::
    +
    +::: details
    +这是一个 details 标签
    +:::
    +

    输出

    提示

    这是一个提示

    注意

    这是一个警告

    警告

    这是一个危险警告

    这是一个 details 标签

    • 示例 2 (自定义标题):

    输入

    ::: danger STOP
    +危险区域,禁止通行
    +:::
    +
    +::: details 点击查看代码
    +
    +```ts
    +console.log('你好,VuePress!')
    +```
    +
    +:::
    +

    输出

    STOP

    危险区域,禁止通行

    点击查看代码
    console.log('你好,VuePress!')
    +
    • 示例 3 (Code Group 别名):

    输入

    :::: code-group
    +::: code-group-item FOO
    +
    +```ts
    +const foo = 'foo'
    +```
    +
    +:::
    +
    +::: code-group-item BAR
    +
    +```ts
    +const bar = 'bar'
    +```
    +
    +:::
    +::::
    +

    输出

    const foo = 'foo'
    +
    const bar = 'bar'
    +
    + + + diff --git a/zh/themes/default/plugin.html b/zh/themes/default/plugin.html new file mode 100644 index 0000000000..c4960f15ed --- /dev/null +++ b/zh/themes/default/plugin.html @@ -0,0 +1,46 @@ + + + + + + + + + 插件配置 | VuePress 生态系统 + + + + + +

    插件配置

    你可以通过 themePlugins 设置默认主题使用的插件。

    默认主题使用了一些插件,如果你确实不需要该插件,你可以选择禁用它。在禁用插件之前,请确保你已了解它的用途。

    import { defaultTheme } from '@vuepress/theme-default'
    +
    +export default {
    +  theme: defaultTheme({
    +    themePlugins: {
    +      // 在这里自定义主题插件
    +    },
    +  }),
    +}
    +

    themePlugins.backToTop

    • 类型: BackToTopPluginOptions | boolean

    • 默认值: true

    • 详情:

      是否启用 @vuepress/plugin-back-to-top

      支持对象格式以作为插件选项。

    themePlugins.container

    themePlugins.copyCode

    • 类型: CopyCodePluginOptions | boolean

    • 默认值: true

    • 详情:

      是否启用 @vuepress/plugin-copy-code

      支持对象格式以作为插件选项。

    themePlugins.externalLinkIcon

    themePlugins.git

    themePlugins.mediumZoom

    themePlugins.nprogress

    themePlugins.seo

    • 类型: SeoPluginOptions | boolean

    • 默认值: true

    • 详情:

      是否启用 @vuepress/plugin-seo

      支持对象格式以作为插件选项。

    themePlugins.sitemap

    • 类型: SitemapPluginOptions | boolean

    • 默认值: true

    • 详情:

      是否启用 @vuepress/plugin-sitemap

      支持对象格式以作为插件选项。

    + + + diff --git a/zh/themes/default/styles.html b/zh/themes/default/styles.html new file mode 100644 index 0000000000..22e07a6689 --- /dev/null +++ b/zh/themes/default/styles.html @@ -0,0 +1,299 @@ + + + + + + + + + 样式 | VuePress 生态系统 + + + + + +

    样式

    @vuepress/theme-default

    默认主题使用 SASS在新窗口打开 作为 CSS 预处理器。

    用户可以通过 palette 文件 来自定义样式变量,还可以通过 style 文件 来添加额外的样式。

    Palette 文件

    Palette 文件的路径是 .vuepress/styles/palette.scss

    你可以利用它来覆盖默认主题的预定义 SASS 变量。

    点击查看 SASS 变量
    // responsive breakpoints
    +$MQNarrow: 959px !default;
    +$MQMobile: 719px !default;
    +$MQMobileNarrow: 419px !default;
    +

    Style 文件

    Style 文件的路径是 .vuepress/styles/index.scss

    你可以在这里添加额外的样式,或者覆盖默认样式:

    :root {
    +  scroll-behavior: smooth;
    +}
    +

    你也可以利用它来覆盖默认主题的预定义 CSS 变量。

    点击查看 CSS 变量
    :root {
    +  // brand colors
    +  --c-brand: #3eaf7c;
    +  --c-brand-light: #4abf8a;
    +
    +  // background colors
    +  --c-bg: #ffffff;
    +  --c-bg-light: #f3f4f5;
    +  --c-bg-lighter: #eeeeee;
    +  --c-bg-dark: #ebebec;
    +  --c-bg-darker: #e6e6e6;
    +  --c-bg-navbar: var(--c-bg);
    +  --c-bg-sidebar: var(--c-bg);
    +  --c-bg-arrow: #cccccc;
    +
    +  // text colors
    +  --c-text: #2c3e50;
    +  --c-text-accent: var(--c-brand);
    +  --c-text-light: #3a5169;
    +  --c-text-lighter: #4e6e8e;
    +  --c-text-lightest: #6a8bad;
    +  --c-text-quote: #999999;
    +
    +  // border colors
    +  --c-border: #eaecef;
    +  --c-border-dark: #dfe2e5;
    +
    +  // custom container colors
    +  --c-tip: #42b983;
    +  --c-tip-bg: var(--c-bg-light);
    +  --c-tip-title: var(--c-text);
    +  --c-tip-text: var(--c-text);
    +  --c-tip-text-accent: var(--c-text-accent);
    +  --c-warning: #ffc310;
    +  --c-warning-bg: #fffae3;
    +  --c-warning-bg-light: #fff3ba;
    +  --c-warning-bg-lighter: #fff0b0;
    +  --c-warning-border-dark: #f7dc91;
    +  --c-warning-details-bg: #fff5ca;
    +  --c-warning-title: #f1b300;
    +  --c-warning-text: #746000;
    +  --c-warning-text-accent: #edb100;
    +  --c-warning-text-light: #c1971c;
    +  --c-warning-text-quote: #ccab49;
    +  --c-danger: #f11e37;
    +  --c-danger-bg: #ffe0e0;
    +  --c-danger-bg-light: #ffcfde;
    +  --c-danger-bg-lighter: #ffc9c9;
    +  --c-danger-border-dark: #f1abab;
    +  --c-danger-details-bg: #ffd4d4;
    +  --c-danger-title: #ed1e2c;
    +  --c-danger-text: #660000;
    +  --c-danger-text-accent: #bd1a1a;
    +  --c-danger-text-light: #b5474d;
    +  --c-danger-text-quote: #c15b5b;
    +  --c-details-bg: #eeeeee;
    +
    +  // badge component colors
    +  --c-badge-tip: var(--c-tip);
    +  --c-badge-warning: #ecc808;
    +  --c-badge-warning-text: var(--c-bg);
    +  --c-badge-danger: #dc2626;
    +  --c-badge-danger-text: var(--c-bg);
    +
    +  // code group colors
    +  --c-code-group-tab-title: rgba(255, 255, 255, 0.9);
    +  --c-code-group-tab-bg: var(--code-bg-color);
    +  --c-code-group-tab-outline: var(var(--c-code-group-tab-title));
    +  --c-code-group-tab-active-border: var(--c-brand);
    +
    +  // transition vars
    +  --t-color: 0.3s ease;
    +  --t-transform: 0.3s ease;
    +
    +  // code blocks vars
    +  --code-bg-color: #282c34;
    +  --code-hl-bg-color: rgba(0, 0, 0, 0.66);
    +  --code-ln-color: #9e9e9e;
    +  --code-ln-wrapper-width: 3.5rem;
    +
    +  // font vars
    +  --font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen,
    +    Ubuntu, Cantarell, 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif;
    +  --font-family-code: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace;
    +
    +  // layout vars
    +  --navbar-height: 3.6rem;
    +  --navbar-padding-v: 0.7rem;
    +  --navbar-padding-h: 1.5rem;
    +  --sidebar-width: 20rem;
    +  --sidebar-width-mobile: calc(var(--sidebar-width) * 0.82);
    +  --content-width: 740px;
    +  --homepage-width: 960px;
    +}
    +
    +// plugin-back-to-top
    +.vp-back-to-top-button {
    +  --back-to-top-color: var(--c-brand);
    +  --back-to-top-color-hover: var(--c-brand-light);
    +  --back-to-top-bg-color: var(--c-bg);
    +}
    +
    +// plugin-catalog
    +.vp-catalog-wrapper {
    +  --catalog-bg-color: var(--c-bg);
    +  --catalog-bg-secondary-color: var(--c-bg-dark);
    +  --catalog-border-color: var(--c-border);
    +  --catalog-active-color: var(--c-brand);
    +  --catalog-hover-color: var(--c-brand-light);
    +}
    +
    +// plugin-docsearch
    +.DocSearch {
    +  --docsearch-primary-color: var(--c-brand);
    +  --docsearch-text-color: var(--c-text);
    +  --docsearch-highlight-color: var(--c-brand);
    +  --docsearch-muted-color: var(--c-text-quote);
    +  --docsearch-container-background: rgba(9, 10, 17, 0.8);
    +  --docsearch-modal-background: var(--c-bg-light);
    +  --docsearch-searchbox-background: var(--c-bg-lighter);
    +  --docsearch-searchbox-focus-background: var(--c-bg);
    +  --docsearch-searchbox-shadow: inset 0 0 0 2px var(--c-brand);
    +  --docsearch-hit-color: var(--c-text-light);
    +  --docsearch-hit-active-color: var(--c-bg);
    +  --docsearch-hit-background: var(--c-bg);
    +  --docsearch-hit-shadow: 0 1px 3px 0 var(--c-border-dark);
    +  --docsearch-footer-background: var(--c-bg);
    +}
    +
    +// plugin-external-link-icon
    +.external-link-icon {
    +  --external-link-icon-color: var(--c-text-quote);
    +}
    +
    +// plugin-medium-zoom
    +.medium-zoom-overlay {
    +  --medium-zoom-bg-color: var(--c-bg);
    +}
    +
    +// plugin-nprogress
    +#nprogress {
    +  --nprogress-color: var(--c-brand);
    +}
    +
    +// plugin-photo-swipe
    +body {
    +  --photo-swipe-bullet: var(--c-bg);
    +  --photo-swipe-bullet-active: var(--c-brand);
    +}
    +
    +// plugin-pwa-popup
    +html {
    +  --pwa-text-color: var(--c-text);
    +  --pwa-bg-color: var(--c-bg);
    +  --pwa-border-color: var(--c-brand);
    +  --pwa-btn-text-color: var(--c-bg);
    +  --pwa-btn-bg-color: var(--c-brand);
    +  --pwa-btn-hover-bg-color: var(--c-brand-light);
    +}
    +
    +html.dark {
    +  --pwa-shadow-color: rgb(0 0 0 / 30%);
    +  --pwa-content-color: #ccc;
    +  --pwa-content-light-color: #999;
    +}
    +
    +// plugin-redirect
    +.language-modal-mask {
    +  --redirect-bg-color: var(--c-bg);
    +  --redirect-bg-color-light: var(--c-bg-light);
    +  --redirect-bg-color-lighter: var(--c-bg-lighter);
    +  --redirect-text-color: var(--c-text);
    +  --redirect-primary-bg-color: var(--c-brand);
    +  --redirect-primary-hover-bg-color: var(--c-brand-light);
    +  --redirect-primary-text-color: var(--c-bg);
    +}
    +
    +// plugin-search
    +.search-box {
    +  --search-bg-color: var(--c-bg);
    +  --search-accent-color: var(--c-brand);
    +  --search-text-color: var(--c-text);
    +  --search-border-color: var(--c-border);
    +
    +  --search-item-text-color: var(--c-text-lighter);
    +  --search-item-focus-bg-color: var(--c-bg-light);
    +}
    +
    点击查看暗黑模式 CSS 变量
    html.dark {
    +  // brand colors
    +  --c-brand: #3aa675;
    +  --c-brand-light: #349469;
    +
    +  // background colors
    +  --c-bg: #22272e;
    +  --c-bg-light: #2b313a;
    +  --c-bg-lighter: #262c34;
    +  --c-bg-dark: #343b44;
    +  --c-bg-darker: #37404c;
    +
    +  // text colors
    +  --c-text: #adbac7;
    +  --c-text-light: #96a7b7;
    +  --c-text-lighter: #8b9eb0;
    +  --c-text-lightest: #8094a8;
    +
    +  // border colors
    +  --c-border: #3e4c5a;
    +  --c-border-dark: #34404c;
    +
    +  // custom container colors
    +  --c-tip: #318a62;
    +  --c-warning: #e0ad15;
    +  --c-warning-bg: #2d2f2d;
    +  --c-warning-bg-light: #423e2a;
    +  --c-warning-bg-lighter: #44442f;
    +  --c-warning-border-dark: #957c35;
    +  --c-warning-details-bg: #39392d;
    +  --c-warning-title: #fdca31;
    +  --c-warning-text: #d8d96d;
    +  --c-warning-text-accent: #ffbf00;
    +  --c-warning-text-light: #ddb84b;
    +  --c-warning-text-quote: #ccab49;
    +  --c-danger: #fc1e38;
    +  --c-danger-bg: #39232c;
    +  --c-danger-bg-light: #4b2b35;
    +  --c-danger-bg-lighter: #553040;
    +  --c-danger-border-dark: #a25151;
    +  --c-danger-details-bg: #482936;
    +  --c-danger-title: #fc2d3b;
    +  --c-danger-text: #ea9ca0;
    +  --c-danger-text-accent: #fd3636;
    +  --c-danger-text-light: #d9777c;
    +  --c-danger-text-quote: #d56b6b;
    +  --c-details-bg: #323843;
    +
    +  // badge component colors
    +  --c-badge-warning: var(--c-warning);
    +  --c-badge-warning-text: #3c2e05;
    +  --c-badge-danger: var(--c-danger);
    +  --c-badge-danger-text: #401416;
    +
    +  // code blocks vars
    +  --code-hl-bg-color: #363b46;
    +}
    +
    +// plugin-docsearch
    +html.dark .DocSearch {
    +  --docsearch-logo-color: var(--c-text);
    +  --docsearch-modal-shadow: inset 1px 1px 0 0 #2c2e40, 0 3px 8px 0 #000309;
    +  --docsearch-key-shadow: inset 0 -2px 0 0 #282d55, inset 0 0 1px 1px #51577d,
    +    0 2px 2px 0 rgba(3, 4, 9, 0.3);
    +  --docsearch-key-gradient: linear-gradient(-225deg, #444950, #1c1e21);
    +  --docsearch-footer-shadow: inset 0 1px 0 0 rgba(73, 76, 106, 0.5),
    +    0 -4px 8px 0 rgba(0, 0, 0, 0.2);
    +}
    +
    + + + diff --git a/zh/themes/index.html b/zh/themes/index.html new file mode 100644 index 0000000000..ff5a8f9407 --- /dev/null +++ b/zh/themes/index.html @@ -0,0 +1,37 @@ + + + + + + + + + 主题 | VuePress 生态系统 + + + + + + + + + diff --git a/zh/tools/helper/client.html b/zh/tools/helper/client.html new file mode 100644 index 0000000000..6aa4ceaf3f --- /dev/null +++ b/zh/tools/helper/client.html @@ -0,0 +1,58 @@ + + + + + + + + + 客户端相关 | VuePress 生态系统 + + + + + +

    客户端相关

    可组合 API

    hasGlobalComponent

    检查组件是否已全局注册。

    提示

    1. 组件的局部导入不影响结果。
    2. 当在 setup 之外调用时,你需要将 app 实例作为第二个参数传递。
    export const hasGlobalComponent: (name: string, app?: App) => boolean
    +
    示例
    // 如果你全局注册了 `<my-component>`
    +hasGlobalComponent('MyComponent') // true
    +hasGlobalComponent('my-component') // true
    +
    +hasGlobalComponent('MyComponent2') // false
    +

    useLocaleConfig

    从语言环境设置中获取当前语言环境配置。

    export const useLocaleConfig: <T extends LocaleData>(
    +  localesConfig: RequiredLocaleConfig<T>,
    +) => ComputedRef<T>
    +
    示例
    const localesCOnfig = {
    +  '/': 'Title',
    +  '/zh/': '标题',
    +}
    +
    +const locale = useLocaleConfig(localesConfig)
    +
    +// under `/page`
    +locale.value // 'Title'
    +
    +// under `/zh/page`
    +locale.value // '标题'
    +
    + + + diff --git a/zh/tools/helper/index.html b/zh/tools/helper/index.html new file mode 100644 index 0000000000..3d9e4f03a6 --- /dev/null +++ b/zh/tools/helper/index.html @@ -0,0 +1,37 @@ + + + + + + + + + @vuepress/helper | VuePress 生态系统 + + + + + +

    @vuepress/helper

    @vuepress/helper

    此包为 VuePress 开发者提供辅助函数。

    + + + diff --git a/zh/tools/helper/node/bundler.html b/zh/tools/helper/node/bundler.html new file mode 100644 index 0000000000..2030f5432e --- /dev/null +++ b/zh/tools/helper/node/bundler.html @@ -0,0 +1,215 @@ + + + + + + + + + 打包器相关 | VuePress 生态系统 + + + + + +

    打包器相关

    打包器函数用于在主题和插件中追加或修改打包器选项。

    所有函数都应在 extendsBundlerOptions 生命周期挂钩中调用。

    提示

    我们在示例中省略了它。 实际代码应该是这样的:

    // 导入你需要的函数
    +import { addCustomElement } from '@vuepress/helper'
    +
    +export const yourPlugin = {
    +  // ...
    +  extendsBundlerOptions: (bundlerOptions, app) => {
    +    // 在此添加它们
    +    addCustomElement(bundlerOptions, app, 'my-custom-element')
    +  },
    +}
    +

    通用方法

    getBundlerName

    获取当前打包器的名称。

    export const getBundlerName: (app: App) => string
    +
    示例
    // @vuepress/bundler-vite
    +getBundleName(app) === 'vite' // true
    +// @vuepress/bundler-webpack
    +getBundleName(app) === 'webpack' // true
    +

    addCustomElement

    将自定义元素声明添加到当前的打包器。

    interface CustomElementCommonOptions {
    +  app: App;
    +  config: unknown;
    +}
    +/**
    + * Add tags as customElement
    + *
    + * @param bundlerOptions VuePress Bundler config
    + * @param app VuePress Node App
    + * @param customElements tags recognized as custom element
    + */
    +export const addCustomElement = (
    +  bundlerOptions: unknown,
    +  app: App,
    +  customElement: string[] | string | RegExp
    +) => void;
    +
    示例
    import { addCustomElement } from '@vuepress/helper'
    +
    +addCustomElement(bundlerConfig, app, 'my-custom-element')
    +addCustomElement(bundlerOptions, app, [
    +  'custom-element1',
    +  'custom-element2',
    +  // all tags start with `math-`
    +  /^math-/,
    +])
    +

    customizeDevServer

    为开发服务器中的特定路径提供内容。

    export interface DevServerOptions {
    +  /**
    +   * Path to be responded
    +   */
    +  path: string;
    +  /**
    +   * Respond function
    +   */
    +  response: (request?: IncomingMessage) => Promise<string | Buffer>;
    +
    +  /**
    +   * error msg
    +   */
    +  errMsg?: string;
    +}
    +
    +/**
    + * Handle specific path when running VuePress Dev Server
    + *
    + * @param bundlerOptions VuePress Bundler config
    + * @param app VuePress Node App
    + * @param path Path to be responded
    + * @param response respond function
    + * @param errMsg error msg
    + */
    +export const customizeDevServer: (
    +  bundlerOptions: unknown,
    +  app: App,
    +  {
    +    errMsg:"The server encountered an error",
    +    response: responseHandler,
    +    path,
    +  }: CustomServerOptions
    +) => void;
    +
    示例
    import { useCustomDevServer } from '@vuepress/helper'
    +
    +// handle `/api/` path
    +useCustomDevServer(bundlerOptions, app, {
    +  path: '/api/',
    +  response: async () => getData(),
    +  errMsg: 'Unexpected api error',
    +})
    +

    Vite 相关

    • addViteOptimizeDepsInclude

      向 Vite optimizeDeps.include 列表中添加模块

      提示

      如果一个包满足下列条件之一,你应该考虑将它添加至此。

      • 为 CJS 格式
      • 包的依赖包含 CJS 包
      • 包通过 import() 动态导入
    • addViteOptimizeDepsExclude

      向 Vite optimizeDeps.exclude 列表中添加模块

      如果一个包和它的依赖都是纯 ESM 包,你应该考虑将它添加至此。

    • addViteSsrExternal

      向 Vite ssr.external 列表中添加模块

      如果一个包是纯 ESM 包,且未使用别名 (alias) 或定义变量 (define),你应该考虑将它添加至此。

    • addViteSsrNoExternal

      向 Vite ssr.noExternal 列表中添加模块

      如果一个包内使用了别名 (alias) 或定义变量 (define),你必须将它添加至此。

      /**
      + * Add modules to Vite `optimizeDeps.include` list
      + */
      +export const addViteOptimizeDepsInclude: (
      +  bundlerOptions: unknown,
      +  app: App,
      +  module: string | string[],
      +) => void
      +
      +/**
      + * Add modules to Vite `optimizeDeps.exclude` list
      + */
      +export const addViteOptimizeDepsExclude: (
      +  bundlerOptions: unknown,
      +  app: App,
      +  module: string | string[],
      +) => void
      +
      +/**
      + * Add modules to Vite `ssr.external` list
      + */
      +export const addViteSsrExternal: (
      +  bundlerOptions: unknown,
      +  app: App,
      +  module: string | string[],
      +) => void
      +
      +/**
      + * Add modules to Vite `ssr.noExternal` list
      + */
      +export const addViteSsrNoExternal: (
      +  bundlerOptions: unknown,
      +  app: App,
      +  module: string | string[],
      +) => void
      +
      示例
      import {
      +  addViteOptimizeDepsInclude,
      +  addViteOptimizeDepsExclude,
      +  addViteSsrExternal,
      +  addViteSsrNoExternal,
      +} from '@vuepress/helper'
      +
      +addViteOptimizeDepsInclude(bundlerOptions, app, ['vue', 'vue-router'])
      +addViteOptimizeDepsExclude(bundlerOptions, app, 'packageA')
      +addViteSsrNoExternal(bundlerOptions, app, ['vue', 'vue-router'])
      +addViteSsrExternal(bundlerOptions, app, 'packageA')
      +
    • addViteConfig

      A function for you to add vite config

      export const addViteConfig: (
      +  bundlerOptions: unknown,
      +  app: App,
      +  config: Record<string, unknown>,
      +) => void
      +
      Example
      import { addViteConfig } from '@vuepress/helper'
      +
      +addViteConfig(bundlerOptions, app, {
      +  build: {
      +    charset: 'utf8',
      +  },
      +})
      +
    • mergeViteConfig

      无需导入 vite 即可合并 vite 配置的功能

      export const mergeViteConfig: (
      +  defaults: Record<string, any>,
      +  overrides: Record<string, any>,
      +) => Record<string, any>
      +

      注意

      你不应将 vite 作为依赖,因为你的的用户可能选择其他打包器!

      示例
      import { mergeViteConfig } from "@vuepress/helper";
      +
      +config.viteOptions mergeViteConfig(config.viteOptions, {
      +  build: {
      +    charset: "utf8",
      +  },
      +});
      +

    Webpack 相关

    • chainWebpack

      链式修改 webpack 配置.

      export const chainWebpack: (
      +  { app, config }: WebpackCommonOptions,
      +  chainWebpack: (
      +    config: WebpackChainConfig,
      +    isServer: boolean,
      +    isBuild: boolean,
      +  ) => void,
      +) => void
      +
      示例
      import { chainWebpack } from '@vuepress/helper'
      +
      +chainWebpack(bundlerOptions, app, (config, isServer, isBuild) => {
      +  // do some customize here
      +})
      +
    • configWebpack

      配置 Webpack

      export const configWebpack: (
      +  bundlerOptions: unknown,
      +  app: App,
      +  configureWebpack: (
      +    config: WebpackConfiguration,
      +    isServer: boolean,
      +    isBuild: boolean,
      +  ) => void,
      +) => void
      +
      实例
      import { configWebpack } from '@vuepress/helper'
      +
      +configWebpack(bundlerOptions, app, (config, isServer, isBuild) => {
      +  // do some customize here
      +})
      +
    + + + diff --git a/zh/tools/helper/node/index.html b/zh/tools/helper/node/index.html new file mode 100644 index 0000000000..3ce25fed86 --- /dev/null +++ b/zh/tools/helper/node/index.html @@ -0,0 +1,37 @@ + + + + + + + + + Node | VuePress 生态系统 + + + + + + + + + diff --git a/zh/tools/helper/node/page.html b/zh/tools/helper/node/page.html new file mode 100644 index 0000000000..4a362d5de1 --- /dev/null +++ b/zh/tools/helper/node/page.html @@ -0,0 +1,113 @@ + + + + + + + + + 页面相关 | VuePress 生态系统 + + + + + +

    页面相关

    这些函数为你的页面生成常见信息。

    getPageExcerpt

    获取页面摘要。

    export interface PageExcerptOptions {
    +  /**
    +   * 摘要分隔符
    +   *
    +   * @default "<!-- more -->"
    +   */
    +  separator?: string
    +
    +  /**
    +   * 摘要的长度
    +   *
    +   * @description 摘要的长度会尽可能的接近这个值
    +   *
    +   * @default 300
    +   */
    +  length?: number
    +
    +  /**
    +   * 被认为是自定义元素的标签
    +   *
    +   * @description 用于判断一个标签是否是自定义元素,因为在摘要中,所有的未知标签都会被移除。
    +   */
    +  isCustomElement?: (tagName: string) => boolean
    +
    +  /**
    +   * 是否保留页面标题 (第一个 h1)
    +   *
    +   * @default false
    +   */
    +  keepPageTitle?: boolean
    +
    +  /**
    +   * 是否保留代码块的标签,诸如行号和高亮行
    +   *
    +   * @default false
    +   */
    +  keepFenceDom?: boolean
    +}
    +
    +export const getPageExcerpt: (
    +  app: App,
    +  page: Page,
    +  options?: PageExcerptOptions,
    +) => string
    +

    getPageText

    获取页面纯文本。

    export interface PageTextOptions {
    +  /**
    +   * 是否将文字转换成单行内容
    +   *
    +   * @default false
    +   */
    +  singleLine?: boolean
    +
    +  /**
    +   * 文字的长度
    +   *
    +   * @description 文字的长度会尽可能的接近这个值
    +   *
    +   * @default 300
    +   */
    +  length?: number
    +
    +  /**
    +   * 需要移除的标签
    +   *
    +   * @description 默认情况下表格和代码块会被移除
    +   *
    +   * @default ['table', 'pre']
    +   */
    +  removedTags?: string[]
    +}
    +
    +export const getPageText: (
    +  app: App,
    +  page: Page,
    +  options?: PageTextOptions,
    +) => string
    +
    + + + diff --git a/zh/tools/helper/shared.html b/zh/tools/helper/shared.html new file mode 100644 index 0000000000..669d49ee48 --- /dev/null +++ b/zh/tools/helper/shared.html @@ -0,0 +1,146 @@ + + + + + + + + + 共享方法 | VuePress 生态系统 + + + + + +

    共享方法

    以下函数在 Node.js 和客户端上均可用。

    数据相关

    此方法在 MarkdownIt 插件中很有用。有些时候你可能需要在 Markdown 插件中生成组件,并将复杂的数据写入到组件属性中,一个通常做法是使用 JSON.stringify + encodeURIComponent,并在客户端 decodeURIComponent + JSON.parse。但如果内容包含很多特殊字符,转换结果会很长。

    所以我们提供 encodeDatadecodeData 来压缩和编码内容。

    export const encodeData: (
    +  data: string,
    +  level: DeflateOptions['level'] = 6,
    +) => string
    +
    +export const decodeData: (compressed: string) => string
    +
    const content = `
    +{
    +  "type": "bar",
    +  "data": {
    +    "labels": ["Red", "Blue", "Yellow", "Green", "Purple", "Orange"],
    +    "datasets": [
    +      {
    +        "label": "# of Votes",
    +        "data": [12, 19, 3, 5, 2, 3],
    +        "backgroundColor": [
    +          "rgba(255, 99, 132, 0.2)",
    +          "rgba(54, 162, 235, 0.2)",
    +          "rgba(255, 206, 86, 0.2)",
    +          "rgba(75, 192, 192, 0.2)",
    +          "rgba(153, 102, 255, 0.2)",
    +          "rgba(255, 159, 64, 0.2)"
    +        ],
    +        "borderColor": [
    +          "rgba(255, 99, 132, 1)",
    +          "rgba(54, 162, 235, 1)",
    +          "rgba(255, 206, 86, 1)",
    +          "rgba(75, 192, 192, 1)",
    +          "rgba(153, 102, 255, 1)",
    +          "rgba(255, 159, 64, 1)"
    +        ],
    +        "borderWidth": 1
    +      }
    +    ]
    +  },
    +  "options": {
    +    "scales": {
    +      "y": {
    +        "beginAtZero": true
    +      }
    +    }
    +  }
    +}
    +`
    +
    +const prop = encodeData(content) // "eJyNUsFOwzAMve8rrHABKZqWlg5WxAE4cARxAMHEIV1NmQhNlaaCCe3fcdKtW0sLWGpjxy/v+UV512mlcIyfhTa2hHP4GgHYVYExsEQaxqlMpZWxbwAomaAqY5izO0wZB3apKnTrIyqlP1x2bRBzl9xWplC+eWNkniF7dmw1X4nWsfgaNtwNP2kfgH6Be22x9CPUUQ8yFwEHMeMQcog4UBFuiF0kcvGWGV3l6ZVW2uw0XDCTJfIwiOjYjAhESIcn4+BoT2MLio6pP6V+EBJ6AOSZgsmUwyl9A6ATwoiZn3lYTkTkRkycnuP8TU9ENPqUxuuA9i9BmxTNPy9A/G2/F9I23wtpW++FdIwPKzW2W5Afph+WqX2NQWz313XicT7XhV3qnB5f/ejKhVTYVACrXUqUmC3zC/uERsdgTYUdVr/Qb302+gZxe7S/"
    +
    +decodeData(prop) // will be the original content
    +
    +// if you use `encodeURIComponent`, it will be much longer
    +encodeURIComponent(content) // '%0A%7B%0A%20%20%22type%22%3A%20%22bar%22%2C%0A%20%20%22data%22%3A%20%7B%0A%20%20%20%20%22labels%22%3A%20%5B%22Red%22%2C%20%22Blue%22%2C%20%22Yellow%22%2C%20%22Green%22%2C%20%22Purple%22%2C%20%22Orange%22%5D%2C%0A%20%20%20%20%22datasets%22%3A%20%5B%0A%20%20%20%20%20%20%7B%0A%20%20%20%20%20%20%20%20%22label%22%3A%20%22%23%20of%20Votes%22%2C%0A%20%20%20%20%20%20%20%20%22data%22%3A%20%5B12%2C%2019%2C%203%2C%205%2C%202%2C%203%5D%2C%0A%20%20%20%20%20%20%20%20%22backgroundColor%22%3A%20%5B%0A%20%20%20%20%20%20%20%20%20%20%22rgba(255%2C%2099%2C%20132%2C%200.2)%22%2C%0A%20%20%20%20%20%20%20%20%20%20%22rgba(54%2C%20162%2C%20235%2C%200.2)%22%2C%0A%20%20%20%20%20%20%20%20%20%20%22rgba(255%2C%20206%2C%2086%2C%200.2)%22%2C%0A%20%20%20%20%20%20%20%20%20%20%22rgba(75%2C%20192%2C%20192%2C%200.2)%22%2C%0A%20%20%20%20%20%20%20%20%20%20%22rgba(153%2C%20102%2C%20255%2C%200.2)%22%2C%0A%20%20%20%20%20%20%20%20%20%20%22rgba(255%2C%20159%2C%2064%2C%200.2)%22%0A%20%20%20%20%20%20%20%20%5D%2C%0A%20%20%20%20%20%20%20%20%22borderColor%22%3A%20%5B%0A%20%20%20%20%20%20%20%20%20%20%22rgba(255%2C%2099%2C%20132%2C%201)%22%2C%0A%20%20%20%20%20%20%20%20%20%20%22rgba(54%2C%20162%2C%20235%2C%201)%22%2C%0A%20%20%20%20%20%20%20%20%20%20%22rgba(255%2C%20206%2C%2086%2C%201)%22%2C%0A%20%20%20%20%20%20%20%20%20%20%22rgba(75%2C%20192%2C%20192%2C%201)%22%2C%0A%20%20%20%20%20%20%20%20%20%20%22rgba(153%2C%20102%2C%20255%2C%201)%22%2C%0A%20%20%20%20%20%20%20%20%20%20%22rgba(255%2C%20159%2C%2064%2C%201)%22%0A%20%20%20%20%20%20%20%20%5D%2C%0A%20%20%20%20%20%20%20%20%22borderWidth%22%3A%201%0A%20%20%20%20%20%20%7D%0A%20%20%20%20%5D%0A%20%20%7D%2C%0A%20%20%22options%22%3A%20%7B%0A%20%20%20%20%22scales%22%3A%20%7B%0A%20%20%20%20%20%20%22y%22%3A%20%7B%0A%20%20%20%20%20%20%20%20%22beginAtZero%22%3A%20true%0A%20%20%20%20%20%20%7D%0A%20%20%20%20%7D%0A%20%20%7D%0A%7D%0A'
    +

    类型助手

    • isDef(x): 判断 x 是否定义。
    • isBoolean(x): 判断 x 是否为布尔值。
    • isString(x): 判断 x 是否为字符串。
    • isNumber(x): 判断 x 是否为数字。
    • isPlainObject(x): 判断值是否为纯对象。
    • isArray(x): 判断 x 是否为数组
    • isFunction(x): 判断 x 是否为函数。
    • isRegExp(x): 判断 x 是否为正则表达式

    字符串相关

    • startsWith(a, b): 判断字符串 a 是否以指定字符串 b 开头
    • endsWith(a, b): 判断字符串 a 是否以指定字符串 b 结尾

    当 a 不是字符串时返回 false

    对象相关

    • keys(x): 以数组形式返回对象 x 的键

    • values(x): 以数组形式返回对象 x 的值

    • entries(x): 将对象 x 转换为键值对数组。

    • fromEntries(x): 将键值对数组 x 转换为对象。

    • deepAssign(x, y, ...): Object.assign 的深度版本。

      示例
      // or @vuepress/helper/client
      +import { deepAssign } from '@vuepress/helper'
      +
      +const defaultOptions = {
      +  optionA: {
      +    optionA1: 'defaultOptionA1',
      +    optionA2: 'defaultOptionA2',
      +    optionA3: 'defaultOptionA3',
      +  },
      +  optionB: true,
      +  optionC: 'optionC',
      +}
      +
      +const userOptions = {
      +  optionA: {
      +    optionA1: 'optionA1',
      +    optionA2: 'optionA2',
      +  },
      +  optionB: false,
      +}
      +
      +deepAssign(defaultOptions, userOptions)
      +// {
      +//   optionA: {
      +//     optionA1: "optionA1",
      +//     optionA2: "optionA2",
      +//     optionA3: "defaultOptionA3",
      +//   },
      +//   optionB: false,
      +//   optionC: "optionC",
      +// }
      +

    日期相关

    • getDate(x): 将输入 x 转换为日期,可以支持 Date,时间戳,日期字符串。日期字符串的支持度以环境的 Date.parse 支持度为准。当不能转换为日期时返回 null

      示例
      getDate('2021-01-01') // a Date object represents 2021-01-01
      +getDate(1609459200000) // a Date object represents 2021-01-01
      +getDate('2021-01-01T00:00:00.000Z') // a Date object represents 2021-01-01
      +getDate('2021/01/01') // a Date object represents 2021-01-01 (might be null in some browsers)
      +getDate('invalid date') // null
      +getDate(undefined) // null
      +getDate(-32) // null
      +
    • dateSorter: 将可转换为日期的值从新到旧排序,不能转换为日期的值会在最后。

      示例
      const arr = [
      +  '2020-01-01',
      +  1609459200000,
      +  '2022-01-01T00:00:00.000Z',
      +  '2023/01/01',
      +  'invalid date',
      +  undefined,
      +  -32,
      +]
      +
      +arr.sort(dateSorter)
      +// [
      +//   '2023/01/01',
      +//   '2022-01-01T00:00:00.000Z',
      +//   1609459200000,
      +//   '2020-01-01',
      +//   'invalid date',
      +//   undefined,
      +//   -32,
      +// ]
      +

    链接相关

    • isLinkHttp(x): x 是否是有效的 HTTP URL。
    • isLinkWithProtocol(x): x 是否是有效的带有协议的 URL。
    • isLinkExternal(x): x 是否是有效的外部 URL。
    • isLinkAbsolute(x): x 是否是有效的绝对 URL。
    • ensureEndingSlash(x): 确保 x 以斜杠结尾。
    • ensureLeadingSlash(x): 确保 x 以斜杠开头。
    • removeEndingSlash(x): 确保 x 不以斜杠结尾。
    • removeLeadingSlash(x): 确保 x 不以斜杠开头。
    + + + diff --git a/zh/tools/index.html b/zh/tools/index.html new file mode 100644 index 0000000000..c77668c9cf --- /dev/null +++ b/zh/tools/index.html @@ -0,0 +1,37 @@ + + + + + + + + + Tools | VuePress 生态系统 + + + + + + + + +