diff --git a/404.html b/404.html new file mode 100644 index 0000000000..df2c5b706e --- /dev/null +++ b/404.html @@ -0,0 +1,43 @@ + + + + + + + + + VuePress Ecosystem + + + + + +

404

How did we get here?
Take me home
+ + + diff --git a/assets/404.html-Ddh2xQcK.js b/assets/404.html-Ddh2xQcK.js new file mode 100644 index 0000000000..a6fddbe0cf --- /dev/null +++ b/assets/404.html-Ddh2xQcK.js @@ -0,0 +1 @@ +import{_ as t,c as s,a as o,o as n}from"./app-DjXMUMWv.js";const r={};function a(p,e){return n(),s("div",null,e[0]||(e[0]=[o("p",null,"404 Not Found",-1)]))}const c=t(r,[["render",a],["__file","404.html.vue"]]),m=JSON.parse('{"path":"/404.html","title":"","lang":"en-US","frontmatter":{"layout":"NotFound","description":"404 Not Found","head":[["meta",{"property":"og:url","content":"https://ecosystem.vuejs.press/ecosystem/404.html"}],["meta",{"property":"og:site_name","content":"VuePress Ecosystem"}],["meta",{"property":"og:description","content":"404 Not Found"}],["meta",{"property":"og:type","content":"website"}],["meta",{"property":"og:locale","content":"en-US"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"WebPage\\",\\"name\\":\\"\\",\\"description\\":\\"404 Not Found\\"}"],["link",{"rel":"alternate","type":"application/atom+xml","href":"https://ecosystem.vuejs.press/ecosystem/atom.xml","title":"VuePress Ecosystem Atom Feed"}],["link",{"rel":"alternate","type":"application/json","href":"https://ecosystem.vuejs.press/ecosystem/feed.json","title":"VuePress Ecosystem JSON Feed"}],["link",{"rel":"alternate","type":"application/rss+xml","href":"https://ecosystem.vuejs.press/ecosystem/rss.xml","title":"VuePress Ecosystem RSS Feed"}]]},"headers":[],"git":{},"autoDesc":true,"filePathRelative":null}');export{c as comp,m as data}; diff --git a/assets/KaTeX_AMS-Regular-BQhdFMY1.woff2 b/assets/KaTeX_AMS-Regular-BQhdFMY1.woff2 new file mode 100644 index 0000000000..0acaaff03d Binary files /dev/null and b/assets/KaTeX_AMS-Regular-BQhdFMY1.woff2 differ diff --git a/assets/KaTeX_AMS-Regular-DMm9YOAa.woff b/assets/KaTeX_AMS-Regular-DMm9YOAa.woff new file mode 100644 index 0000000000..b804d7b33a Binary files /dev/null and b/assets/KaTeX_AMS-Regular-DMm9YOAa.woff differ diff --git a/assets/KaTeX_AMS-Regular-DRggAlZN.ttf b/assets/KaTeX_AMS-Regular-DRggAlZN.ttf new file mode 100644 index 0000000000..c6f9a5e7c0 Binary files /dev/null and b/assets/KaTeX_AMS-Regular-DRggAlZN.ttf differ diff --git a/assets/KaTeX_Caligraphic-Bold-ATXxdsX0.ttf b/assets/KaTeX_Caligraphic-Bold-ATXxdsX0.ttf new file mode 100644 index 0000000000..9ff4a5e044 Binary files /dev/null and b/assets/KaTeX_Caligraphic-Bold-ATXxdsX0.ttf differ diff --git a/assets/KaTeX_Caligraphic-Bold-BEiXGLvX.woff b/assets/KaTeX_Caligraphic-Bold-BEiXGLvX.woff new file mode 100644 index 0000000000..9759710d1d Binary files /dev/null and b/assets/KaTeX_Caligraphic-Bold-BEiXGLvX.woff differ diff --git a/assets/KaTeX_Caligraphic-Bold-Dq_IR9rO.woff2 b/assets/KaTeX_Caligraphic-Bold-Dq_IR9rO.woff2 new file mode 100644 index 0000000000..f390922ece Binary files /dev/null and b/assets/KaTeX_Caligraphic-Bold-Dq_IR9rO.woff2 differ diff --git a/assets/KaTeX_Caligraphic-Regular-CTRA-rTL.woff b/assets/KaTeX_Caligraphic-Regular-CTRA-rTL.woff new file mode 100644 index 0000000000..9bdd534fd2 Binary files /dev/null and b/assets/KaTeX_Caligraphic-Regular-CTRA-rTL.woff differ diff --git a/assets/KaTeX_Caligraphic-Regular-Di6jR-x-.woff2 b/assets/KaTeX_Caligraphic-Regular-Di6jR-x-.woff2 new file mode 100644 index 0000000000..75344a1f98 Binary files /dev/null and b/assets/KaTeX_Caligraphic-Regular-Di6jR-x-.woff2 differ diff --git a/assets/KaTeX_Caligraphic-Regular-wX97UBjC.ttf b/assets/KaTeX_Caligraphic-Regular-wX97UBjC.ttf new file mode 100644 index 0000000000..f522294ff0 Binary files /dev/null and b/assets/KaTeX_Caligraphic-Regular-wX97UBjC.ttf differ diff --git a/assets/KaTeX_Fraktur-Bold-BdnERNNW.ttf b/assets/KaTeX_Fraktur-Bold-BdnERNNW.ttf new file mode 100644 index 0000000000..4e98259c3b Binary files /dev/null and b/assets/KaTeX_Fraktur-Bold-BdnERNNW.ttf differ diff --git a/assets/KaTeX_Fraktur-Bold-BsDP51OF.woff b/assets/KaTeX_Fraktur-Bold-BsDP51OF.woff new file mode 100644 index 0000000000..e7730f6627 Binary files /dev/null and b/assets/KaTeX_Fraktur-Bold-BsDP51OF.woff differ diff --git a/assets/KaTeX_Fraktur-Bold-CL6g_b3V.woff2 b/assets/KaTeX_Fraktur-Bold-CL6g_b3V.woff2 new file mode 100644 index 0000000000..395f28beac Binary files /dev/null and b/assets/KaTeX_Fraktur-Bold-CL6g_b3V.woff2 differ diff --git a/assets/KaTeX_Fraktur-Regular-CB_wures.ttf b/assets/KaTeX_Fraktur-Regular-CB_wures.ttf new file mode 100644 index 0000000000..b8461b275f Binary files /dev/null and b/assets/KaTeX_Fraktur-Regular-CB_wures.ttf differ diff --git a/assets/KaTeX_Fraktur-Regular-CTYiF6lA.woff2 b/assets/KaTeX_Fraktur-Regular-CTYiF6lA.woff2 new file mode 100644 index 0000000000..735f6948d6 Binary files /dev/null and b/assets/KaTeX_Fraktur-Regular-CTYiF6lA.woff2 differ diff --git a/assets/KaTeX_Fraktur-Regular-Dxdc4cR9.woff b/assets/KaTeX_Fraktur-Regular-Dxdc4cR9.woff new file mode 100644 index 0000000000..acab069f90 Binary files /dev/null and b/assets/KaTeX_Fraktur-Regular-Dxdc4cR9.woff differ diff --git a/assets/KaTeX_Main-Bold-Cx986IdX.woff2 b/assets/KaTeX_Main-Bold-Cx986IdX.woff2 new file mode 100644 index 0000000000..ab2ad21da6 Binary files /dev/null and b/assets/KaTeX_Main-Bold-Cx986IdX.woff2 differ diff --git a/assets/KaTeX_Main-Bold-Jm3AIy58.woff b/assets/KaTeX_Main-Bold-Jm3AIy58.woff new file mode 100644 index 0000000000..f38136ac1c Binary files /dev/null and b/assets/KaTeX_Main-Bold-Jm3AIy58.woff differ diff --git a/assets/KaTeX_Main-Bold-waoOVXN0.ttf b/assets/KaTeX_Main-Bold-waoOVXN0.ttf new file mode 100644 index 0000000000..4060e627dc Binary files /dev/null and b/assets/KaTeX_Main-Bold-waoOVXN0.ttf differ diff --git a/assets/KaTeX_Main-BoldItalic-DxDJ3AOS.woff2 b/assets/KaTeX_Main-BoldItalic-DxDJ3AOS.woff2 new file mode 100644 index 0000000000..5931794de4 Binary files /dev/null and b/assets/KaTeX_Main-BoldItalic-DxDJ3AOS.woff2 differ diff --git a/assets/KaTeX_Main-BoldItalic-DzxPMmG6.ttf b/assets/KaTeX_Main-BoldItalic-DzxPMmG6.ttf new file mode 100644 index 0000000000..dc007977ee Binary files /dev/null and b/assets/KaTeX_Main-BoldItalic-DzxPMmG6.ttf differ diff --git a/assets/KaTeX_Main-BoldItalic-SpSLRI95.woff b/assets/KaTeX_Main-BoldItalic-SpSLRI95.woff new file mode 100644 index 0000000000..67807b0bd4 Binary files /dev/null and b/assets/KaTeX_Main-BoldItalic-SpSLRI95.woff differ diff --git a/assets/KaTeX_Main-Italic-3WenGoN9.ttf b/assets/KaTeX_Main-Italic-3WenGoN9.ttf new file mode 100644 index 0000000000..0e9b0f354a Binary files /dev/null and b/assets/KaTeX_Main-Italic-3WenGoN9.ttf differ diff --git a/assets/KaTeX_Main-Italic-BMLOBm91.woff b/assets/KaTeX_Main-Italic-BMLOBm91.woff new file mode 100644 index 0000000000..6f43b594b6 Binary files /dev/null and b/assets/KaTeX_Main-Italic-BMLOBm91.woff differ diff --git a/assets/KaTeX_Main-Italic-NWA7e6Wa.woff2 b/assets/KaTeX_Main-Italic-NWA7e6Wa.woff2 new file mode 100644 index 0000000000..b50920e138 Binary files /dev/null and b/assets/KaTeX_Main-Italic-NWA7e6Wa.woff2 differ diff --git a/assets/KaTeX_Main-Regular-B22Nviop.woff2 b/assets/KaTeX_Main-Regular-B22Nviop.woff2 new file mode 100644 index 0000000000..eb24a7ba28 Binary files /dev/null and b/assets/KaTeX_Main-Regular-B22Nviop.woff2 differ diff --git a/assets/KaTeX_Main-Regular-Dr94JaBh.woff b/assets/KaTeX_Main-Regular-Dr94JaBh.woff new file mode 100644 index 0000000000..21f5812968 Binary files /dev/null and b/assets/KaTeX_Main-Regular-Dr94JaBh.woff differ diff --git a/assets/KaTeX_Main-Regular-ypZvNtVU.ttf b/assets/KaTeX_Main-Regular-ypZvNtVU.ttf new file mode 100644 index 0000000000..dd45e1ed2e Binary files /dev/null and b/assets/KaTeX_Main-Regular-ypZvNtVU.ttf differ diff --git a/assets/KaTeX_Math-BoldItalic-B3XSjfu4.ttf b/assets/KaTeX_Math-BoldItalic-B3XSjfu4.ttf new file mode 100644 index 0000000000..728ce7a1e2 Binary files /dev/null and b/assets/KaTeX_Math-BoldItalic-B3XSjfu4.ttf differ diff --git a/assets/KaTeX_Math-BoldItalic-CZnvNsCZ.woff2 b/assets/KaTeX_Math-BoldItalic-CZnvNsCZ.woff2 new file mode 100644 index 0000000000..29657023ad Binary files /dev/null and b/assets/KaTeX_Math-BoldItalic-CZnvNsCZ.woff2 differ diff --git a/assets/KaTeX_Math-BoldItalic-iY-2wyZ7.woff b/assets/KaTeX_Math-BoldItalic-iY-2wyZ7.woff new file mode 100644 index 0000000000..0ae390d74c Binary files /dev/null and b/assets/KaTeX_Math-BoldItalic-iY-2wyZ7.woff differ diff --git a/assets/KaTeX_Math-Italic-DA0__PXp.woff b/assets/KaTeX_Math-Italic-DA0__PXp.woff new file mode 100644 index 0000000000..eb5159d4c1 Binary files /dev/null and b/assets/KaTeX_Math-Italic-DA0__PXp.woff differ diff --git a/assets/KaTeX_Math-Italic-flOr_0UB.ttf b/assets/KaTeX_Math-Italic-flOr_0UB.ttf new file mode 100644 index 0000000000..70d559b4e9 Binary files /dev/null and b/assets/KaTeX_Math-Italic-flOr_0UB.ttf differ diff --git a/assets/KaTeX_Math-Italic-t53AETM-.woff2 b/assets/KaTeX_Math-Italic-t53AETM-.woff2 new file mode 100644 index 0000000000..215c143fd7 Binary files /dev/null and b/assets/KaTeX_Math-Italic-t53AETM-.woff2 differ diff --git a/assets/KaTeX_SansSerif-Bold-CFMepnvq.ttf b/assets/KaTeX_SansSerif-Bold-CFMepnvq.ttf new file mode 100644 index 0000000000..2f65a8a3a6 Binary files /dev/null and b/assets/KaTeX_SansSerif-Bold-CFMepnvq.ttf differ diff --git a/assets/KaTeX_SansSerif-Bold-D1sUS0GD.woff2 b/assets/KaTeX_SansSerif-Bold-D1sUS0GD.woff2 new file mode 100644 index 0000000000..cfaa3bda59 Binary files /dev/null and b/assets/KaTeX_SansSerif-Bold-D1sUS0GD.woff2 differ diff --git a/assets/KaTeX_SansSerif-Bold-DbIhKOiC.woff b/assets/KaTeX_SansSerif-Bold-DbIhKOiC.woff new file mode 100644 index 0000000000..8d47c02d94 Binary files /dev/null and b/assets/KaTeX_SansSerif-Bold-DbIhKOiC.woff differ diff --git a/assets/KaTeX_SansSerif-Italic-C3H0VqGB.woff2 b/assets/KaTeX_SansSerif-Italic-C3H0VqGB.woff2 new file mode 100644 index 0000000000..349c06dc60 Binary files /dev/null and b/assets/KaTeX_SansSerif-Italic-C3H0VqGB.woff2 differ diff --git a/assets/KaTeX_SansSerif-Italic-DN2j7dab.woff b/assets/KaTeX_SansSerif-Italic-DN2j7dab.woff new file mode 100644 index 0000000000..7e02df9636 Binary files /dev/null and b/assets/KaTeX_SansSerif-Italic-DN2j7dab.woff differ diff --git a/assets/KaTeX_SansSerif-Italic-YYjJ1zSn.ttf b/assets/KaTeX_SansSerif-Italic-YYjJ1zSn.ttf new file mode 100644 index 0000000000..d5850df98e Binary files /dev/null and b/assets/KaTeX_SansSerif-Italic-YYjJ1zSn.ttf differ diff --git a/assets/KaTeX_SansSerif-Regular-BNo7hRIc.ttf b/assets/KaTeX_SansSerif-Regular-BNo7hRIc.ttf new file mode 100644 index 0000000000..537279f6bd Binary files /dev/null and b/assets/KaTeX_SansSerif-Regular-BNo7hRIc.ttf differ diff --git a/assets/KaTeX_SansSerif-Regular-CS6fqUqJ.woff b/assets/KaTeX_SansSerif-Regular-CS6fqUqJ.woff new file mode 100644 index 0000000000..31b84829b4 Binary files /dev/null and b/assets/KaTeX_SansSerif-Regular-CS6fqUqJ.woff differ diff --git a/assets/KaTeX_SansSerif-Regular-DDBCnlJ7.woff2 b/assets/KaTeX_SansSerif-Regular-DDBCnlJ7.woff2 new file mode 100644 index 0000000000..a90eea85f6 Binary files /dev/null and b/assets/KaTeX_SansSerif-Regular-DDBCnlJ7.woff2 differ diff --git a/assets/KaTeX_Script-Regular-C5JkGWo-.ttf b/assets/KaTeX_Script-Regular-C5JkGWo-.ttf new file mode 100644 index 0000000000..fd679bf374 Binary files /dev/null and b/assets/KaTeX_Script-Regular-C5JkGWo-.ttf differ diff --git a/assets/KaTeX_Script-Regular-D3wIWfF6.woff2 b/assets/KaTeX_Script-Regular-D3wIWfF6.woff2 new file mode 100644 index 0000000000..b3048fc115 Binary files /dev/null and b/assets/KaTeX_Script-Regular-D3wIWfF6.woff2 differ diff --git a/assets/KaTeX_Script-Regular-D5yQViql.woff b/assets/KaTeX_Script-Regular-D5yQViql.woff new file mode 100644 index 0000000000..0e7da821ee Binary files /dev/null and b/assets/KaTeX_Script-Regular-D5yQViql.woff differ diff --git a/assets/KaTeX_Size1-Regular-C195tn64.woff b/assets/KaTeX_Size1-Regular-C195tn64.woff new file mode 100644 index 0000000000..7f292d9118 Binary files /dev/null and b/assets/KaTeX_Size1-Regular-C195tn64.woff differ diff --git a/assets/KaTeX_Size1-Regular-Dbsnue_I.ttf b/assets/KaTeX_Size1-Regular-Dbsnue_I.ttf new file mode 100644 index 0000000000..871fd7d19d Binary files /dev/null and b/assets/KaTeX_Size1-Regular-Dbsnue_I.ttf differ diff --git a/assets/KaTeX_Size1-Regular-mCD8mA8B.woff2 b/assets/KaTeX_Size1-Regular-mCD8mA8B.woff2 new file mode 100644 index 0000000000..c5a8462fbf Binary files /dev/null and b/assets/KaTeX_Size1-Regular-mCD8mA8B.woff2 differ diff --git a/assets/KaTeX_Size2-Regular-B7gKUWhC.ttf b/assets/KaTeX_Size2-Regular-B7gKUWhC.ttf new file mode 100644 index 0000000000..7a212caf91 Binary files /dev/null and b/assets/KaTeX_Size2-Regular-B7gKUWhC.ttf differ diff --git a/assets/KaTeX_Size2-Regular-Dy4dx90m.woff2 b/assets/KaTeX_Size2-Regular-Dy4dx90m.woff2 new file mode 100644 index 0000000000..e1bccfe240 Binary files /dev/null and b/assets/KaTeX_Size2-Regular-Dy4dx90m.woff2 differ diff --git a/assets/KaTeX_Size2-Regular-oD1tc_U0.woff b/assets/KaTeX_Size2-Regular-oD1tc_U0.woff new file mode 100644 index 0000000000..d241d9be2d Binary files /dev/null and b/assets/KaTeX_Size2-Regular-oD1tc_U0.woff differ diff --git a/assets/KaTeX_Size3-Regular-CTq5MqoE.woff b/assets/KaTeX_Size3-Regular-CTq5MqoE.woff new file mode 100644 index 0000000000..e6e9b658dc Binary files /dev/null and b/assets/KaTeX_Size3-Regular-CTq5MqoE.woff differ diff --git a/assets/KaTeX_Size3-Regular-DgpXs0kz.ttf b/assets/KaTeX_Size3-Regular-DgpXs0kz.ttf new file mode 100644 index 0000000000..00bff3495f Binary files /dev/null and b/assets/KaTeX_Size3-Regular-DgpXs0kz.ttf differ diff --git a/assets/KaTeX_Size4-Regular-BF-4gkZK.woff b/assets/KaTeX_Size4-Regular-BF-4gkZK.woff new file mode 100644 index 0000000000..e1ec545766 Binary files /dev/null and b/assets/KaTeX_Size4-Regular-BF-4gkZK.woff differ diff --git a/assets/KaTeX_Size4-Regular-DWFBv043.ttf b/assets/KaTeX_Size4-Regular-DWFBv043.ttf new file mode 100644 index 0000000000..74f08921f0 Binary files /dev/null and b/assets/KaTeX_Size4-Regular-DWFBv043.ttf differ diff --git a/assets/KaTeX_Size4-Regular-Dl5lxZxV.woff2 b/assets/KaTeX_Size4-Regular-Dl5lxZxV.woff2 new file mode 100644 index 0000000000..680c130850 Binary files /dev/null and b/assets/KaTeX_Size4-Regular-Dl5lxZxV.woff2 differ diff --git a/assets/KaTeX_Typewriter-Regular-C0xS9mPB.woff b/assets/KaTeX_Typewriter-Regular-C0xS9mPB.woff new file mode 100644 index 0000000000..2432419f28 Binary files /dev/null and b/assets/KaTeX_Typewriter-Regular-C0xS9mPB.woff differ diff --git a/assets/KaTeX_Typewriter-Regular-CO6r4hn1.woff2 b/assets/KaTeX_Typewriter-Regular-CO6r4hn1.woff2 new file mode 100644 index 0000000000..771f1af705 Binary files /dev/null and b/assets/KaTeX_Typewriter-Regular-CO6r4hn1.woff2 differ diff --git a/assets/KaTeX_Typewriter-Regular-D3Ib7_Hf.ttf b/assets/KaTeX_Typewriter-Regular-D3Ib7_Hf.ttf new file mode 100644 index 0000000000..c83252c571 Binary files /dev/null and b/assets/KaTeX_Typewriter-Regular-D3Ib7_Hf.ttf differ diff --git a/assets/NpmBadge-BXbpMzev.js b/assets/NpmBadge-BXbpMzev.js new file mode 100644 index 0000000000..3cb04d5957 --- /dev/null +++ b/assets/NpmBadge-BXbpMzev.js @@ -0,0 +1 @@ +import{h as p,i as n,_ as d,o as g,c as _,a as i}from"./app-DjXMUMWv.js";const l=p({__name:"NpmBadge",props:{package:{},distTag:{default:"next"}},setup(s,{expose:c}){c();const e=s,a=n(()=>`https://www.npmjs.com/package/${e.package}/v/next`),t=n(()=>e.distTag?`${e.package}@${e.distTag}`:e.package),o=n(()=>`https://badgen.net/npm/v/${e.package}/${e.distTag}?label=${encodeURIComponent(t.value)}`),r={props:e,badgeLink:a,badgeLabel:t,badgeImg:o};return Object.defineProperty(r,"__isScriptSetup",{enumerable:!1,value:!0}),r}}),m=["href","title"],f=["src","alt"];function u(s,c,e,a,t,o){return g(),_("a",{class:"npm-badge",href:a.badgeLink,title:e.package,target:"_blank",rel:"noopener noreferrer"},[i("img",{src:a.badgeImg,alt:e.package},null,8,f)],8,m)}const b=d(l,[["render",u],["__scopeId","data-v-87400fa7"],["__file","NpmBadge.vue"]]);export{b as default}; diff --git a/assets/PaletteDemo-BvnX8knh.js b/assets/PaletteDemo-BvnX8knh.js new file mode 100644 index 0000000000..5be7d3f534 --- /dev/null +++ b/assets/PaletteDemo-BvnX8knh.js @@ -0,0 +1 @@ +import{h as r,u as c,_ as d,o as b,c as i,a as t,t as s,d as a,F as u}from"./app-DjXMUMWv.js";const v=r({__name:"PaletteDemo",setup(l,{expose:e}){e();const o={locale:c({"/":{text:"Text Colors",accent:"Accent Colors",border:"Border Colors",shadow:"Shadow Colors",control:"Control Colors"},"/zh/":{text:"文字颜色",accent:"强调色",border:"边框颜色",shadow:"阴影色",control:"控件颜色"}})};return Object.defineProperty(o,"__isScriptSetup",{enumerable:!1,value:!0}),o}}),p={class:"header"},h={class:"header"},m={class:"header"},_={class:"header"},w={class:"header"};function y(l,e,n,o,x,f){return b(),i(u,null,[t("div",p,s(o.locale.text),1),e[0]||(e[0]=t("div",{class:"demo"},[t("div",{class:"text"},"text"),t("div",{class:"text-mute"},"text mute"),t("div",{class:"text-subtle"},"text subtle")],-1)),t("div",h,s(o.locale.border),1),e[1]||(e[1]=t("div",{class:"demo"},[t("div",{class:"border-display border"},"border"),t("div",{class:"border-display border-hard"},"border hard"),t("div",{class:"border-display gutter"},"gutter")],-1)),t("div",m,s(o.locale.accent),1),e[2]||(e[2]=a('
',1)),t("div",_,s(o.locale.shadow),1),e[3]||(e[3]=a('
',1)),t("div",w,s(o.locale.control),1),e[4]||(e[4]=a('
',1))],64)}const C=d(v,[["render",y],["__file","PaletteDemo.vue"]]);export{C as default}; diff --git a/assets/PaletteDisplay-Dx6IkeAC.js b/assets/PaletteDisplay-Dx6IkeAC.js new file mode 100644 index 0000000000..36a0084383 --- /dev/null +++ b/assets/PaletteDisplay-Dx6IkeAC.js @@ -0,0 +1 @@ +import n from"./PaletteDemo-BvnX8knh.js";import{h as l,_ as p,o as i,c as _,a as t,b as r,F as d}from"./app-DjXMUMWv.js";const c=l({__name:"PaletteDisplay",setup(o,{expose:e}){e();const a={PaletteDemo:n};return Object.defineProperty(a,"__isScriptSetup",{enumerable:!1,value:!0}),a}}),b={class:"bg border bg-wrapper"},m={class:"bg-alt border bg-wrapper"};function f(o,e,a,s,u,g){return i(),_(d,null,[t("div",b,[e[0]||(e[0]=t("div",{class:"bg-hint"},"bg",-1)),r(s.PaletteDemo)]),t("div",m,[e[1]||(e[1]=t("div",{class:"bg-hint"},"bg alt",-1)),r(s.PaletteDemo)])],64)}const D=p(c,[["render",f],["__file","PaletteDisplay.vue"]]);export{D as default}; diff --git a/assets/active-header-links.html-DtNaUDF_.js b/assets/active-header-links.html-DtNaUDF_.js new file mode 100644 index 0000000000..0361e9206e --- /dev/null +++ b/assets/active-header-links.html-DtNaUDF_.js @@ -0,0 +1,9 @@ +import{_ as i,c as t,a as s,b as l,d as n,o as r,r as o}from"./app-DjXMUMWv.js";const h={};function p(d,e){const a=o("NpmBadge");return r(),t("div",null,[e[0]||(e[0]=s("h1",{id:"active-header-links",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#active-header-links"},[s("span",null,"active-header-links")])],-1)),l(a,{package:"@vuepress/plugin-active-header-links"}),e[1]||(e[1]=n(`

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

delay

offset

`,14))])}const k=i(h,[["render",p],["__file","active-header-links.html.vue"]]),m=JSON.parse('{"path":"/plugins/development/active-header-links.html","title":"active-header-links","lang":"en-US","frontmatter":{"icon":"link-2","description":"active-header-links","head":[["link",{"rel":"alternate","hreflang":"zh-cn","href":"https://ecosystem.vuejs.press/ecosystem/zh/plugins/development/active-header-links.html"}],["meta",{"property":"og:url","content":"https://ecosystem.vuejs.press/ecosystem/plugins/development/active-header-links.html"}],["meta",{"property":"og:site_name","content":"VuePress Ecosystem"}],["meta",{"property":"og:title","content":"active-header-links"}],["meta",{"property":"og:description","content":"active-header-links"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"en-US"}],["meta",{"property":"og:locale:alternate","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2025-01-10T18:07:54.000Z"}],["meta",{"property":"article:modified_time","content":"2025-01-10T18:07:54.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"active-header-links\\",\\"image\\":[\\"\\"],\\"dateModified\\":\\"2025-01-10T18:07:54.000Z\\",\\"author\\":[]}"],["link",{"rel":"alternate","type":"application/atom+xml","href":"https://ecosystem.vuejs.press/ecosystem/atom.xml","title":"VuePress Ecosystem Atom Feed"}],["link",{"rel":"alternate","type":"application/json","href":"https://ecosystem.vuejs.press/ecosystem/feed.json","title":"VuePress Ecosystem JSON Feed"}],["link",{"rel":"alternate","type":"application/rss+xml","href":"https://ecosystem.vuejs.press/ecosystem/rss.xml","title":"VuePress Ecosystem RSS Feed"}]]},"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":1736532474000,"contributors":[{"name":"Mr.Hope","username":"Mr.Hope","email":"mister-hope@outlook.com","commits":3,"url":"https://github.com/Mr.Hope"},{"name":"Mister-Hope","username":"Mister-Hope","email":"mister-hope@outlook.com","commits":2,"url":"https://github.com/Mister-Hope"}]},"autoDesc":true,"filePathRelative":"plugins/development/active-header-links.md"}');export{k as comp,m as data}; diff --git a/assets/active-header-links.html-GVuFxrx5.js b/assets/active-header-links.html-GVuFxrx5.js new file mode 100644 index 0000000000..b92544ce65 --- /dev/null +++ b/assets/active-header-links.html-GVuFxrx5.js @@ -0,0 +1,9 @@ +import{_ as a,c as t,a as s,b as l,d as n,o as r,r as h}from"./app-DjXMUMWv.js";const p={};function o(d,e){const i=h("NpmBadge");return r(),t("div",null,[e[0]||(e[0]=s("h1",{id:"active-header-links",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#active-header-links"},[s("span",null,"active-header-links")])],-1)),l(i,{package:"@vuepress/plugin-active-header-links"}),e[1]||(e[1]=n(`

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

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

使用方法

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

配置项

headerLinkSelector

headerAnchorSelector

delay

offset

`,14))])}const k=a(p,[["render",o],["__file","active-header-links.html.vue"]]),m=JSON.parse('{"path":"/zh/plugins/development/active-header-links.html","title":"active-header-links","lang":"zh-CN","frontmatter":{"icon":"link-2","description":"active-header-links","head":[["link",{"rel":"alternate","hreflang":"en-us","href":"https://ecosystem.vuejs.press/ecosystem/plugins/development/active-header-links.html"}],["meta",{"property":"og:url","content":"https://ecosystem.vuejs.press/ecosystem/zh/plugins/development/active-header-links.html"}],["meta",{"property":"og:site_name","content":"VuePress 生态系统"}],["meta",{"property":"og:title","content":"active-header-links"}],["meta",{"property":"og:description","content":"active-header-links"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:locale:alternate","content":"en-US"}],["meta",{"property":"og:updated_time","content":"2025-01-10T18:07:54.000Z"}],["meta",{"property":"article:modified_time","content":"2025-01-10T18:07:54.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"active-header-links\\",\\"image\\":[\\"\\"],\\"dateModified\\":\\"2025-01-10T18:07:54.000Z\\",\\"author\\":[]}"],["link",{"rel":"alternate","type":"application/atom+xml","href":"https://ecosystem.vuejs.press/ecosystem/zh/atom.xml","title":"VuePress 生态系统 Atom Feed"}],["link",{"rel":"alternate","type":"application/json","href":"https://ecosystem.vuejs.press/ecosystem/zh/feed.json","title":"VuePress 生态系统 JSON Feed"}],["link",{"rel":"alternate","type":"application/rss+xml","href":"https://ecosystem.vuejs.press/ecosystem/zh/rss.xml","title":"VuePress 生态系统 RSS Feed"}]]},"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":1736532474000,"contributors":[{"name":"Mr.Hope","username":"Mr.Hope","email":"mister-hope@outlook.com","commits":3,"url":"https://github.com/Mr.Hope"},{"name":"Mister-Hope","username":"Mister-Hope","email":"mister-hope@outlook.com","commits":2,"url":"https://github.com/Mister-Hope"}]},"autoDesc":true,"filePathRelative":"zh/plugins/development/active-header-links.md"}');export{k as comp,m as data}; diff --git a/assets/app-DjXMUMWv.js b/assets/app-DjXMUMWv.js new file mode 100644 index 0000000000..155813f0fe --- /dev/null +++ b/assets/app-DjXMUMWv.js @@ -0,0 +1,46 @@ +const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["assets/markdown-image.html-DHPoKt8m.js","assets/favicon-CFFEFrKu.js","assets/extending.html-BMVvbvzq.js","assets/extending-a-theme-01-Cqco1E4_.js","assets/markdown-image.html-DEegF7wB.js","assets/extending.html-CSozc1NZ.js","assets/index.html-C1HbKT7i.js","assets/vercel-9-BeG_WJ35.js","assets/index.html-dFD4TqPg.js","assets/PaletteDisplay-Dx6IkeAC.js","assets/PaletteDemo-BvnX8knh.js"])))=>i.map(i=>d[i]); +const wp="modulepreload",Ap=function(e){return"/ecosystem/"+e},Ol={},b=function(t,n,r){let o=Promise.resolve();if(n&&n.length>0){document.getElementsByTagName("link");const s=document.querySelector("meta[property=csp-nonce]"),l=(s==null?void 0:s.nonce)||(s==null?void 0:s.getAttribute("nonce"));o=Promise.allSettled(n.map(a=>{if(a=Ap(a),a in Ol)return;Ol[a]=!0;const u=a.endsWith(".css"),d=u?'[rel="stylesheet"]':"";if(document.querySelector(`link[href="${a}"]${d}`))return;const c=document.createElement("link");if(c.rel=u?"stylesheet":wp,u||(c.as="script"),c.crossOrigin="",c.href=a,l&&c.setAttribute("nonce",l),document.head.appendChild(c),u)return new Promise((f,p)=>{c.addEventListener("load",f),c.addEventListener("error",()=>p(new Error(`Unable to preload CSS for ${a}`)))})}))}function i(s){const l=new Event("vite:preloadError",{cancelable:!0});if(l.payload=s,window.dispatchEvent(l),!l.defaultPrevented)throw s}return o.then(s=>{for(const l of s||[])l.status==="rejected"&&i(l.reason);return t().catch(i)})};/** +* @vue/shared v3.5.13 +* (c) 2018-present Yuxi (Evan) You and Vue contributors +* @license MIT +**//*! #__NO_SIDE_EFFECTS__ */function vr(e){const t=Object.create(null);for(const n of e.split(","))t[n]=1;return n=>n in t}const Ae={},er=[],Bt=()=>{},Sp=()=>!1,oo=e=>e.charCodeAt(0)===111&&e.charCodeAt(1)===110&&(e.charCodeAt(2)>122||e.charCodeAt(2)<97),Is=e=>e.startsWith("onUpdate:"),Ye=Object.assign,Ls=(e,t)=>{const n=e.indexOf(t);n>-1&&e.splice(n,1)},kp=Object.prototype.hasOwnProperty,be=(e,t)=>kp.call(e,t),ie=Array.isArray,tr=e=>ni(e)==="[object Map]",ec=e=>ni(e)==="[object Set]",le=e=>typeof e=="function",Ce=e=>typeof e=="string",Jt=e=>typeof e=="symbol",xe=e=>e!==null&&typeof e=="object",tc=e=>(xe(e)||le(e))&&le(e.then)&&le(e.catch),nc=Object.prototype.toString,ni=e=>nc.call(e),Op=e=>ni(e).slice(8,-1),rc=e=>ni(e)==="[object Object]",Rs=e=>Ce(e)&&e!=="NaN"&&e[0]!=="-"&&""+parseInt(e,10)===e,nr=vr(",key,ref,ref_for,ref_key,onVnodeBeforeMount,onVnodeMounted,onVnodeBeforeUpdate,onVnodeUpdated,onVnodeBeforeUnmount,onVnodeUnmounted"),ri=e=>{const t=Object.create(null);return n=>t[n]||(t[n]=e(n))},Pp=/-(\w)/g,ut=ri(e=>e.replace(Pp,(t,n)=>n?n.toUpperCase():"")),Cp=/\B([A-Z])/g,Qt=ri(e=>e.replace(Cp,"-$1").toLowerCase()),io=ri(e=>e.charAt(0).toUpperCase()+e.slice(1)),yi=ri(e=>e?`on${io(e)}`:""),mn=(e,t)=>!Object.is(e,t),Ti=(e,...t)=>{for(let n=0;n{Object.defineProperty(e,t,{configurable:!0,enumerable:!1,writable:r,value:n})},xp=e=>{const t=parseFloat(e);return isNaN(t)?e:t},Ip=e=>{const t=Ce(e)?Number(e):NaN;return isNaN(t)?e:t};let Pl;const oi=()=>Pl||(Pl=typeof globalThis<"u"?globalThis:typeof self<"u"?self:typeof window<"u"?window:typeof global<"u"?global:{});function jn(e){if(ie(e)){const t={};for(let n=0;n{if(n){const r=n.split(Rp);r.length>1&&(t[r[0].trim()]=r[1].trim())}}),t}function Np(e){if(!e)return"";if(Ce(e))return e;let t="";for(const n in e){const r=e[n];if(Ce(r)||typeof r=="number"){const o=n.startsWith("--")?n:Qt(n);t+=`${o}:${r};`}}return t}function st(e){let t="";if(Ce(e))t=e;else if(ie(e))for(let n=0;n?@[\\\]^`{|}~]/g;function Hp(e,t){return e.replace(Fp,n=>`\\${n}`)}const ic=e=>!!(e&&e.__v_isRef===!0),$e=e=>Ce(e)?e:e==null?"":ie(e)||xe(e)&&(e.toString===nc||!le(e.toString))?ic(e)?$e(e.value):JSON.stringify(e,sc,2):String(e),sc=(e,t)=>ic(t)?sc(e,t.value):tr(t)?{[`Map(${t.size})`]:[...t.entries()].reduce((n,[r,o],i)=>(n[Ai(r,i)+" =>"]=o,n),{})}:ec(t)?{[`Set(${t.size})`]:[...t.values()].map(n=>Ai(n))}:Jt(t)?Ai(t):xe(t)&&!ie(t)&&!rc(t)?String(t):t,Ai=(e,t="")=>{var n;return Jt(e)?`Symbol(${(n=e.description)!=null?n:t})`:e};/** +* @vue/reactivity v3.5.13 +* (c) 2018-present Yuxi (Evan) You and Vue contributors +* @license MIT +**/let rt;class jp{constructor(t=!1){this.detached=t,this._active=!0,this.effects=[],this.cleanups=[],this._isPaused=!1,this.parent=rt,!t&&rt&&(this.index=(rt.scopes||(rt.scopes=[])).push(this)-1)}get active(){return this._active}pause(){if(this._active){this._isPaused=!0;let t,n;if(this.scopes)for(t=0,n=this.scopes.length;t0)return;if(Vr){let t=Vr;for(Vr=void 0;t;){const n=t.next;t.next=void 0,t.flags&=-9,t=n}}let e;for(;Dr;){let t=Dr;for(Dr=void 0;t;){const n=t.next;if(t.next=void 0,t.flags&=-9,t.flags&1)try{t.trigger()}catch(r){e||(e=r)}t=n}}if(e)throw e}function dc(e){for(let t=e.deps;t;t=t.nextDep)t.version=-1,t.prevActiveLink=t.dep.activeLink,t.dep.activeLink=t}function fc(e){let t,n=e.depsTail,r=n;for(;r;){const o=r.prevDep;r.version===-1?(r===n&&(n=o),Ms(r),Kp(r)):t=r,r.dep.activeLink=r.prevActiveLink,r.prevActiveLink=void 0,r=o}e.deps=t,e.depsTail=n}function Qi(e){for(let t=e.deps;t;t=t.nextDep)if(t.dep.version!==t.version||t.dep.computed&&(pc(t.dep.computed)||t.dep.version!==t.version))return!0;return!!e._dirty}function pc(e){if(e.flags&4&&!(e.flags&16)||(e.flags&=-17,e.globalVersion===Ur))return;e.globalVersion=Ur;const t=e.dep;if(e.flags|=2,t.version>0&&!e.isSSR&&e.deps&&!Qi(e)){e.flags&=-3;return}const n=Oe,r=It;Oe=e,It=!0;try{dc(e);const o=e.fn(e._value);(t.version===0||mn(o,e._value))&&(e._value=o,t.version++)}catch(o){throw t.version++,o}finally{Oe=n,It=r,fc(e),e.flags&=-3}}function Ms(e,t=!1){const{dep:n,prevSub:r,nextSub:o}=e;if(r&&(r.nextSub=o,e.prevSub=void 0),o&&(o.prevSub=r,e.nextSub=void 0),n.subs===e&&(n.subs=r,!r&&n.computed)){n.computed.flags&=-5;for(let i=n.computed.deps;i;i=i.nextDep)Ms(i,!0)}!t&&!--n.sc&&n.map&&n.map.delete(n.key)}function Kp(e){const{prevDep:t,nextDep:n}=e;t&&(t.nextDep=n,e.prevDep=void 0),n&&(n.prevDep=t,e.nextDep=void 0)}let It=!0;const hc=[];function en(){hc.push(It),It=!1}function tn(){const e=hc.pop();It=e===void 0?!0:e}function xl(e){const{cleanup:t}=e;if(e.cleanup=void 0,t){const n=Oe;Oe=void 0;try{t()}finally{Oe=n}}}let Ur=0;class Wp{constructor(t,n){this.sub=t,this.dep=n,this.version=n.version,this.nextDep=this.prevDep=this.nextSub=this.prevSub=this.prevActiveLink=void 0}}class ii{constructor(t){this.computed=t,this.version=0,this.activeLink=void 0,this.subs=void 0,this.map=void 0,this.key=void 0,this.sc=0}track(t){if(!Oe||!It||Oe===this.computed)return;let n=this.activeLink;if(n===void 0||n.sub!==Oe)n=this.activeLink=new Wp(Oe,this),Oe.deps?(n.prevDep=Oe.depsTail,Oe.depsTail.nextDep=n,Oe.depsTail=n):Oe.deps=Oe.depsTail=n,mc(n);else if(n.version===-1&&(n.version=this.version,n.nextDep)){const r=n.nextDep;r.prevDep=n.prevDep,n.prevDep&&(n.prevDep.nextDep=r),n.prevDep=Oe.depsTail,n.nextDep=void 0,Oe.depsTail.nextDep=n,Oe.depsTail=n,Oe.deps===n&&(Oe.deps=r)}return n}trigger(t){this.version++,Ur++,this.notify(t)}notify(t){Vs();try{for(let n=this.subs;n;n=n.prevSub)n.sub.notify()&&n.sub.dep.notify()}finally{Ns()}}}function mc(e){if(e.dep.sc++,e.sub.flags&4){const t=e.dep.computed;if(t&&!e.dep.subs){t.flags|=20;for(let r=t.deps;r;r=r.nextDep)mc(r)}const n=e.dep.subs;n!==e&&(e.prevSub=n,n&&(n.nextSub=e)),e.dep.subs=e}}const jo=new WeakMap,Rn=Symbol(""),es=Symbol(""),Kr=Symbol("");function et(e,t,n){if(It&&Oe){let r=jo.get(e);r||jo.set(e,r=new Map);let o=r.get(n);o||(r.set(n,o=new ii),o.map=r,o.key=n),o.track()}}function Yt(e,t,n,r,o,i){const s=jo.get(e);if(!s){Ur++;return}const l=a=>{a&&a.trigger()};if(Vs(),t==="clear")s.forEach(l);else{const a=ie(e),u=a&&Rs(n);if(a&&n==="length"){const d=Number(r);s.forEach((c,f)=>{(f==="length"||f===Kr||!Jt(f)&&f>=d)&&l(c)})}else switch((n!==void 0||s.has(void 0))&&l(s.get(n)),u&&l(s.get(Kr)),t){case"add":a?u&&l(s.get("length")):(l(s.get(Rn)),tr(e)&&l(s.get(es)));break;case"delete":a||(l(s.get(Rn)),tr(e)&&l(s.get(es)));break;case"set":tr(e)&&l(s.get(Rn));break}}Ns()}function Gp(e,t){const n=jo.get(e);return n&&n.get(t)}function qn(e){const t=he(e);return t===e?t:(et(t,"iterate",Kr),kt(e)?t:t.map(tt))}function si(e){return et(e=he(e),"iterate",Kr),e}const qp={__proto__:null,[Symbol.iterator](){return ki(this,Symbol.iterator,tt)},concat(...e){return qn(this).concat(...e.map(t=>ie(t)?qn(t):t))},entries(){return ki(this,"entries",e=>(e[1]=tt(e[1]),e))},every(e,t){return jt(this,"every",e,t,void 0,arguments)},filter(e,t){return jt(this,"filter",e,t,n=>n.map(tt),arguments)},find(e,t){return jt(this,"find",e,t,tt,arguments)},findIndex(e,t){return jt(this,"findIndex",e,t,void 0,arguments)},findLast(e,t){return jt(this,"findLast",e,t,tt,arguments)},findLastIndex(e,t){return jt(this,"findLastIndex",e,t,void 0,arguments)},forEach(e,t){return jt(this,"forEach",e,t,void 0,arguments)},includes(...e){return Oi(this,"includes",e)},indexOf(...e){return Oi(this,"indexOf",e)},join(e){return qn(this).join(e)},lastIndexOf(...e){return Oi(this,"lastIndexOf",e)},map(e,t){return jt(this,"map",e,t,void 0,arguments)},pop(){return wr(this,"pop")},push(...e){return wr(this,"push",e)},reduce(e,...t){return Il(this,"reduce",e,t)},reduceRight(e,...t){return Il(this,"reduceRight",e,t)},shift(){return wr(this,"shift")},some(e,t){return jt(this,"some",e,t,void 0,arguments)},splice(...e){return wr(this,"splice",e)},toReversed(){return qn(this).toReversed()},toSorted(e){return qn(this).toSorted(e)},toSpliced(...e){return qn(this).toSpliced(...e)},unshift(...e){return wr(this,"unshift",e)},values(){return ki(this,"values",tt)}};function ki(e,t,n){const r=si(e),o=r[t]();return r!==e&&!kt(e)&&(o._next=o.next,o.next=()=>{const i=o._next();return i.value&&(i.value=n(i.value)),i}),o}const Yp=Array.prototype;function jt(e,t,n,r,o,i){const s=si(e),l=s!==e&&!kt(e),a=s[t];if(a!==Yp[t]){const c=a.apply(e,i);return l?tt(c):c}let u=n;s!==e&&(l?u=function(c,f){return n.call(this,tt(c),f,e)}:n.length>2&&(u=function(c,f){return n.call(this,c,f,e)}));const d=a.call(s,u,r);return l&&o?o(d):d}function Il(e,t,n,r){const o=si(e);let i=n;return o!==e&&(kt(e)?n.length>3&&(i=function(s,l,a){return n.call(this,s,l,a,e)}):i=function(s,l,a){return n.call(this,s,tt(l),a,e)}),o[t](i,...r)}function Oi(e,t,n){const r=he(e);et(r,"iterate",Kr);const o=r[t](...n);return(o===-1||o===!1)&&Bs(n[0])?(n[0]=he(n[0]),r[t](...n)):o}function wr(e,t,n=[]){en(),Vs();const r=he(e)[t].apply(e,n);return Ns(),tn(),r}const Xp=vr("__proto__,__v_isRef,__isVue"),_c=new Set(Object.getOwnPropertyNames(Symbol).filter(e=>e!=="arguments"&&e!=="caller").map(e=>Symbol[e]).filter(Jt));function Zp(e){Jt(e)||(e=String(e));const t=he(this);return et(t,"has",e),t.hasOwnProperty(e)}class gc{constructor(t=!1,n=!1){this._isReadonly=t,this._isShallow=n}get(t,n,r){if(n==="__v_skip")return t.__v_skip;const o=this._isReadonly,i=this._isShallow;if(n==="__v_isReactive")return!o;if(n==="__v_isReadonly")return o;if(n==="__v_isShallow")return i;if(n==="__v_raw")return r===(o?i?lh:yc:i?bc:Ec).get(t)||Object.getPrototypeOf(t)===Object.getPrototypeOf(r)?t:void 0;const s=ie(t);if(!o){let a;if(s&&(a=qp[n]))return a;if(n==="hasOwnProperty")return Zp}const l=Reflect.get(t,n,Me(t)?t:r);return(Jt(n)?_c.has(n):Xp(n))||(o||et(t,"get",n),i)?l:Me(l)?s&&Rs(n)?l:l.value:xe(l)?o?Un(l):zn(l):l}}class vc extends gc{constructor(t=!1){super(!1,t)}set(t,n,r,o){let i=t[n];if(!this._isShallow){const a=$n(i);if(!kt(r)&&!$n(r)&&(i=he(i),r=he(r)),!ie(t)&&Me(i)&&!Me(r))return a?!1:(i.value=r,!0)}const s=ie(t)&&Rs(n)?Number(n)e,vo=e=>Reflect.getPrototypeOf(e);function nh(e,t,n){return function(...r){const o=this.__v_raw,i=he(o),s=tr(i),l=e==="entries"||e===Symbol.iterator&&s,a=e==="keys"&&s,u=o[e](...r),d=n?ts:t?ns:tt;return!t&&et(i,"iterate",a?es:Rn),{next(){const{value:c,done:f}=u.next();return f?{value:c,done:f}:{value:l?[d(c[0]),d(c[1])]:d(c),done:f}},[Symbol.iterator](){return this}}}}function Eo(e){return function(...t){return e==="delete"?!1:e==="clear"?void 0:this}}function rh(e,t){const n={get(o){const i=this.__v_raw,s=he(i),l=he(o);e||(mn(o,l)&&et(s,"get",o),et(s,"get",l));const{has:a}=vo(s),u=t?ts:e?ns:tt;if(a.call(s,o))return u(i.get(o));if(a.call(s,l))return u(i.get(l));i!==s&&i.get(o)},get size(){const o=this.__v_raw;return!e&&et(he(o),"iterate",Rn),Reflect.get(o,"size",o)},has(o){const i=this.__v_raw,s=he(i),l=he(o);return e||(mn(o,l)&&et(s,"has",o),et(s,"has",l)),o===l?i.has(o):i.has(o)||i.has(l)},forEach(o,i){const s=this,l=s.__v_raw,a=he(l),u=t?ts:e?ns:tt;return!e&&et(a,"iterate",Rn),l.forEach((d,c)=>o.call(i,u(d),u(c),s))}};return Ye(n,e?{add:Eo("add"),set:Eo("set"),delete:Eo("delete"),clear:Eo("clear")}:{add(o){!t&&!kt(o)&&!$n(o)&&(o=he(o));const i=he(this);return vo(i).has.call(i,o)||(i.add(o),Yt(i,"add",o,o)),this},set(o,i){!t&&!kt(i)&&!$n(i)&&(i=he(i));const s=he(this),{has:l,get:a}=vo(s);let u=l.call(s,o);u||(o=he(o),u=l.call(s,o));const d=a.call(s,o);return s.set(o,i),u?mn(i,d)&&Yt(s,"set",o,i):Yt(s,"add",o,i),this},delete(o){const i=he(this),{has:s,get:l}=vo(i);let a=s.call(i,o);a||(o=he(o),a=s.call(i,o)),l&&l.call(i,o);const u=i.delete(o);return a&&Yt(i,"delete",o,void 0),u},clear(){const o=he(this),i=o.size!==0,s=o.clear();return i&&Yt(o,"clear",void 0,void 0),s}}),["keys","values","entries",Symbol.iterator].forEach(o=>{n[o]=nh(o,e,t)}),n}function zs(e,t){const n=rh(e,t);return(r,o,i)=>o==="__v_isReactive"?!e:o==="__v_isReadonly"?e:o==="__v_raw"?r:Reflect.get(be(n,o)&&o in r?n:r,o,i)}const oh={get:zs(!1,!1)},ih={get:zs(!1,!0)},sh={get:zs(!0,!1)};const Ec=new WeakMap,bc=new WeakMap,yc=new WeakMap,lh=new WeakMap;function ah(e){switch(e){case"Object":case"Array":return 1;case"Map":case"Set":case"WeakMap":case"WeakSet":return 2;default:return 0}}function uh(e){return e.__v_skip||!Object.isExtensible(e)?0:ah(Op(e))}function zn(e){return $n(e)?e:$s(e,!1,Qp,oh,Ec)}function Tc(e){return $s(e,!1,th,ih,bc)}function Un(e){return $s(e,!0,eh,sh,yc)}function $s(e,t,n,r,o){if(!xe(e)||e.__v_raw&&!(t&&e.__v_isReactive))return e;const i=o.get(e);if(i)return i;const s=uh(e);if(s===0)return e;const l=new Proxy(e,s===2?r:n);return o.set(e,l),l}function rr(e){return $n(e)?rr(e.__v_raw):!!(e&&e.__v_isReactive)}function $n(e){return!!(e&&e.__v_isReadonly)}function kt(e){return!!(e&&e.__v_isShallow)}function Bs(e){return e?!!e.__v_raw:!1}function he(e){const t=e&&e.__v_raw;return t?he(t):e}function ch(e){return!be(e,"__v_skip")&&Object.isExtensible(e)&&ar(e,"__v_skip",!0),e}const tt=e=>xe(e)?zn(e):e,ns=e=>xe(e)?Un(e):e;function Me(e){return e?e.__v_isRef===!0:!1}function ue(e){return wc(e,!1)}function ct(e){return wc(e,!0)}function wc(e,t){return Me(e)?e:new dh(e,t)}class dh{constructor(t,n){this.dep=new ii,this.__v_isRef=!0,this.__v_isShallow=!1,this._rawValue=n?t:he(t),this._value=n?t:tt(t),this.__v_isShallow=n}get value(){return this.dep.track(),this._value}set value(t){const n=this._rawValue,r=this.__v_isShallow||kt(t)||$n(t);t=r?t:he(t),mn(t,n)&&(this._rawValue=t,this._value=r?t:tt(t),this.dep.trigger())}}function pn(e){return Me(e)?e.value:e}function we(e){return le(e)?e():pn(e)}const fh={get:(e,t,n)=>t==="__v_raw"?e:pn(Reflect.get(e,t,n)),set:(e,t,n,r)=>{const o=e[t];return Me(o)&&!Me(n)?(o.value=n,!0):Reflect.set(e,t,n,r)}};function Ac(e){return rr(e)?e:new Proxy(e,fh)}class ph{constructor(t){this.__v_isRef=!0,this._value=void 0;const n=this.dep=new ii,{get:r,set:o}=t(n.track.bind(n),n.trigger.bind(n));this._get=r,this._set=o}get value(){return this._value=this._get()}set value(t){this._set(t)}}function Sc(e){return new ph(e)}function kc(e){const t=ie(e)?new Array(e.length):{};for(const n in e)t[n]=Pc(e,n);return t}class hh{constructor(t,n,r){this._object=t,this._key=n,this._defaultValue=r,this.__v_isRef=!0,this._value=void 0}get value(){const t=this._object[this._key];return this._value=t===void 0?this._defaultValue:t}set value(t){this._object[this._key]=t}get dep(){return Gp(he(this._object),this._key)}}class mh{constructor(t){this._getter=t,this.__v_isRef=!0,this.__v_isReadonly=!0,this._value=void 0}get value(){return this._value=this._getter()}}function Oc(e,t,n){return Me(e)?e:le(e)?new mh(e):xe(e)&&arguments.length>1?Pc(e,t,n):ue(e)}function Pc(e,t,n){const r=e[t];return Me(r)?r:new hh(e,t,n)}class _h{constructor(t,n,r){this.fn=t,this.setter=n,this._value=void 0,this.dep=new ii(this),this.__v_isRef=!0,this.deps=void 0,this.depsTail=void 0,this.flags=16,this.globalVersion=Ur-1,this.next=void 0,this.effect=this,this.__v_isReadonly=!n,this.isSSR=r}notify(){if(this.flags|=16,!(this.flags&8)&&Oe!==this)return cc(this,!0),!0}get value(){const t=this.dep.track();return pc(this),t&&(t.version=this.dep.version),this._value}set value(t){this.setter&&this.setter(t)}}function gh(e,t,n=!1){let r,o;return le(e)?r=e:(r=e.get,o=e.set),new _h(r,o,n)}const bo={},Uo=new WeakMap;let On;function vh(e,t=!1,n=On){if(n){let r=Uo.get(n);r||Uo.set(n,r=[]),r.push(e)}}function Eh(e,t,n=Ae){const{immediate:r,deep:o,once:i,scheduler:s,augmentJob:l,call:a}=n,u=T=>o?T:kt(T)||o===!1||o===0?Xt(T,1):Xt(T);let d,c,f,p,_=!1,g=!1;if(Me(e)?(c=()=>e.value,_=kt(e)):rr(e)?(c=()=>u(e),_=!0):ie(e)?(g=!0,_=e.some(T=>rr(T)||kt(T)),c=()=>e.map(T=>{if(Me(T))return T.value;if(rr(T))return u(T);if(le(T))return a?a(T,2):T()})):le(e)?t?c=a?()=>a(e,2):e:c=()=>{if(f){en();try{f()}finally{tn()}}const T=On;On=d;try{return a?a(e,3,[p]):e(p)}finally{On=T}}:c=Bt,t&&o){const T=c,R=o===!0?1/0:o;c=()=>Xt(T(),R)}const v=lc(),y=()=>{d.stop(),v&&v.active&&Ls(v.effects,d)};if(i&&t){const T=t;t=(...R)=>{T(...R),y()}}let w=g?new Array(e.length).fill(bo):bo;const h=T=>{if(!(!(d.flags&1)||!d.dirty&&!T))if(t){const R=d.run();if(o||_||(g?R.some((j,k)=>mn(j,w[k])):mn(R,w))){f&&f();const j=On;On=d;try{const k=[R,w===bo?void 0:g&&w[0]===bo?[]:w,p];a?a(t,3,k):t(...k),w=R}finally{On=j}}}else d.run()};return l&&l(h),d=new ac(c),d.scheduler=s?()=>s(h,!1):h,p=T=>vh(T,!1,d),f=d.onStop=()=>{const T=Uo.get(d);if(T){if(a)a(T,4);else for(const R of T)R();Uo.delete(d)}},t?r?h(!0):w=d.run():s?s(h.bind(null,!0),!0):d.run(),y.pause=d.pause.bind(d),y.resume=d.resume.bind(d),y.stop=y,y}function Xt(e,t=1/0,n){if(t<=0||!xe(e)||e.__v_skip||(n=n||new Set,n.has(e)))return e;if(n.add(e),t--,Me(e))Xt(e.value,t,n);else if(ie(e))for(let r=0;r{Xt(r,t,n)});else if(rc(e)){for(const r in e)Xt(e[r],t,n);for(const r of Object.getOwnPropertySymbols(e))Object.prototype.propertyIsEnumerable.call(e,r)&&Xt(e[r],t,n)}return e}/** +* @vue/runtime-core v3.5.13 +* (c) 2018-present Yuxi (Evan) You and Vue contributors +* @license MIT +**/const Nr=[];let Pi=!1;function ln(e,...t){if(Pi)return;Pi=!0,en();const n=Nr.length?Nr[Nr.length-1].component:null,r=n&&n.appContext.config.warnHandler,o=bh();if(r)Er(r,n,11,[e+t.map(i=>{var s,l;return(l=(s=i.toString)==null?void 0:s.call(i))!=null?l:JSON.stringify(i)}).join(""),n&&n.proxy,o.map(({vnode:i})=>`at <${bd(n,i.type)}>`).join(` +`),o]);else{const i=[`[Vue warn]: ${e}`,...t];o.length&&i.push(` +`,...yh(o)),console.warn(...i)}tn(),Pi=!1}function bh(){let e=Nr[Nr.length-1];if(!e)return[];const t=[];for(;e;){const n=t[0];n&&n.vnode===e?n.recurseCount++:t.push({vnode:e,recurseCount:0});const r=e.component&&e.component.parent;e=r&&r.vnode}return t}function yh(e){const t=[];return e.forEach((n,r)=>{t.push(...r===0?[]:[` +`],...Th(n))}),t}function Th({vnode:e,recurseCount:t}){const n=t>0?`... (${t} recursive calls)`:"",r=e.component?e.component.parent==null:!1,o=` at <${bd(e.component,e.type,r)}`,i=">"+n;return e.props?[o,...wh(e.props),i]:[o+i]}function wh(e){const t=[],n=Object.keys(e);return n.slice(0,3).forEach(r=>{t.push(...Cc(r,e[r]))}),n.length>3&&t.push(" ..."),t}function Cc(e,t,n){return Ce(t)?(t=JSON.stringify(t),n?t:[`${e}=${t}`]):typeof t=="number"||typeof t=="boolean"||t==null?n?t:[`${e}=${t}`]:Me(t)?(t=Cc(e,he(t.value),!0),n?t:[`${e}=Ref<`,t,">"]):le(t)?[`${e}=fn${t.name?`<${t.name}>`:""}`]:(t=he(t),n?t:[`${e}=`,t])}function Er(e,t,n,r){try{return r?e(...r):e()}catch(o){so(o,t,n)}}function Lt(e,t,n,r){if(le(e)){const o=Er(e,t,n,r);return o&&tc(o)&&o.catch(i=>{so(i,t,n)}),o}if(ie(e)){const o=[];for(let i=0;i>>1,o=ot[r],i=Wr(o);i=Wr(n)?ot.push(e):ot.splice(Sh(t),0,e),e.flags|=1,Ic()}}function Ic(){Ko||(Ko=xc.then(Lc))}function kh(e){ie(e)?or.push(...e):un&&e.id===-1?un.splice(Zn+1,0,e):e.flags&1||(or.push(e),e.flags|=1),Ic()}function Ll(e,t,n=Mt+1){for(;nWr(n)-Wr(r));if(or.length=0,un){un.push(...t);return}for(un=t,Zn=0;Zne.id==null?e.flags&2?-1:1/0:e.id;function Lc(e){try{for(Mt=0;Mt$t.emit(o,...i)),Cr=[]):typeof window<"u"&&window.HTMLElement&&!((r=(n=window.navigator)==null?void 0:n.userAgent)!=null&&r.includes("jsdom"))?((t.__VUE_DEVTOOLS_HOOK_REPLAY__=t.__VUE_DEVTOOLS_HOOK_REPLAY__||[]).push(i=>{Rc(i,t)}),setTimeout(()=>{$t||(t.__VUE_DEVTOOLS_HOOK_REPLAY__=null,rs=!0,Cr=[])},3e3)):(rs=!0,Cr=[])}function Oh(e,t){li("app:init",e,t,{Fragment:ye,Text:_n,Comment:Je,Static:ir})}function Ph(e){li("app:unmount",e)}const Ch=Hs("component:added"),Dc=Hs("component:updated"),xh=Hs("component:removed"),Ih=e=>{$t&&typeof $t.cleanupBuffer=="function"&&!$t.cleanupBuffer(e)&&xh(e)};/*! #__NO_SIDE_EFFECTS__ */function Hs(e){return t=>{li(e,t.appContext.app,t.uid,t.parent?t.parent.uid:void 0,t)}}function Lh(e,t,n){li("component:emit",e.appContext.app,e,t,n)}let qe=null,Vc=null;function Go(e){const t=qe;return qe=e,Vc=e&&e.type.__scopeId||null,t}function Ne(e,t=qe,n){if(!t||e._n)return e;const r=(...o)=>{r._d&&Gl(-1);const i=Go(t);let s;try{s=e(...o)}finally{Go(i),r._d&&Gl(1)}return Dc(t),s};return r._n=!0,r._c=!0,r._d=!0,r}function qo(e,t){if(qe===null)return e;const n=di(qe),r=e.dirs||(e.dirs=[]);for(let o=0;oe.__isTeleport,cn=Symbol("_leaveCb"),yo=Symbol("_enterCb");function Mc(){const e={isMounted:!1,isLeaving:!1,isUnmounting:!1,leavingVNodes:new Map};return Ke(()=>{e.isMounted=!0}),ui(()=>{e.isUnmounting=!0}),e}const yt=[Function,Array],zc={mode:String,appear:Boolean,persisted:Boolean,onBeforeEnter:yt,onEnter:yt,onAfterEnter:yt,onEnterCancelled:yt,onBeforeLeave:yt,onLeave:yt,onAfterLeave:yt,onLeaveCancelled:yt,onBeforeAppear:yt,onAppear:yt,onAfterAppear:yt,onAppearCancelled:yt},$c=e=>{const t=e.subTree;return t.component?$c(t.component):t},Dh={name:"BaseTransition",props:zc,setup(e,{slots:t}){const n=yn(),r=Mc();return()=>{const o=t.default&&js(t.default(),!0);if(!o||!o.length)return;const i=Bc(o),s=he(e),{mode:l}=s;if(r.isLeaving)return Ci(i);const a=Rl(i);if(!a)return Ci(i);let u=Gr(a,s,r,n,c=>u=c);a.type!==Je&&Bn(a,u);let d=n.subTree&&Rl(n.subTree);if(d&&d.type!==Je&&!In(a,d)&&$c(n).type!==Je){let c=Gr(d,s,r,n);if(Bn(d,c),l==="out-in"&&a.type!==Je)return r.isLeaving=!0,c.afterLeave=()=>{r.isLeaving=!1,n.job.flags&8||n.update(),delete c.afterLeave,d=void 0},Ci(i);l==="in-out"&&a.type!==Je?c.delayLeave=(f,p,_)=>{const g=Fc(r,d);g[String(d.key)]=d,f[cn]=()=>{p(),f[cn]=void 0,delete u.delayedLeave,d=void 0},u.delayedLeave=()=>{_(),delete u.delayedLeave,d=void 0}}:d=void 0}else d&&(d=void 0);return i}}};function Bc(e){let t=e[0];if(e.length>1){for(const n of e)if(n.type!==Je){t=n;break}}return t}const Vh=Dh;function Fc(e,t){const{leavingVNodes:n}=e;let r=n.get(t.type);return r||(r=Object.create(null),n.set(t.type,r)),r}function Gr(e,t,n,r,o){const{appear:i,mode:s,persisted:l=!1,onBeforeEnter:a,onEnter:u,onAfterEnter:d,onEnterCancelled:c,onBeforeLeave:f,onLeave:p,onAfterLeave:_,onLeaveCancelled:g,onBeforeAppear:v,onAppear:y,onAfterAppear:w,onAppearCancelled:h}=t,T=String(e.key),R=Fc(n,e),j=(V,$)=>{V&&Lt(V,r,9,$)},k=(V,$)=>{const I=$[1];j(V,$),ie(V)?V.every(O=>O.length<=1)&&I():V.length<=1&&I()},S={mode:s,persisted:l,beforeEnter(V){let $=a;if(!n.isMounted)if(i)$=v||a;else return;V[cn]&&V[cn](!0);const I=R[T];I&&In(e,I)&&I.el[cn]&&I.el[cn](),j($,[V])},enter(V){let $=u,I=d,O=c;if(!n.isMounted)if(i)$=y||u,I=w||d,O=h||c;else return;let P=!1;const X=V[yo]=te=>{P||(P=!0,te?j(O,[V]):j(I,[V]),S.delayedLeave&&S.delayedLeave(),V[yo]=void 0)};$?k($,[V,X]):X()},leave(V,$){const I=String(e.key);if(V[yo]&&V[yo](!0),n.isUnmounting)return $();j(f,[V]);let O=!1;const P=V[cn]=X=>{O||(O=!0,$(),X?j(g,[V]):j(_,[V]),V[cn]=void 0,R[I]===e&&delete R[I])};R[I]=e,p?k(p,[V,P]):P()},clone(V){const $=Gr(V,t,n,r,o);return o&&o($),$}};return S}function Ci(e){if(lo(e))return e=vn(e),e.children=null,e}function Rl(e){if(!lo(e))return Nc(e.type)&&e.children?Bc(e.children):e;const{shapeFlag:t,children:n}=e;if(n){if(t&16)return n[0];if(t&32&&le(n.default))return n.default()}}function Bn(e,t){e.shapeFlag&6&&e.component?(e.transition=t,Bn(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 js(e,t=!1,n){let r=[],o=0;for(let i=0;i1)for(let i=0;in.value,set:i=>n.value=i})}return n}function qr(e,t,n,r,o=!1){if(ie(e)){e.forEach((_,g)=>qr(_,t&&(ie(t)?t[g]:t),n,r,o));return}if(Dn(r)&&!o){r.shapeFlag&512&&r.type.__asyncResolved&&r.component.subTree.component&&qr(e,t,n,r.component.subTree);return}const i=r.shapeFlag&4?di(r.component):r.el,s=o?null:i,{i:l,r:a}=e,u=t&&t.r,d=l.refs===Ae?l.refs={}:l.refs,c=l.setupState,f=he(c),p=c===Ae?()=>!1:_=>be(f,_);if(u!=null&&u!==a&&(Ce(u)?(d[u]=null,p(u)&&(c[u]=null)):Me(u)&&(u.value=null)),le(a))Er(a,l,12,[s,d]);else{const _=Ce(a),g=Me(a);if(_||g){const v=()=>{if(e.f){const y=_?p(a)?c[a]:d[a]:a.value;o?ie(y)&&Ls(y,i):ie(y)?y.includes(i)||y.push(i):_?(d[a]=[i],p(a)&&(c[a]=d[a])):(a.value=[i],e.k&&(d[e.k]=a.value))}else _?(d[a]=s,p(a)&&(c[a]=s)):g&&(a.value=s,e.k&&(d[e.k]=s))};s?(v.id=-1,ht(v,n)):v()}}}let Vl=!1;const wn=()=>{Vl||(console.error("Hydration completed but contains mismatches."),Vl=!0)},Nh=e=>e.namespaceURI.includes("svg")&&e.tagName!=="foreignObject",Mh=e=>e.namespaceURI.includes("MathML"),To=e=>{if(e.nodeType===1){if(Nh(e))return"svg";if(Mh(e))return"mathml"}},xn=e=>e.nodeType===8;function zh(e){const{mt:t,p:n,o:{patchProp:r,createText:o,nextSibling:i,parentNode:s,remove:l,insert:a,createComment:u}}=e,d=(h,T)=>{if(!T.hasChildNodes()){ln("Attempting to hydrate existing markup but container is empty. Performing full mount instead."),n(null,h,T),Wo(),T._vnode=h;return}c(T.firstChild,h,null,null,null),Wo(),T._vnode=h},c=(h,T,R,j,k,S=!1)=>{S=S||!!T.dynamicChildren;const V=xn(h)&&h.data==="[",$=()=>g(h,T,R,j,k,V),{type:I,ref:O,shapeFlag:P,patchFlag:X}=T;let te=h.nodeType;T.el=h,ar(h,"__vnode",T,!0),ar(h,"__vueParentComponent",R,!0),X===-2&&(S=!1,T.dynamicChildren=null);let L=null;switch(I){case _n:te!==3?T.children===""?(a(T.el=o(""),s(h),h),L=h):L=$():(h.data!==T.children&&(ln("Hydration text mismatch in",h.parentNode,` + - rendered on server: ${JSON.stringify(h.data)} + - expected on client: ${JSON.stringify(T.children)}`),wn(),h.data=T.children),L=i(h));break;case Je:w(h)?(L=i(h),y(T.el=h.content.firstChild,h,R)):te!==8||V?L=$():L=i(h);break;case ir:if(V&&(h=i(h),te=h.nodeType),te===1||te===3){L=h;const D=!T.children.length;for(let K=0;K{S=S||!!T.dynamicChildren;const{type:V,props:$,patchFlag:I,shapeFlag:O,dirs:P,transition:X}=T,te=V==="input"||V==="option";if(te||I!==-1){P&&zt(T,null,R,"created");let L=!1;if(w(h)){L=sd(null,X)&&R&&R.vnode.props&&R.vnode.props.appear;const K=h.content.firstChild;L&&X.beforeEnter(K),y(K,h,R),T.el=h=K}if(O&16&&!($&&($.innerHTML||$.textContent))){let K=p(h.firstChild,T,h,R,j,k,S),re=!1;for(;K;){xr(h,1)||(re||(ln("Hydration children mismatch on",h,` +Server rendered element contains more child nodes than client vdom.`),re=!0),wn());const Ee=K;K=K.nextSibling,l(Ee)}}else if(O&8){let K=T.children;K[0]===` +`&&(h.tagName==="PRE"||h.tagName==="TEXTAREA")&&(K=K.slice(1)),h.textContent!==K&&(xr(h,0)||(ln("Hydration text content mismatch on",h,` + - rendered on server: ${h.textContent} + - expected on client: ${T.children}`),wn()),h.textContent=T.children)}if($){const K=h.tagName.includes("-");for(const re in $)!(P&&P.some(Ee=>Ee.dir.created))&&$h(h,re,$[re],T,R)&&wn(),(te&&(re.endsWith("value")||re==="indeterminate")||oo(re)&&!nr(re)||re[0]==="."||K)&&r(h,re,null,$[re],void 0,R)}let D;(D=$&&$.onVnodeBeforeMount)&&Tt(D,R,T),P&&zt(T,null,R,"beforeMount"),((D=$&&$.onVnodeMounted)||P||L)&&hd(()=>{D&&Tt(D,R,T),L&&X.enter(h),P&&zt(T,null,R,"mounted")},j)}return h.nextSibling},p=(h,T,R,j,k,S,V)=>{V=V||!!T.dynamicChildren;const $=T.children,I=$.length;let O=!1;for(let P=0;P{const{slotScopeIds:V}=T;V&&(k=k?k.concat(V):V);const $=s(h),I=p(i(h),T,$,R,j,k,S);return I&&xn(I)&&I.data==="]"?i(T.anchor=I):(wn(),a(T.anchor=u("]"),$,I),I)},g=(h,T,R,j,k,S)=>{if(xr(h.parentElement,1)||(ln(`Hydration node mismatch: +- rendered on server:`,h,h.nodeType===3?"(text)":xn(h)&&h.data==="["?"(start of fragment)":"",` +- expected on client:`,T.type),wn()),T.el=null,S){const I=v(h);for(;;){const O=i(h);if(O&&O!==I)l(O);else break}}const V=i(h),$=s(h);return l(h),n(null,T,$,V,R,j,To($),k),R&&(R.vnode.el=T.el,fd(R,T.el)),V},v=(h,T="[",R="]")=>{let j=0;for(;h;)if(h=i(h),h&&xn(h)&&(h.data===T&&j++,h.data===R)){if(j===0)return i(h);j--}return h},y=(h,T,R)=>{const j=T.parentNode;j&&j.replaceChild(h,T);let k=R;for(;k;)k.vnode.el===T&&(k.vnode.el=k.subTree.el=h),k=k.parent},w=h=>h.nodeType===1&&h.tagName==="TEMPLATE";return[d,c]}function $h(e,t,n,r,o){let i,s,l,a;if(t==="class")l=e.getAttribute("class"),a=st(n),Bh(Nl(l||""),Nl(a))||(i=2,s="class");else if(t==="style"){l=e.getAttribute("style")||"",a=Ce(n)?n:Np(jn(n));const u=Ml(l),d=Ml(a);if(r.dirs)for(const{dir:c,value:f}of r.dirs)c.name==="show"&&!f&&d.set("display","none");o&&Hc(o,r,d),Fh(u,d)||(i=3,s="style")}else(e instanceof SVGElement&&$p(t)||e instanceof HTMLElement&&(Cl(t)||zp(t)))&&(Cl(t)?(l=e.hasAttribute(t),a=Ds(n)):n==null?(l=e.hasAttribute(t),a=!1):(e.hasAttribute(t)?l=e.getAttribute(t):t==="value"&&e.tagName==="TEXTAREA"?l=e.value:l=!1,a=Bp(n)?String(n):!1),l!==a&&(i=4,s=t));if(i!=null&&!xr(e,i)){const u=f=>f===!1?"(not rendered)":`${s}="${f}"`,d=`Hydration ${jc[i]} mismatch on`,c=` + - rendered on server: ${u(l)} + - expected on client: ${u(a)} + Note: this mismatch is check-only. The DOM will not be rectified in production due to performance overhead. + You should fix the source of the mismatch.`;return ln(d,e,c),!0}return!1}function Nl(e){return new Set(e.trim().split(/\s+/))}function Bh(e,t){if(e.size!==t.size)return!1;for(const n of e)if(!t.has(n))return!1;return!0}function Ml(e){const t=new Map;for(const n of e.split(";")){let[r,o]=n.split(":");r=r.trim(),o=o&&o.trim(),r&&o&&t.set(r,o)}return t}function Fh(e,t){if(e.size!==t.size)return!1;for(const[n,r]of e)if(r!==t.get(n))return!1;return!0}function Hc(e,t,n){const r=e.subTree;if(e.getCssVars&&(t===r||r&&r.type===ye&&r.children.includes(t))){const o=e.getCssVars();for(const i in o)n.set(`--${Hp(i)}`,String(o[i]))}t===r&&e.parent&&Hc(e.parent,e.vnode,n)}const zl="data-allow-mismatch",jc={0:"text",1:"children",2:"class",3:"style",4:"attribute"};function xr(e,t){if(t===0||t===1)for(;e&&!e.hasAttribute(zl);)e=e.parentElement;const n=e&&e.getAttribute(zl);if(n==null)return!1;if(n==="")return!0;{const r=n.split(",");return t===0&&r.includes("children")?!0:n.split(",").includes(jc[t])}}oi().requestIdleCallback;oi().cancelIdleCallback;function Hh(e,t){if(xn(e)&&e.data==="["){let n=1,r=e.nextSibling;for(;r;){if(r.nodeType===1){if(t(r)===!1)break}else if(xn(r))if(r.data==="]"){if(--n===0)break}else r.data==="["&&n++;r=r.nextSibling}}else t(e)}const Dn=e=>!!e.type.__asyncLoader;/*! #__NO_SIDE_EFFECTS__ */function Pn(e){le(e)&&(e={loader:e});const{loader:t,loadingComponent:n,errorComponent:r,delay:o=200,hydrate:i,timeout:s,suspensible:l=!0,onError:a}=e;let u=null,d,c=0;const f=()=>(c++,u=null,p()),p=()=>{let _;return u||(_=u=t().catch(g=>{if(g=g instanceof Error?g:new Error(String(g)),a)return new Promise((v,y)=>{a(g,()=>v(f()),()=>y(g),c+1)});throw g}).then(g=>_!==u&&u?u:(g&&(g.__esModule||g[Symbol.toStringTag]==="Module")&&(g=g.default),d=g,g)))};return fe({name:"AsyncComponentWrapper",__asyncLoader:p,__asyncHydrate(_,g,v){const y=i?()=>{const w=i(v,h=>Hh(_,h));w&&(g.bum||(g.bum=[])).push(w)}:v;d?y():p().then(()=>!g.isUnmounted&&y())},get __asyncResolved(){return d},setup(){const _=We;if(Us(_),d)return()=>xi(d,_);const g=h=>{u=null,so(h,_,13,!r)};if(l&&_.suspense||ur)return p().then(h=>()=>xi(h,_)).catch(h=>(g(h),()=>r?ae(r,{error:h}):null));const v=ue(!1),y=ue(),w=ue(!!o);return o&&setTimeout(()=>{w.value=!1},o),s!=null&&setTimeout(()=>{if(!v.value&&!y.value){const h=new Error(`Async component timed out after ${s}ms.`);g(h),y.value=h}},s),p().then(()=>{v.value=!0,_.parent&&lo(_.parent.vnode)&&_.parent.update()}).catch(h=>{g(h),y.value=h}),()=>{if(v.value&&d)return xi(d,_);if(y.value&&r)return ae(r,{error:y.value});if(n&&!w.value)return ae(n)}}})}function xi(e,t){const{ref:n,props:r,children:o,ce:i}=t.vnode,s=ae(e,r,o);return s.ref=n,s.ce=i,delete t.vnode.ce,s}const lo=e=>e.type.__isKeepAlive;function jh(e,t){Uc(e,"a",t)}function Uh(e,t){Uc(e,"da",t)}function Uc(e,t,n=We){const r=e.__wdc||(e.__wdc=()=>{let o=n;for(;o;){if(o.isDeactivated)return;o=o.parent}return e()});if(ai(t,r,n),n){let o=n.parent;for(;o&&o.parent;)lo(o.parent.vnode)&&Kh(r,t,n,o),o=o.parent}}function Kh(e,t,n,r){const o=ai(t,e,r,!0);Wn(()=>{Ls(r[t],o)},n)}function ai(e,t,n=We,r=!1){if(n){const o=n[e]||(n[e]=[]),i=t.__weh||(t.__weh=(...s)=>{en();const l=ao(n),a=Lt(t,n,e,s);return l(),tn(),a});return r?o.unshift(i):o.push(i),i}}const nn=e=>(t,n=We)=>{(!ur||e==="sp")&&ai(e,(...r)=>t(...r),n)},Wh=nn("bm"),Ke=nn("m"),Gh=nn("bu"),Kc=nn("u"),ui=nn("bum"),Wn=nn("um"),qh=nn("sp"),Yh=nn("rtg"),Xh=nn("rtc");function Zh(e,t=We){ai("ec",e,t)}const Jh="components";function Fn(e,t){return em(Jh,e,!0,t)||e}const Qh=Symbol.for("v-ndc");function em(e,t,n=!0,r=!1){const o=qe||We;if(o){const i=o.type;{const l=Ed(i,!1);if(l&&(l===t||l===ut(t)||l===io(ut(t))))return i}const s=$l(o[e]||i[e],t)||$l(o.appContext[e],t);return!s&&r?i:s}}function $l(e,t){return e&&(e[t]||e[ut(t)]||e[io(ut(t))])}function gn(e,t,n,r){let o;const i=n,s=ie(e);if(s||Ce(e)){const l=s&&rr(e);let a=!1;l&&(a=!kt(e),e=si(e)),o=new Array(e.length);for(let u=0,d=e.length;ut(l,a,void 0,i));else{const l=Object.keys(e);o=new Array(l.length);for(let a=0,u=l.length;a{const i=r.fn(...o);return i&&(i.key=r.key),i}:r.fn)}return e}function De(e,t,n={},r,o){if(qe.ce||qe.parent&&Dn(qe.parent)&&qe.parent.ce)return t!=="default"&&(n.name=t),q(),Se(ye,null,[ae("slot",n,r&&r())],64);let i=e[t];i&&i._c&&(i._d=!1),q();const s=i&&Wc(i(n)),l=n.key||s&&s.key,a=Se(ye,{key:(l&&!Jt(l)?l:`_${t}`)+(!s&&r?"_fb":"")},s||(r?r():[]),s&&e._===1?64:-2);return a.scopeId&&(a.slotScopeIds=[a.scopeId+"-s"]),i&&i._c&&(i._d=!0),a}function Wc(e){return e.some(t=>Xr(t)?!(t.type===Je||t.type===ye&&!Wc(t.children)):!0)?e:null}const os=e=>e?gd(e)?di(e):os(e.parent):null,Mr=Ye(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=>os(e.parent),$root:e=>os(e.root),$host:e=>e.ce,$emit:e=>e.emit,$options:e=>qc(e),$forceUpdate:e=>e.f||(e.f=()=>{Fs(e.update)}),$nextTick:e=>e.n||(e.n=Kn.bind(e.proxy)),$watch:e=>ym.bind(e)}),Ii=(e,t)=>e!==Ae&&!e.__isScriptSetup&&be(e,t),nm={get({_:e},t){if(t==="__v_skip")return!0;const{ctx:n,setupState:r,data:o,props:i,accessCache:s,type:l,appContext:a}=e;let u;if(t[0]!=="$"){const p=s[t];if(p!==void 0)switch(p){case 1:return r[t];case 2:return o[t];case 4:return n[t];case 3:return i[t]}else{if(Ii(r,t))return s[t]=1,r[t];if(o!==Ae&&be(o,t))return s[t]=2,o[t];if((u=e.propsOptions[0])&&be(u,t))return s[t]=3,i[t];if(n!==Ae&&be(n,t))return s[t]=4,n[t];is&&(s[t]=0)}}const d=Mr[t];let c,f;if(d)return t==="$attrs"&&et(e.attrs,"get",""),d(e);if((c=l.__cssModules)&&(c=c[t]))return c;if(n!==Ae&&be(n,t))return s[t]=4,n[t];if(f=a.config.globalProperties,be(f,t))return f[t]},set({_:e},t,n){const{data:r,setupState:o,ctx:i}=e;return Ii(o,t)?(o[t]=n,!0):r!==Ae&&be(r,t)?(r[t]=n,!0):be(e.props,t)||t[0]==="$"&&t.slice(1)in e?!1:(i[t]=n,!0)},has({_:{data:e,setupState:t,accessCache:n,ctx:r,appContext:o,propsOptions:i}},s){let l;return!!n[s]||e!==Ae&&be(e,s)||Ii(t,s)||(l=i[0])&&be(l,s)||be(r,s)||be(Mr,s)||be(o.config.globalProperties,s)},defineProperty(e,t,n){return n.get!=null?e._.accessCache[t]=0:be(n,"value")&&this.set(e,t,n.value,null),Reflect.defineProperty(e,t,n)}};function Bl(e){return ie(e)?e.reduce((t,n)=>(t[n]=null,t),{}):e}let is=!0;function rm(e){const t=qc(e),n=e.proxy,r=e.ctx;is=!1,t.beforeCreate&&Fl(t.beforeCreate,e,"bc");const{data:o,computed:i,methods:s,watch:l,provide:a,inject:u,created:d,beforeMount:c,mounted:f,beforeUpdate:p,updated:_,activated:g,deactivated:v,beforeDestroy:y,beforeUnmount:w,destroyed:h,unmounted:T,render:R,renderTracked:j,renderTriggered:k,errorCaptured:S,serverPrefetch:V,expose:$,inheritAttrs:I,components:O,directives:P,filters:X}=t;if(u&&om(u,r,null),s)for(const D in s){const K=s[D];le(K)&&(r[D]=K.bind(n))}if(o){const D=o.call(n,n);xe(D)&&(e.data=zn(D))}if(is=!0,i)for(const D in i){const K=i[D],re=le(K)?K.bind(n,n):le(K.get)?K.get.bind(n,n):Bt,Ee=!le(K)&&le(K.set)?K.set.bind(n):Bt,Ie=N({get:re,set:Ee});Object.defineProperty(r,D,{enumerable:!0,configurable:!0,get:()=>Ie.value,set:_e=>Ie.value=_e})}if(l)for(const D in l)Gc(l[D],r,n,D);if(a){const D=le(a)?a.call(n):a;Reflect.ownKeys(D).forEach(K=>{Nn(K,D[K])})}d&&Fl(d,e,"c");function L(D,K){ie(K)?K.forEach(re=>D(re.bind(n))):K&&D(K.bind(n))}if(L(Wh,c),L(Ke,f),L(Gh,p),L(Kc,_),L(jh,g),L(Uh,v),L(Zh,S),L(Xh,j),L(Yh,k),L(ui,w),L(Wn,T),L(qh,V),ie($))if($.length){const D=e.exposed||(e.exposed={});$.forEach(K=>{Object.defineProperty(D,K,{get:()=>n[K],set:re=>n[K]=re})})}else e.exposed||(e.exposed={});R&&e.render===Bt&&(e.render=R),I!=null&&(e.inheritAttrs=I),O&&(e.components=O),P&&(e.directives=P),V&&Us(e)}function om(e,t,n=Bt){ie(e)&&(e=ss(e));for(const r in e){const o=e[r];let i;xe(o)?"default"in o?i=Ue(o.from||r,o.default,!0):i=Ue(o.from||r):i=Ue(o),Me(i)?Object.defineProperty(t,r,{enumerable:!0,configurable:!0,get:()=>i.value,set:s=>i.value=s}):t[r]=i}}function Fl(e,t,n){Lt(ie(e)?e.map(r=>r.bind(t.proxy)):e.bind(t.proxy),t,n)}function Gc(e,t,n,r){let o=r.includes(".")?cd(n,r):()=>n[r];if(Ce(e)){const i=t[e];le(i)&&Be(o,i)}else if(le(e))Be(o,e.bind(n));else if(xe(e))if(ie(e))e.forEach(i=>Gc(i,t,n,r));else{const i=le(e.handler)?e.handler.bind(n):t[e.handler];le(i)&&Be(o,i,e)}}function qc(e){const t=e.type,{mixins:n,extends:r}=t,{mixins:o,optionsCache:i,config:{optionMergeStrategies:s}}=e.appContext,l=i.get(t);let a;return l?a=l:!o.length&&!n&&!r?a=t:(a={},o.length&&o.forEach(u=>Yo(a,u,s,!0)),Yo(a,t,s)),xe(t)&&i.set(t,a),a}function Yo(e,t,n,r=!1){const{mixins:o,extends:i}=t;i&&Yo(e,i,n,!0),o&&o.forEach(s=>Yo(e,s,n,!0));for(const s in t)if(!(r&&s==="expose")){const l=im[s]||n&&n[s];e[s]=l?l(e[s],t[s]):t[s]}return e}const im={data:Hl,props:jl,emits:jl,methods:Ir,computed:Ir,beforeCreate:nt,created:nt,beforeMount:nt,mounted:nt,beforeUpdate:nt,updated:nt,beforeDestroy:nt,beforeUnmount:nt,destroyed:nt,unmounted:nt,activated:nt,deactivated:nt,errorCaptured:nt,serverPrefetch:nt,components:Ir,directives:Ir,watch:lm,provide:Hl,inject:sm};function Hl(e,t){return t?e?function(){return Ye(le(e)?e.call(this,this):e,le(t)?t.call(this,this):t)}:t:e}function sm(e,t){return Ir(ss(e),ss(t))}function ss(e){if(ie(e)){const t={};for(let n=0;n1)return n&&le(t)?t.call(r&&r.proxy):t}}function Xc(){return!!(We||qe||Vn)}const Zc={},Jc=()=>Object.create(Zc),Qc=e=>Object.getPrototypeOf(e)===Zc;function cm(e,t,n,r=!1){const o={},i=Jc();e.propsDefaults=Object.create(null),ed(e,t,o,i);for(const s in e.propsOptions[0])s in o||(o[s]=void 0);n?e.props=r?o:Tc(o):e.type.props?e.props=o:e.props=i,e.attrs=i}function dm(e,t,n,r){const{props:o,attrs:i,vnode:{patchFlag:s}}=e,l=he(o),[a]=e.propsOptions;let u=!1;if((r||s>0)&&!(s&16)){if(s&8){const d=e.vnode.dynamicProps;for(let c=0;c{a=!0;const[f,p]=td(c,t,!0);Ye(s,f),p&&l.push(...p)};!n&&t.mixins.length&&t.mixins.forEach(d),e.extends&&d(e.extends),e.mixins&&e.mixins.forEach(d)}if(!i&&!a)return xe(e)&&r.set(e,er),er;if(ie(i))for(let d=0;de[0]==="_"||e==="$stable",Ks=e=>ie(e)?e.map(At):[At(e)],pm=(e,t,n)=>{if(t._n)return t;const r=Ne((...o)=>Ks(t(...o)),n);return r._c=!1,r},rd=(e,t,n)=>{const r=e._ctx;for(const o in e){if(nd(o))continue;const i=e[o];if(le(i))t[o]=pm(o,i,r);else if(i!=null){const s=Ks(i);t[o]=()=>s}}},od=(e,t)=>{const n=Ks(t);e.slots.default=()=>n},id=(e,t,n)=>{for(const r in t)(n||r!=="_")&&(e[r]=t[r])},hm=(e,t,n)=>{const r=e.slots=Jc();if(e.vnode.shapeFlag&32){const o=t._;o?(id(r,t,n),n&&ar(r,"_",o,!0)):rd(t,r)}else t&&od(e,t)},mm=(e,t,n)=>{const{vnode:r,slots:o}=e;let i=!0,s=Ae;if(r.shapeFlag&32){const l=t._;l?n&&l===1?i=!1:id(o,t,n):(i=!t.$stable,rd(t,o)),s=t}else t&&(od(e,t),s={default:1});if(i)for(const l in o)!nd(l)&&s[l]==null&&delete o[l]},ht=hd;function _m(e){return gm(e,zh)}function gm(e,t){const n=oi();n.__VUE__=!0,Rc(n.__VUE_DEVTOOLS_GLOBAL_HOOK__,n);const{insert:r,remove:o,patchProp:i,createElement:s,createText:l,createComment:a,setText:u,setElementText:d,parentNode:c,nextSibling:f,setScopeId:p=Bt,insertStaticContent:_}=e,g=(m,E,A,M=null,x=null,B=null,Z=void 0,W=null,U=!!E.dynamicChildren)=>{if(m===E)return;m&&!In(m,E)&&(M=C(m),_e(m,x,B,!0),m=null),E.patchFlag===-2&&(U=!1,E.dynamicChildren=null);const{type:F,ref:oe,shapeFlag:J}=E;switch(F){case _n:v(m,E,A,M);break;case Je:y(m,E,A,M);break;case ir:m==null&&w(E,A,M,Z);break;case ye:O(m,E,A,M,x,B,Z,W,U);break;default:J&1?R(m,E,A,M,x,B,Z,W,U):J&6?P(m,E,A,M,x,B,Z,W,U):(J&64||J&128)&&F.process(m,E,A,M,x,B,Z,W,U,ee)}oe!=null&&x&&qr(oe,m&&m.ref,B,E||m,!E)},v=(m,E,A,M)=>{if(m==null)r(E.el=l(E.children),A,M);else{const x=E.el=m.el;E.children!==m.children&&u(x,E.children)}},y=(m,E,A,M)=>{m==null?r(E.el=a(E.children||""),A,M):E.el=m.el},w=(m,E,A,M)=>{[m.el,m.anchor]=_(m.children,E,A,M,m.el,m.anchor)},h=({el:m,anchor:E},A,M)=>{let x;for(;m&&m!==E;)x=f(m),r(m,A,M),m=x;r(E,A,M)},T=({el:m,anchor:E})=>{let A;for(;m&&m!==E;)A=f(m),o(m),m=A;o(E)},R=(m,E,A,M,x,B,Z,W,U)=>{E.type==="svg"?Z="svg":E.type==="math"&&(Z="mathml"),m==null?j(E,A,M,x,B,Z,W,U):V(m,E,x,B,Z,W,U)},j=(m,E,A,M,x,B,Z,W)=>{let U,F;const{props:oe,shapeFlag:J,transition:ne,dirs:ce}=m;if(U=m.el=s(m.type,B,oe&&oe.is,oe),J&8?d(U,m.children):J&16&&S(m.children,U,null,M,x,Li(m,B),Z,W),ce&&zt(m,null,M,"created"),k(U,m,m.scopeId,Z,M),oe){for(const ke in oe)ke!=="value"&&!nr(ke)&&i(U,ke,null,oe[ke],B,M);"value"in oe&&i(U,"value",null,oe.value,B),(F=oe.onVnodeBeforeMount)&&Tt(F,M,m)}ar(U,"__vnode",m,!0),ar(U,"__vueParentComponent",M,!0),ce&&zt(m,null,M,"beforeMount");const me=sd(x,ne);me&&ne.beforeEnter(U),r(U,E,A),((F=oe&&oe.onVnodeMounted)||me||ce)&&ht(()=>{F&&Tt(F,M,m),me&&ne.enter(U),ce&&zt(m,null,M,"mounted")},x)},k=(m,E,A,M,x)=>{if(A&&p(m,A),M)for(let B=0;B{for(let F=U;F{const W=E.el=m.el;W.__vnode=E;let{patchFlag:U,dynamicChildren:F,dirs:oe}=E;U|=m.patchFlag&16;const J=m.props||Ae,ne=E.props||Ae;let ce;if(A&&An(A,!1),(ce=ne.onVnodeBeforeUpdate)&&Tt(ce,A,E,m),oe&&zt(E,m,A,"beforeUpdate"),A&&An(A,!0),(J.innerHTML&&ne.innerHTML==null||J.textContent&&ne.textContent==null)&&d(W,""),F?$(m.dynamicChildren,F,W,A,M,Li(E,x),B):Z||K(m,E,W,null,A,M,Li(E,x),B,!1),U>0){if(U&16)I(W,J,ne,A,x);else if(U&2&&J.class!==ne.class&&i(W,"class",null,ne.class,x),U&4&&i(W,"style",J.style,ne.style,x),U&8){const me=E.dynamicProps;for(let ke=0;ke{ce&&Tt(ce,A,E,m),oe&&zt(E,m,A,"updated")},M)},$=(m,E,A,M,x,B,Z)=>{for(let W=0;W{if(E!==A){if(E!==Ae)for(const B in E)!nr(B)&&!(B in A)&&i(m,B,E[B],null,x,M);for(const B in A){if(nr(B))continue;const Z=A[B],W=E[B];Z!==W&&B!=="value"&&i(m,B,W,Z,x,M)}"value"in A&&i(m,"value",E.value,A.value,x)}},O=(m,E,A,M,x,B,Z,W,U)=>{const F=E.el=m?m.el:l(""),oe=E.anchor=m?m.anchor:l("");let{patchFlag:J,dynamicChildren:ne,slotScopeIds:ce}=E;ce&&(W=W?W.concat(ce):ce),m==null?(r(F,A,M),r(oe,A,M),S(E.children||[],A,oe,x,B,Z,W,U)):J>0&&J&64&&ne&&m.dynamicChildren?($(m.dynamicChildren,ne,A,x,B,Z,W),(E.key!=null||x&&E===x.subTree)&&ld(m,E,!0)):K(m,E,A,oe,x,B,Z,W,U)},P=(m,E,A,M,x,B,Z,W,U)=>{E.slotScopeIds=W,m==null?E.shapeFlag&512?x.ctx.activate(E,A,M,Z,U):X(E,A,M,x,B,Z,U):te(m,E,U)},X=(m,E,A,M,x,B,Z)=>{const W=m.component=Rm(m,M,x);if(lo(m)&&(W.ctx.renderer=ee),Dm(W,!1,Z),W.asyncDep){if(x&&x.registerDep(W,L,Z),!m.el){const U=W.subTree=ae(Je);y(null,U,E,A)}}else L(W,m,E,A,x,B,Z)},te=(m,E,A)=>{const M=E.component=m.component;if(km(m,E,A))if(M.asyncDep&&!M.asyncResolved){D(M,E,A);return}else M.next=E,M.update();else E.el=m.el,M.vnode=E},L=(m,E,A,M,x,B,Z)=>{const W=()=>{if(m.isMounted){let{next:J,bu:ne,u:ce,parent:me,vnode:ke}=m;{const ft=ad(m);if(ft){J&&(J.el=ke.el,D(m,J,Z)),ft.asyncDep.then(()=>{m.isUnmounted||W()});return}}let Te=J,dt;An(m,!1),J?(J.el=ke.el,D(m,J,Z)):J=ke,ne&&Ti(ne),(dt=J.props&&J.props.onVnodeBeforeUpdate)&&Tt(dt,me,J,ke),An(m,!0);const Qe=Ri(m),Ct=m.subTree;m.subTree=Qe,g(Ct,Qe,c(Ct.el),C(Ct),m,x,B),J.el=Qe.el,Te===null&&fd(m,Qe.el),ce&&ht(ce,x),(dt=J.props&&J.props.onVnodeUpdated)&&ht(()=>Tt(dt,me,J,ke),x),Dc(m)}else{let J;const{el:ne,props:ce}=E,{bm:me,m:ke,parent:Te,root:dt,type:Qe}=m,Ct=Dn(E);if(An(m,!1),me&&Ti(me),!Ct&&(J=ce&&ce.onVnodeBeforeMount)&&Tt(J,Te,E),An(m,!0),ne&&ge){const ft=()=>{m.subTree=Ri(m),ge(ne,m.subTree,m,x,null)};Ct&&Qe.__asyncHydrate?Qe.__asyncHydrate(ne,m,ft):ft()}else{dt.ce&&dt.ce._injectChildStyle(Qe);const ft=m.subTree=Ri(m);g(null,ft,A,M,m,x,B),E.el=ft.el}if(ke&&ht(ke,x),!Ct&&(J=ce&&ce.onVnodeMounted)){const ft=E;ht(()=>Tt(J,Te,ft),x)}(E.shapeFlag&256||Te&&Dn(Te.vnode)&&Te.vnode.shapeFlag&256)&&m.a&&ht(m.a,x),m.isMounted=!0,Ch(m),E=A=M=null}};m.scope.on();const U=m.effect=new ac(W);m.scope.off();const F=m.update=U.run.bind(U),oe=m.job=U.runIfDirty.bind(U);oe.i=m,oe.id=m.uid,U.scheduler=()=>Fs(oe),An(m,!0),F()},D=(m,E,A)=>{E.component=m;const M=m.vnode.props;m.vnode=E,m.next=null,dm(m,E.props,M,A),mm(m,E.children,A),en(),Ll(m),tn()},K=(m,E,A,M,x,B,Z,W,U=!1)=>{const F=m&&m.children,oe=m?m.shapeFlag:0,J=E.children,{patchFlag:ne,shapeFlag:ce}=E;if(ne>0){if(ne&128){Ee(F,J,A,M,x,B,Z,W,U);return}else if(ne&256){re(F,J,A,M,x,B,Z,W,U);return}}ce&8?(oe&16&&Ze(F,x,B),J!==F&&d(A,J)):oe&16?ce&16?Ee(F,J,A,M,x,B,Z,W,U):Ze(F,x,B,!0):(oe&8&&d(A,""),ce&16&&S(J,A,M,x,B,Z,W,U))},re=(m,E,A,M,x,B,Z,W,U)=>{m=m||er,E=E||er;const F=m.length,oe=E.length,J=Math.min(F,oe);let ne;for(ne=0;neoe?Ze(m,x,B,!0,!1,J):S(E,A,M,x,B,Z,W,U,J)},Ee=(m,E,A,M,x,B,Z,W,U)=>{let F=0;const oe=E.length;let J=m.length-1,ne=oe-1;for(;F<=J&&F<=ne;){const ce=m[F],me=E[F]=U?dn(E[F]):At(E[F]);if(In(ce,me))g(ce,me,A,null,x,B,Z,W,U);else break;F++}for(;F<=J&&F<=ne;){const ce=m[J],me=E[ne]=U?dn(E[ne]):At(E[ne]);if(In(ce,me))g(ce,me,A,null,x,B,Z,W,U);else break;J--,ne--}if(F>J){if(F<=ne){const ce=ne+1,me=cene)for(;F<=J;)_e(m[F],x,B,!0),F++;else{const ce=F,me=F,ke=new Map;for(F=me;F<=ne;F++){const pt=E[F]=U?dn(E[F]):At(E[F]);pt.key!=null&&ke.set(pt.key,F)}let Te,dt=0;const Qe=ne-me+1;let Ct=!1,ft=0;const Tr=new Array(Qe);for(F=0;F=Qe){_e(pt,x,B,!0);continue}let Dt;if(pt.key!=null)Dt=ke.get(pt.key);else for(Te=me;Te<=ne;Te++)if(Tr[Te-me]===0&&In(pt,E[Te])){Dt=Te;break}Dt===void 0?_e(pt,x,B,!0):(Tr[Dt-me]=F+1,Dt>=ft?ft=Dt:Ct=!0,g(pt,E[Dt],A,null,x,B,Z,W,U),dt++)}const Sl=Ct?vm(Tr):er;for(Te=Sl.length-1,F=Qe-1;F>=0;F--){const pt=me+F,Dt=E[pt],kl=pt+1{const{el:B,type:Z,transition:W,children:U,shapeFlag:F}=m;if(F&6){Ie(m.component.subTree,E,A,M);return}if(F&128){m.suspense.move(E,A,M);return}if(F&64){Z.move(m,E,A,ee);return}if(Z===ye){r(B,E,A);for(let J=0;JW.enter(B),x);else{const{leave:J,delayLeave:ne,afterLeave:ce}=W,me=()=>r(B,E,A),ke=()=>{J(B,()=>{me(),ce&&ce()})};ne?ne(B,me,ke):ke()}else r(B,E,A)},_e=(m,E,A,M=!1,x=!1)=>{const{type:B,props:Z,ref:W,children:U,dynamicChildren:F,shapeFlag:oe,patchFlag:J,dirs:ne,cacheIndex:ce}=m;if(J===-2&&(x=!1),W!=null&&qr(W,null,A,m,!0),ce!=null&&(E.renderCache[ce]=void 0),oe&256){E.ctx.deactivate(m);return}const me=oe&1&&ne,ke=!Dn(m);let Te;if(ke&&(Te=Z&&Z.onVnodeBeforeUnmount)&&Tt(Te,E,m),oe&6)je(m.component,A,M);else{if(oe&128){m.suspense.unmount(A,M);return}me&&zt(m,null,E,"beforeUnmount"),oe&64?m.type.remove(m,E,A,ee,M):F&&!F.hasOnce&&(B!==ye||J>0&&J&64)?Ze(F,E,A,!1,!0):(B===ye&&J&384||!x&&oe&16)&&Ze(U,E,A),M&&Fe(m)}(ke&&(Te=Z&&Z.onVnodeUnmounted)||me)&&ht(()=>{Te&&Tt(Te,E,m),me&&zt(m,null,E,"unmounted")},A)},Fe=m=>{const{type:E,el:A,anchor:M,transition:x}=m;if(E===ye){lt(A,M);return}if(E===ir){T(m);return}const B=()=>{o(A),x&&!x.persisted&&x.afterLeave&&x.afterLeave()};if(m.shapeFlag&1&&x&&!x.persisted){const{leave:Z,delayLeave:W}=x,U=()=>Z(A,B);W?W(m.el,B,U):U()}else B()},lt=(m,E)=>{let A;for(;m!==E;)A=f(m),o(m),m=A;o(E)},je=(m,E,A)=>{const{bum:M,scope:x,job:B,subTree:Z,um:W,m:U,a:F}=m;Kl(U),Kl(F),M&&Ti(M),x.stop(),B&&(B.flags|=8,_e(Z,m,E,A)),W&&ht(W,E),ht(()=>{m.isUnmounted=!0},E),E&&E.pendingBranch&&!E.isUnmounted&&m.asyncDep&&!m.asyncResolved&&m.suspenseId===E.pendingId&&(E.deps--,E.deps===0&&E.resolve()),Ih(m)},Ze=(m,E,A,M=!1,x=!1,B=0)=>{for(let Z=B;Z{if(m.shapeFlag&6)return C(m.component.subTree);if(m.shapeFlag&128)return m.suspense.next();const E=f(m.anchor||m.el),A=E&&E[Rh];return A?f(A):E};let Y=!1;const G=(m,E,A)=>{m==null?E._vnode&&_e(E._vnode,null,null,!0):g(E._vnode||null,m,E,null,null,null,A),E._vnode=m,Y||(Y=!0,Ll(),Wo(),Y=!1)},ee={p:g,um:_e,m:Ie,r:Fe,mt:X,mc:S,pc:K,pbc:$,n:C,o:e};let pe,ge;return[pe,ge]=t(ee),{render:G,hydrate:pe,createApp:um(G,pe)}}function Li({type:e,props:t},n){return n==="svg"&&e==="foreignObject"||n==="mathml"&&e==="annotation-xml"&&t&&t.encoding&&t.encoding.includes("html")?void 0:n}function An({effect:e,job:t},n){n?(e.flags|=32,t.flags|=4):(e.flags&=-33,t.flags&=-5)}function sd(e,t){return(!e||e&&!e.pendingBranch)&&t&&!t.persisted}function ld(e,t,n=!1){const r=e.children,o=t.children;if(ie(r)&&ie(o))for(let i=0;i>1,e[n[l]]0&&(t[r]=n[i-1]),n[i]=r)}}for(i=n.length,s=n[i-1];i-- >0;)n[i]=s,s=t[s];return n}function ad(e){const t=e.subTree.component;if(t)return t.asyncDep&&!t.asyncResolved?t:ad(t)}function Kl(e){if(e)for(let t=0;tUe(Em);function ud(e,t){return Ws(e,null,t)}function Be(e,t,n){return Ws(e,t,n)}function Ws(e,t,n=Ae){const{immediate:r,deep:o,flush:i,once:s}=n,l=Ye({},n),a=t&&r||!t&&i!=="post";let u;if(ur){if(i==="sync"){const p=bm();u=p.__watcherHandles||(p.__watcherHandles=[])}else if(!a){const p=()=>{};return p.stop=Bt,p.resume=Bt,p.pause=Bt,p}}const d=We;l.call=(p,_,g)=>Lt(p,d,_,g);let c=!1;i==="post"?l.scheduler=p=>{ht(p,d&&d.suspense)}:i!=="sync"&&(c=!0,l.scheduler=(p,_)=>{_?p():Fs(p)}),l.augmentJob=p=>{t&&(p.flags|=4),c&&(p.flags|=2,d&&(p.id=d.uid,p.i=d))};const f=Eh(e,t,l);return ur&&(u?u.push(f):a&&f()),f}function ym(e,t,n){const r=this.proxy,o=Ce(e)?e.includes(".")?cd(r,e):()=>r[e]:e.bind(r,r);let i;le(t)?i=t:(i=t.handler,n=t);const s=ao(this),l=Ws(o,i.bind(r),n);return s(),l}function cd(e,t){const n=t.split(".");return()=>{let r=e;for(let o=0;ot==="modelValue"||t==="model-value"?e.modelModifiers:e[`${t}Modifiers`]||e[`${ut(t)}Modifiers`]||e[`${Qt(t)}Modifiers`];function wm(e,t,...n){if(e.isUnmounted)return;const r=e.vnode.props||Ae;let o=n;const i=t.startsWith("update:"),s=i&&Tm(r,t.slice(7));s&&(s.trim&&(o=n.map(d=>Ce(d)?d.trim():d)),s.number&&(o=n.map(xp))),Lh(e,t,o);let l,a=r[l=yi(t)]||r[l=yi(ut(t))];!a&&i&&(a=r[l=yi(Qt(t))]),a&&Lt(a,e,6,o);const u=r[l+"Once"];if(u){if(!e.emitted)e.emitted={};else if(e.emitted[l])return;e.emitted[l]=!0,Lt(u,e,6,o)}}function dd(e,t,n=!1){const r=t.emitsCache,o=r.get(e);if(o!==void 0)return o;const i=e.emits;let s={},l=!1;if(!le(e)){const a=u=>{const d=dd(u,t,!0);d&&(l=!0,Ye(s,d))};!n&&t.mixins.length&&t.mixins.forEach(a),e.extends&&a(e.extends),e.mixins&&e.mixins.forEach(a)}return!i&&!l?(xe(e)&&r.set(e,null),null):(ie(i)?i.forEach(a=>s[a]=null):Ye(s,i),xe(e)&&r.set(e,s),s)}function ci(e,t){return!e||!oo(t)?!1:(t=t.slice(2).replace(/Once$/,""),be(e,t[0].toLowerCase()+t.slice(1))||be(e,Qt(t))||be(e,t))}function Ri(e){const{type:t,vnode:n,proxy:r,withProxy:o,propsOptions:[i],slots:s,attrs:l,emit:a,render:u,renderCache:d,props:c,data:f,setupState:p,ctx:_,inheritAttrs:g}=e,v=Go(e);let y,w;try{if(n.shapeFlag&4){const T=o||r,R=T;y=At(u.call(R,T,d,c,p,f,_)),w=l}else{const T=t;y=At(T.length>1?T(c,{attrs:l,slots:s,emit:a}):T(c,null)),w=t.props?l:Am(l)}}catch(T){zr.length=0,so(T,e,1),y=ae(Je)}let h=y;if(w&&g!==!1){const T=Object.keys(w),{shapeFlag:R}=h;T.length&&R&7&&(i&&T.some(Is)&&(w=Sm(w,i)),h=vn(h,w,!1,!0))}return n.dirs&&(h=vn(h,null,!1,!0),h.dirs=h.dirs?h.dirs.concat(n.dirs):n.dirs),n.transition&&Bn(h,n.transition),y=h,Go(v),y}const Am=e=>{let t;for(const n in e)(n==="class"||n==="style"||oo(n))&&((t||(t={}))[n]=e[n]);return t},Sm=(e,t)=>{const n={};for(const r in e)(!Is(r)||!(r.slice(9)in t))&&(n[r]=e[r]);return n};function km(e,t,n){const{props:r,children:o,component:i}=e,{props:s,children:l,patchFlag:a}=t,u=i.emitsOptions;if(t.dirs||t.transition)return!0;if(n&&a>=0){if(a&1024)return!0;if(a&16)return r?Wl(r,s,u):!!s;if(a&8){const d=t.dynamicProps;for(let c=0;ce.__isSuspense;function hd(e,t){t&&t.pendingBranch?ie(e)?t.effects.push(...e):t.effects.push(e):kh(e)}const ye=Symbol.for("v-fgt"),_n=Symbol.for("v-txt"),Je=Symbol.for("v-cmt"),ir=Symbol.for("v-stc"),zr=[];let gt=null;function q(e=!1){zr.push(gt=e?null:[])}function Om(){zr.pop(),gt=zr[zr.length-1]||null}let Yr=1;function Gl(e,t=!1){Yr+=e,e<0&>&&t&&(gt.hasOnce=!0)}function md(e){return e.dynamicChildren=Yr>0?gt||er:null,Om(),Yr>0&>&>.push(e),e}function se(e,t,n,r,o,i){return md(de(e,t,n,r,o,i,!0))}function Se(e,t,n,r,o){return md(ae(e,t,n,r,o,!0))}function Xr(e){return e?e.__v_isVNode===!0:!1}function In(e,t){return e.type===t.type&&e.key===t.key}const _d=({key:e})=>e??null,Lo=({ref:e,ref_key:t,ref_for:n})=>(typeof e=="number"&&(e=""+e),e!=null?Ce(e)||Me(e)||le(e)?{i:qe,r:e,k:t,f:!!n}:e:null);function de(e,t=null,n=null,r=0,o=null,i=e===ye?0:1,s=!1,l=!1){const a={__v_isVNode:!0,__v_skip:!0,type:e,props:t,key:t&&_d(t),ref:t&&Lo(t),scopeId:Vc,slotScopeIds:null,children:n,component:null,suspense:null,ssContent:null,ssFallback:null,dirs:null,transition:null,el:null,anchor:null,target:null,targetStart:null,targetAnchor:null,staticCount:0,shapeFlag:i,patchFlag:r,dynamicProps:o,dynamicChildren:null,appContext:null,ctx:qe};return l?(Gs(a,n),i&128&&e.normalize(a)):n&&(a.shapeFlag|=Ce(n)?8:16),Yr>0&&!s&>&&(a.patchFlag>0||i&6)&&a.patchFlag!==32&>.push(a),a}const ae=Pm;function Pm(e,t=null,n=null,r=0,o=null,i=!1){if((!e||e===Qh)&&(e=Je),Xr(e)){const l=vn(e,t,!0);return n&&Gs(l,n),Yr>0&&!i&>&&(l.shapeFlag&6?gt[gt.indexOf(e)]=l:gt.push(l)),l.patchFlag=-2,l}if(Bm(e)&&(e=e.__vccOpts),t){t=Ro(t);let{class:l,style:a}=t;l&&!Ce(l)&&(t.class=st(l)),xe(a)&&(Bs(a)&&!ie(a)&&(a=Ye({},a)),t.style=jn(a))}const s=Ce(e)?1:pd(e)?128:Nc(e)?64:xe(e)?4:le(e)?2:0;return de(e,t,n,r,o,s,i,!0)}function Ro(e){return e?Bs(e)||Qc(e)?Ye({},e):e:null}function vn(e,t,n=!1,r=!1){const{props:o,ref:i,patchFlag:s,children:l,transition:a}=e,u=t?xm(o||{},t):o,d={__v_isVNode:!0,__v_skip:!0,type:e.type,props:u,key:u&&_d(u),ref:t&&t.ref?n&&i?ie(i)?i.concat(Lo(t)):[i,Lo(t)]:Lo(t):i,scopeId:e.scopeId,slotScopeIds:e.slotScopeIds,children:l,target:e.target,targetStart:e.targetStart,targetAnchor:e.targetAnchor,staticCount:e.staticCount,shapeFlag:e.shapeFlag,patchFlag:t&&e.type!==ye?s===-1?16:s|16:s,dynamicProps:e.dynamicProps,dynamicChildren:e.dynamicChildren,appContext:e.appContext,dirs:e.dirs,transition:a,component:e.component,suspense:e.suspense,ssContent:e.ssContent&&vn(e.ssContent),ssFallback:e.ssFallback&&vn(e.ssFallback),el:e.el,anchor:e.anchor,ctx:e.ctx,ce:e.ce};return a&&r&&Bn(d,a.clone(d)),d}function Zt(e=" ",t=0){return ae(_n,null,e,t)}function Cm(e,t){const n=ae(ir,null,e);return n.staticCount=t,n}function Ve(e="",t=!1){return t?(q(),Se(Je,null,e)):ae(Je,null,e)}function At(e){return e==null||typeof e=="boolean"?ae(Je):ie(e)?ae(ye,null,e.slice()):Xr(e)?dn(e):ae(_n,null,String(e))}function dn(e){return e.el===null&&e.patchFlag!==-1||e.memo?e:vn(e)}function Gs(e,t){let n=0;const{shapeFlag:r}=e;if(t==null)t=null;else if(ie(t))n=16;else if(typeof t=="object")if(r&65){const o=t.default;o&&(o._c&&(o._d=!1),Gs(e,o()),o._c&&(o._d=!0));return}else{n=32;const o=t._;!o&&!Qc(t)?t._ctx=qe:o===3&&qe&&(qe.slots._===1?t._=1:(t._=2,e.patchFlag|=1024))}else le(t)?(t={default:t,_ctx:qe},n=32):(t=String(t),r&64?(n=16,t=[Zt(t)]):n=8);e.children=t,e.shapeFlag|=n}function xm(...e){const t={};for(let n=0;nWe||qe;let Xo,as;{const e=oi(),t=(n,r)=>{let o;return(o=e[n])||(o=e[n]=[]),o.push(r),i=>{o.length>1?o.forEach(s=>s(i)):o[0](i)}};Xo=t("__VUE_INSTANCE_SETTERS__",n=>We=n),as=t("__VUE_SSR_SETTERS__",n=>ur=n)}const ao=e=>{const t=We;return Xo(e),e.scope.on(),()=>{e.scope.off(),Xo(t)}},ql=()=>{We&&We.scope.off(),Xo(null)};function gd(e){return e.vnode.shapeFlag&4}let ur=!1;function Dm(e,t=!1,n=!1){t&&as(t);const{props:r,children:o}=e.vnode,i=gd(e);cm(e,r,i,t),hm(e,o,n);const s=i?Vm(e,t):void 0;return t&&as(!1),s}function Vm(e,t){const n=e.type;e.accessCache=Object.create(null),e.proxy=new Proxy(e.ctx,nm);const{setup:r}=n;if(r){en();const o=e.setupContext=r.length>1?Mm(e):null,i=ao(e),s=Er(r,e,0,[e.props,o]),l=tc(s);if(tn(),i(),(l||e.sp)&&!Dn(e)&&Us(e),l){if(s.then(ql,ql),t)return s.then(a=>{Yl(e,a)}).catch(a=>{so(a,e,0)});e.asyncDep=s}else Yl(e,s)}else vd(e)}function Yl(e,t,n){le(t)?e.type.__ssrInlineRender?e.ssrRender=t:e.render=t:xe(t)&&(e.devtoolsRawSetupState=t,e.setupState=Ac(t)),vd(e)}function vd(e,t,n){const r=e.type;e.render||(e.render=r.render||Bt);{const o=ao(e);en();try{rm(e)}finally{tn(),o()}}}const Nm={get(e,t){return et(e,"get",""),e[t]}};function Mm(e){const t=n=>{e.exposed=n||{}};return{attrs:new Proxy(e.attrs,Nm),slots:e.slots,emit:e.emit,expose:t}}function di(e){return e.exposed?e.exposeProxy||(e.exposeProxy=new Proxy(Ac(ch(e.exposed)),{get(t,n){if(n in t)return t[n];if(n in Mr)return Mr[n](e)},has(t,n){return n in t||n in Mr}})):e.proxy}const zm=/(?:^|[-_])(\w)/g,$m=e=>e.replace(zm,t=>t.toUpperCase()).replace(/[-_]/g,"");function Ed(e,t=!0){return le(e)?e.displayName||e.name:e.name||t&&e.__name}function bd(e,t,n=!1){let r=Ed(t);if(!r&&t.__file){const o=t.__file.match(/([^/\\]+)\.\w+$/);o&&(r=o[1])}if(!r&&e&&e.parent){const o=i=>{for(const s in i)if(i[s]===t)return s};r=o(e.components||e.parent.type.components)||o(e.appContext.components)}return r?$m(r):n?"App":"Anonymous"}function Bm(e){return le(e)&&"__vccOpts"in e}const N=(e,t)=>gh(e,t,ur);function H(e,t,n){const r=arguments.length;return r===2?xe(t)&&!ie(t)?Xr(t)?ae(e,null,[t]):ae(e,t):ae(e,null,t):(r>3?n=Array.prototype.slice.call(arguments,2):r===3&&Xr(n)&&(n=[n]),ae(e,t,n))}const Xl="3.5.13";/** +* @vue/runtime-dom v3.5.13 +* (c) 2018-present Yuxi (Evan) You and Vue contributors +* @license MIT +**/let us;const Zl=typeof window<"u"&&window.trustedTypes;if(Zl)try{us=Zl.createPolicy("vue",{createHTML:e=>e})}catch{}const yd=us?e=>us.createHTML(e):e=>e,Fm="http://www.w3.org/2000/svg",Hm="http://www.w3.org/1998/Math/MathML",Gt=typeof document<"u"?document:null,Jl=Gt&&Gt.createElement("template"),jm={insert:(e,t,n)=>{t.insertBefore(e,n||null)},remove:e=>{const t=e.parentNode;t&&t.removeChild(e)},createElement:(e,t,n,r)=>{const o=t==="svg"?Gt.createElementNS(Fm,e):t==="mathml"?Gt.createElementNS(Hm,e):n?Gt.createElement(e,{is:n}):Gt.createElement(e);return e==="select"&&r&&r.multiple!=null&&o.setAttribute("multiple",r.multiple),o},createText:e=>Gt.createTextNode(e),createComment:e=>Gt.createComment(e),setText:(e,t)=>{e.nodeValue=t},setElementText:(e,t)=>{e.textContent=t},parentNode:e=>e.parentNode,nextSibling:e=>e.nextSibling,querySelector:e=>Gt.querySelector(e),setScopeId(e,t){e.setAttribute(t,"")},insertStaticContent(e,t,n,r,o,i){const s=n?n.previousSibling:t.lastChild;if(o&&(o===i||o.nextSibling))for(;t.insertBefore(o.cloneNode(!0),n),!(o===i||!(o=o.nextSibling)););else{Jl.innerHTML=yd(r==="svg"?`${e}`:r==="mathml"?`${e}`:e);const l=Jl.content;if(r==="svg"||r==="mathml"){const a=l.firstChild;for(;a.firstChild;)l.appendChild(a.firstChild);l.removeChild(a)}t.insertBefore(l,n)}return[s?s.nextSibling:t.firstChild,n?n.previousSibling:t.lastChild]}},on="transition",Ar="animation",cr=Symbol("_vtc"),Td={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},wd=Ye({},zc,Td),Um=e=>(e.displayName="Transition",e.props=wd,e),qs=Um((e,{slots:t})=>H(Vh,Ad(e),t)),Sn=(e,t=[])=>{ie(e)?e.forEach(n=>n(...t)):e&&e(...t)},Ql=e=>e?ie(e)?e.some(t=>t.length>1):e.length>1:!1;function Ad(e){const t={};for(const O in e)O in Td||(t[O]=e[O]);if(e.css===!1)return t;const{name:n="v",type:r,duration:o,enterFromClass:i=`${n}-enter-from`,enterActiveClass:s=`${n}-enter-active`,enterToClass:l=`${n}-enter-to`,appearFromClass:a=i,appearActiveClass:u=s,appearToClass:d=l,leaveFromClass:c=`${n}-leave-from`,leaveActiveClass:f=`${n}-leave-active`,leaveToClass:p=`${n}-leave-to`}=e,_=Km(o),g=_&&_[0],v=_&&_[1],{onBeforeEnter:y,onEnter:w,onEnterCancelled:h,onLeave:T,onLeaveCancelled:R,onBeforeAppear:j=y,onAppear:k=w,onAppearCancelled:S=h}=t,V=(O,P,X,te)=>{O._enterCancelled=te,an(O,P?d:l),an(O,P?u:s),X&&X()},$=(O,P)=>{O._isLeaving=!1,an(O,c),an(O,p),an(O,f),P&&P()},I=O=>(P,X)=>{const te=O?k:w,L=()=>V(P,O,X);Sn(te,[P,L]),ea(()=>{an(P,O?a:i),Nt(P,O?d:l),Ql(te)||ta(P,r,g,L)})};return Ye(t,{onBeforeEnter(O){Sn(y,[O]),Nt(O,i),Nt(O,s)},onBeforeAppear(O){Sn(j,[O]),Nt(O,a),Nt(O,u)},onEnter:I(!1),onAppear:I(!0),onLeave(O,P){O._isLeaving=!0;const X=()=>$(O,P);Nt(O,c),O._enterCancelled?(Nt(O,f),cs()):(cs(),Nt(O,f)),ea(()=>{O._isLeaving&&(an(O,c),Nt(O,p),Ql(T)||ta(O,r,v,X))}),Sn(T,[O,X])},onEnterCancelled(O){V(O,!1,void 0,!0),Sn(h,[O])},onAppearCancelled(O){V(O,!0,void 0,!0),Sn(S,[O])},onLeaveCancelled(O){$(O),Sn(R,[O])}})}function Km(e){if(e==null)return null;if(xe(e))return[Di(e.enter),Di(e.leave)];{const t=Di(e);return[t,t]}}function Di(e){return Ip(e)}function Nt(e,t){t.split(/\s+/).forEach(n=>n&&e.classList.add(n)),(e[cr]||(e[cr]=new Set)).add(t)}function an(e,t){t.split(/\s+/).forEach(r=>r&&e.classList.remove(r));const n=e[cr];n&&(n.delete(t),n.size||(e[cr]=void 0))}function ea(e){requestAnimationFrame(()=>{requestAnimationFrame(e)})}let Wm=0;function ta(e,t,n,r){const o=e._endId=++Wm,i=()=>{o===e._endId&&r()};if(n!=null)return setTimeout(i,n);const{type:s,timeout:l,propCount:a}=Sd(e,t);if(!s)return r();const u=s+"end";let d=0;const c=()=>{e.removeEventListener(u,f),i()},f=p=>{p.target===e&&++d>=a&&c()};setTimeout(()=>{d(n[_]||"").split(", "),o=r(`${on}Delay`),i=r(`${on}Duration`),s=na(o,i),l=r(`${Ar}Delay`),a=r(`${Ar}Duration`),u=na(l,a);let d=null,c=0,f=0;t===on?s>0&&(d=on,c=s,f=i.length):t===Ar?u>0&&(d=Ar,c=u,f=a.length):(c=Math.max(s,u),d=c>0?s>u?on:Ar:null,f=d?d===on?i.length:a.length:0);const p=d===on&&/\b(transform|all)(,|$)/.test(r(`${on}Property`).toString());return{type:d,timeout:c,propCount:f,hasTransform:p}}function na(e,t){for(;e.lengthra(n)+ra(e[r])))}function ra(e){return e==="auto"?0:Number(e.slice(0,-1).replace(",","."))*1e3}function cs(){return document.body.offsetHeight}function Gm(e,t,n){const r=e[cr];r&&(t=(t?[t,...r]:[...r]).join(" ")),t==null?e.removeAttribute("class"):n?e.setAttribute("class",t):e.className=t}const Zo=Symbol("_vod"),kd=Symbol("_vsh"),Jo={beforeMount(e,{value:t},{transition:n}){e[Zo]=e.style.display==="none"?"":e.style.display,n&&t?n.beforeEnter(e):Sr(e,t)},mounted(e,{value:t},{transition:n}){n&&t&&n.enter(e)},updated(e,{value:t,oldValue:n},{transition:r}){!t!=!n&&(r?t?(r.beforeEnter(e),Sr(e,!0),r.enter(e)):r.leave(e,()=>{Sr(e,!1)}):Sr(e,t))},beforeUnmount(e,{value:t}){Sr(e,t)}};function Sr(e,t){e.style.display=t?e[Zo]:"none",e[kd]=!t}const qm=Symbol(""),Ym=/(^|;)\s*display\s*:/;function Xm(e,t,n){const r=e.style,o=Ce(n);let i=!1;if(n&&!o){if(t)if(Ce(t))for(const s of t.split(";")){const l=s.slice(0,s.indexOf(":")).trim();n[l]==null&&Do(r,l,"")}else for(const s in t)n[s]==null&&Do(r,s,"");for(const s in n)s==="display"&&(i=!0),Do(r,s,n[s])}else if(o){if(t!==n){const s=r[qm];s&&(n+=";"+s),r.cssText=n,i=Ym.test(n)}}else t&&e.removeAttribute("style");Zo in e&&(e[Zo]=i?r.display:"",e[kd]&&(r.display="none"))}const oa=/\s*!important$/;function Do(e,t,n){if(ie(n))n.forEach(r=>Do(e,t,r));else if(n==null&&(n=""),t.startsWith("--"))e.setProperty(t,n);else{const r=Zm(e,t);oa.test(n)?e.setProperty(Qt(r),n.replace(oa,""),"important"):e[r]=n}}const ia=["Webkit","Moz","ms"],Vi={};function Zm(e,t){const n=Vi[t];if(n)return n;let r=ut(t);if(r!=="filter"&&r in e)return Vi[t]=r;r=io(r);for(let o=0;oNi||(n_.then(()=>Ni=0),Ni=Date.now());function o_(e,t){const n=r=>{if(!r._vts)r._vts=Date.now();else if(r._vts<=n.attached)return;Lt(i_(r,n.value),t,5,[r])};return n.value=e,n.attached=r_(),n}function i_(e,t){if(ie(t)){const n=e.stopImmediatePropagation;return e.stopImmediatePropagation=()=>{n.call(e),e._stopped=!0},t.map(r=>o=>!o._stopped&&r&&r(o))}else return t}const da=e=>e.charCodeAt(0)===111&&e.charCodeAt(1)===110&&e.charCodeAt(2)>96&&e.charCodeAt(2)<123,s_=(e,t,n,r,o,i)=>{const s=o==="svg";t==="class"?Gm(e,r,s):t==="style"?Xm(e,n,r):oo(t)?Is(t)||e_(e,t,n,r,i):(t[0]==="."?(t=t.slice(1),!0):t[0]==="^"?(t=t.slice(1),!1):l_(e,t,r,s))?(aa(e,t,r),!e.tagName.includes("-")&&(t==="value"||t==="checked"||t==="selected")&&la(e,t,r,s,i,t!=="value")):e._isVueCE&&(/[A-Z]/.test(t)||!Ce(r))?aa(e,ut(t),r,i,t):(t==="true-value"?e._trueValue=r:t==="false-value"&&(e._falseValue=r),la(e,t,r,s))};function l_(e,t,n,r){if(r)return!!(t==="innerHTML"||t==="textContent"||t in e&&da(t)&&le(n));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 o=e.tagName;if(o==="IMG"||o==="VIDEO"||o==="CANVAS"||o==="SOURCE")return!1}return da(t)&&Ce(n)?!1:t in e}const Od=new WeakMap,Pd=new WeakMap,Qo=Symbol("_moveCb"),fa=Symbol("_enterCb"),a_=e=>(delete e.props.mode,e),u_=a_({name:"TransitionGroup",props:Ye({},wd,{tag:String,moveClass:String}),setup(e,{slots:t}){const n=yn(),r=Mc();let o,i;return Kc(()=>{if(!o.length)return;const s=e.moveClass||`${e.name||"v"}-move`;if(!h_(o[0].el,n.vnode.el,s))return;o.forEach(d_),o.forEach(f_);const l=o.filter(p_);cs(),l.forEach(a=>{const u=a.el,d=u.style;Nt(u,s),d.transform=d.webkitTransform=d.transitionDuration="";const c=u[Qo]=f=>{f&&f.target!==u||(!f||/transform$/.test(f.propertyName))&&(u.removeEventListener("transitionend",c),u[Qo]=null,an(u,s))};u.addEventListener("transitionend",c)})}),()=>{const s=he(e),l=Ad(s);let a=s.tag||ye;if(o=[],i)for(let u=0;u{l.split(/\s+/).forEach(a=>a&&r.classList.remove(a))}),n.split(/\s+/).forEach(l=>l&&r.classList.add(l)),r.style.display="none";const i=t.nodeType===1?t:t.parentNode;i.appendChild(r);const{hasTransform:s}=Sd(r);return i.removeChild(r),s}const m_={esc:"escape",space:" ",up:"arrow-up",left:"arrow-left",right:"arrow-right",down:"arrow-down",delete:"backspace"},__=(e,t)=>{const n=e._withKeys||(e._withKeys={}),r=t.join(".");return n[r]||(n[r]=o=>{if(!("key"in o))return;const i=Qt(o.key);if(t.some(s=>s===i||m_[s]===i))return e(o)})},g_=Ye({patchProp:s_},jm);let Mi,pa=!1;function v_(){return Mi=pa?Mi:_m(g_),pa=!0,Mi}const E_=(...e)=>{const t=v_().createApp(...e),{mount:n}=t;return t.mount=r=>{const o=y_(r);if(o)return n(o,!0,b_(o))},t};function b_(e){if(e instanceof SVGElement)return"svg";if(typeof MathMLElement=="function"&&e instanceof MathMLElement)return"mathml"}function y_(e){return Ce(e)?document.querySelector(e):e}var uo=e=>/^[a-z][a-z0-9+.-]*:/.test(e)||e.startsWith("//"),T_=/.md((\?|#).*)?$/,w_=(e,t="/")=>uo(e)||e.startsWith("/")&&!e.startsWith(t)&&!T_.test(e),co=e=>/^(https?:)?\/\//.test(e),ha=e=>{if(!e||e.endsWith("/"))return e;let t=e.replace(/(^|\/)README.md$/i,"$1index.html");return t.endsWith(".md")?t=`${t.substring(0,t.length-3)}.html`:t.endsWith(".html")||(t=`${t}.html`),t.endsWith("/index.html")&&(t=t.substring(0,t.length-10)),t},A_="http://.",S_=(e,t)=>{if(!e.startsWith("/")&&t){const n=t.slice(0,t.lastIndexOf("/"));return ha(new URL(`${n}/${e}`,A_).pathname)}return ha(e)},k_=(e,t)=>{const n=Object.keys(e).sort((r,o)=>{const i=o.split("/").length-r.split("/").length;return i!==0?i:o.length-r.length});for(const r of n)if(t.startsWith(r))return r;return"/"},O_=(e,t="/")=>{const n=e.replace(/^(?:https?:)?\/\/[^/]*/,"");return n.startsWith(t)?`/${n.slice(t.length)}`:n},P_=/(#|\?)/,Cd=e=>{const[t,...n]=e.split(P_);return{pathname:t,hashAndQueries:n.join("")}},C_=["link","meta","script","style","noscript","template"],x_=["title","base"],I_=([e,t,n])=>x_.includes(e)?e:C_.includes(e)?e==="meta"&&t.name?`${e}.${t.name}`:e==="template"&&t.id?`${e}.${t.id}`:JSON.stringify([e,Object.entries(t).map(([r,o])=>typeof o=="boolean"?o?[r,""]:null:[r,o]).filter(r=>r!=null).sort(([r],[o])=>r.localeCompare(o)),n]):null,L_=e=>{const t=new Set,n=[];return e.forEach(r=>{const o=I_(r);o&&!t.has(o)&&(t.add(o),n.push(r))}),n},R_=e=>e.startsWith("/")?e:`/${e}`,xd=e=>e.endsWith("/")||e.endsWith(".html")?e:`${e}/`,Id=e=>e.endsWith("/")?e.slice(0,-1):e,Ys=e=>e.startsWith("/")?e.slice(1):e,Ld=e=>typeof e=="function",Mn=e=>Object.prototype.toString.call(e)==="[object Object]",at=e=>typeof e=="string";const D_=JSON.parse("{}"),V_=Object.fromEntries([["/",{loader:()=>b(()=>import("./index.html-BjkTCafT.js"),[]),meta:{title:"Home",icon:"home"}}],["/plugins/",{loader:()=>b(()=>import("./index.html-C662hOWR.js"),[]),meta:{title:"Plugins",icon:"unplug"}}],["/themes/",{loader:()=>b(()=>import("./index.html-DJC6tXxV.js"),[]),meta:{title:"Themes",icon:"palette"}}],["/themes/guidelines.html",{loader:()=>b(()=>import("./guidelines.html-fxhIrna-.js"),[]),meta:{title:"Theme Guidelines",icon:"signpost"}}],["/tools/",{loader:()=>b(()=>import("./index.html-6nvCSvf6.js"),[]),meta:{title:"Tool Packages",icon:"hammer"}}],["/zh/",{loader:()=>b(()=>import("./index.html-2Osui4z3.js"),[]),meta:{title:"首页",icon:"home"}}],["/plugins/analytics/",{loader:()=>b(()=>import("./index.html-BAUM7J5d.js"),[]),meta:{title:"Analytics Plugins",icon:"chart-no-axes-combined"}}],["/plugins/analytics/baidu-analytics.html",{loader:()=>b(()=>import("./baidu-analytics.html-Cw_mVFLe.js"),[]),meta:{title:"baidu-analytics",icon:"chart-no-axes-combined"}}],["/plugins/analytics/google-analytics.html",{loader:()=>b(()=>import("./google-analytics.html-BgirWhmW.js"),[]),meta:{title:"google-analytics",icon:"chart-no-axes-combined"}}],["/plugins/analytics/umami-analytics.html",{loader:()=>b(()=>import("./umami-analytics.html-8SskgrZI.js"),[]),meta:{title:"umami-analytics",icon:"chart-no-axes-combined"}}],["/plugins/blog/",{loader:()=>b(()=>import("./index.html-Dooyygol.js"),[]),meta:{title:"Blog Plugins",icon:"la:blog"}}],["/plugins/development/",{loader:()=>b(()=>import("./index.html-xvSYBnou.js"),[]),meta:{title:"Development Plugins",icon:"server-cog"}}],["/plugins/development/active-header-links.html",{loader:()=>b(()=>import("./active-header-links.html-DtNaUDF_.js"),[]),meta:{title:"active-header-links",icon:"link-2"}}],["/plugins/development/git.html",{loader:()=>b(()=>import("./git.html-B9E4vyJw.js"),[]),meta:{title:"git",icon:"la:git-alt"}}],["/plugins/development/palette.html",{loader:()=>b(()=>import("./palette.html-BpZmMQAv.js"),[]),meta:{title:"palette",icon:"palette"}}],["/plugins/development/reading-time.html",{loader:()=>b(()=>import("./reading-time.html-kFqlXSdZ.js"),[]),meta:{title:"reading-time",icon:"book-open-text"}}],["/plugins/development/rtl.html",{loader:()=>b(()=>import("./rtl.html-DLdl8_6R.js"),[]),meta:{title:"rtl",icon:"pilcrow-left"}}],["/plugins/development/theme-data.html",{loader:()=>b(()=>import("./theme-data.html-wRGdqnC3.js"),[]),meta:{title:"theme-data",icon:"database"}}],["/plugins/development/toc.html",{loader:()=>b(()=>import("./toc.html-XoWxL5zQ.js"),[]),meta:{title:"toc",icon:"heading"}}],["/plugins/features/",{loader:()=>b(()=>import("./index.html-x3nK14k7.js"),[]),meta:{title:"Feature Plugins",icon:"sparkles"}}],["/plugins/features/back-to-top.html",{loader:()=>b(()=>import("./back-to-top.html-DohjHbTl.js"),[]),meta:{title:"back-to-top",icon:"arrow-up-to-line"}}],["/plugins/features/catalog.html",{loader:()=>b(()=>import("./catalog.html-CZKGc2hX.js"),[]),meta:{title:"catalog",icon:"list-tree"}}],["/plugins/features/copy-code.html",{loader:()=>b(()=>import("./copy-code.html-DHqUfozp.js"),[]),meta:{title:"copy-code",icon:"clipboard-copy"}}],["/plugins/features/copyright.html",{loader:()=>b(()=>import("./copyright.html-D0I0MAPW.js"),[]),meta:{title:"copyright",icon:"fa-regular:copyright"}}],["/plugins/features/icon.html",{loader:()=>b(()=>import("./icon.html-CNJqE-EW.js"),[]),meta:{title:"icon",icon:"fa6-solid:icons"}}],["/plugins/features/medium-zoom.html",{loader:()=>b(()=>import("./medium-zoom.html-BDfRwLyj.js"),[]),meta:{title:"medium-zoom",icon:"fullscreen"}}],["/plugins/features/notice.html",{loader:()=>b(()=>import("./notice.html-tFsrpZh8.js"),[]),meta:{title:"notice",icon:"bell"}}],["/plugins/features/nprogress.html",{loader:()=>b(()=>import("./nprogress.html-l9G9p-h5.js"),[]),meta:{title:"nprogress",icon:"pajamas:progress"}}],["/plugins/features/photo-swipe.html",{loader:()=>b(()=>import("./photo-swipe.html-BsE7xJnr.js"),[]),meta:{title:"photo-swipe",icon:"image-play"}}],["/plugins/features/watermark.html",{loader:()=>b(()=>import("./watermark.html-CqqmIUdG.js"),[]),meta:{title:"watermark",icon:"droplet"}}],["/plugins/markdown/",{loader:()=>b(()=>import("./index.html-1PiMGZGb.js"),[]),meta:{title:"Markdown Plugins",icon:"octicon:markdown-16"}}],["/plugins/markdown/append-date.html",{loader:()=>b(()=>import("./append-date.html-BIWaU9aO.js"),[]),meta:{title:"append-date",icon:"calendar"}}],["/plugins/markdown/links-check.html",{loader:()=>b(()=>import("./links-check.html-Bz-dcZza.js"),[]),meta:{title:"links-check",icon:"list-checks"}}],["/plugins/markdown/markdown-container.html",{loader:()=>b(()=>import("./markdown-container.html-kHCUtNuS.js"),[]),meta:{title:"markdown-container",icon:"package"}}],["/plugins/markdown/markdown-ext.html",{loader:()=>b(()=>import("./markdown-ext.html-CVNshKOI.js"),[]),meta:{title:"markdown-ext",icon:"expand"}}],["/plugins/markdown/markdown-hint.html",{loader:()=>b(()=>import("./markdown-hint.html-BtLSwiKD.js"),[]),meta:{title:"markdown-hint",icon:"siren"}}],["/plugins/markdown/markdown-image.html",{loader:()=>b(()=>import("./markdown-image.html-DHPoKt8m.js"),__vite__mapDeps([0,1])),meta:{title:"markdown-image",icon:"image"}}],["/plugins/markdown/markdown-include.html",{loader:()=>b(()=>import("./markdown-include.html-CXeOj66c.js"),[]),meta:{title:"markdown-include",icon:"between-horizontal-end"}}],["/plugins/markdown/markdown-math.html",{loader:()=>b(()=>import("./markdown-math.html-mmldwPHJ.js"),[]),meta:{title:"markdown-math",icon:"sigma"}}],["/plugins/markdown/markdown-stylize.html",{loader:()=>b(()=>import("./markdown-stylize.html-Bhw93E07.js"),[]),meta:{title:"markdown-stylize",icon:"paint-bucket"}}],["/plugins/markdown/markdown-tab.html",{loader:()=>b(()=>import("./markdown-tab.html-CbekDOjw.js"),[]),meta:{title:"markdown-tab",icon:"columns-2"}}],["/plugins/markdown/prismjs.html",{loader:()=>b(()=>import("./prismjs.html-CUQOdGAl.js"),[]),meta:{title:"prismjs",icon:"pyramid"}}],["/plugins/markdown/shiki.html",{loader:()=>b(()=>import("./shiki.html-7M4H008f.js"),[]),meta:{title:"shiki",icon:"highlighter"}}],["/plugins/pwa/",{loader:()=>b(()=>import("./index.html-Cc1qzhEZ.js"),[]),meta:{title:"PWA Plugins",icon:"layout-grid"}}],["/plugins/pwa/remove-pwa.html",{loader:()=>b(()=>import("./remove-pwa.html-BMrGzskM.js"),[]),meta:{title:"remove-pwa",icon:"trash-2"}}],["/plugins/search/",{loader:()=>b(()=>import("./index.html-4AUqLOiw.js"),[]),meta:{title:"Search Plugins",icon:"search"}}],["/plugins/search/docsearch.html",{loader:()=>b(()=>import("./docsearch.html-D9q3u3bM.js"),[]),meta:{title:"docsearch",icon:"search"}}],["/plugins/search/guidelines.html",{loader:()=>b(()=>import("./guidelines.html-B8G2-t-t.js"),[]),meta:{title:"Search Plugin Guidelines",icon:"signpost"}}],["/plugins/search/search.html",{loader:()=>b(()=>import("./search.html-C70QB80L.js"),[]),meta:{title:"search",icon:"search"}}],["/plugins/search/slimsearch.html",{loader:()=>b(()=>import("./slimsearch.html-BmQGsTxN.js"),[]),meta:{title:"slimsearch",icon:"search"}}],["/plugins/seo/",{loader:()=>b(()=>import("./index.html-C14S5u40.js"),[]),meta:{title:"SEO Plugins",icon:"scan-search"}}],["/plugins/tools/",{loader:()=>b(()=>import("./index.html-C5V7_Q9M.js"),[]),meta:{title:"Tool Plugins",icon:"hammer"}}],["/plugins/tools/cache.html",{loader:()=>b(()=>import("./cache.html-BUtWyh6p.js"),[]),meta:{title:"cache",icon:"database-zap"}}],["/plugins/tools/google-tag-manager.html",{loader:()=>b(()=>import("./google-tag-manager.html-BITEDEb1.js"),[]),meta:{title:"google-tag-manager",icon:"logos:google-marketing-platform"}}],["/plugins/tools/redirect.html",{loader:()=>b(()=>import("./redirect.html-BMDL5hME.js"),[]),meta:{title:"redirect",icon:"forward"}}],["/plugins/tools/register-components.html",{loader:()=>b(()=>import("./register-components.html-DT8EZOfN.js"),[]),meta:{title:"register-components",icon:"puzzle"}}],["/themes/default/",{loader:()=>b(()=>import("./index.html-BGfQGak5.js"),[]),meta:{title:"theme-default",icon:"palette"}}],["/themes/default/components.html",{loader:()=>b(()=>import("./components.html-DE1zQg-Q.js"),[]),meta:{title:"Built-in Components",icon:"puzzle"}}],["/themes/default/config.html",{loader:()=>b(()=>import("./config.html-uU6Y7sKi.js"),[]),meta:{title:"Config",icon:"settings-2"}}],["/themes/default/extending.html",{loader:()=>b(()=>import("./extending.html-BMVvbvzq.js"),__vite__mapDeps([2,3])),meta:{title:"Extending",icon:"cable"}}],["/themes/default/frontmatter.html",{loader:()=>b(()=>import("./frontmatter.html-DSQ_Ctbk.js"),[]),meta:{title:"Frontmatter",icon:"captions"}}],["/themes/default/locale.html",{loader:()=>b(()=>import("./locale.html-DQ6Oo3F8.js"),[]),meta:{title:"Locale Config",icon:"languages"}}],["/themes/default/markdown.html",{loader:()=>b(()=>import("./markdown.html-Debey0sp.js"),[]),meta:{title:"Markdown",icon:"octicon:markdown-16"}}],["/themes/default/plugin.html",{loader:()=>b(()=>import("./plugin.html-Duno4foi.js"),[]),meta:{title:"Plugins Config",icon:"unplug"}}],["/themes/default/styles.html",{loader:()=>b(()=>import("./styles.html-CkzOl05O.js"),[]),meta:{title:"Styles",icon:"paintbrush-vertical"}}],["/tools/helper/",{loader:()=>b(()=>import("./index.html-CtE2QZQj.js"),[]),meta:{title:"@vuepress/helper",icon:"hammer"}}],["/tools/helper/client.html",{loader:()=>b(()=>import("./client.html-B1jgAa0E.js"),[]),meta:{title:"Client Related",icon:"chrome"}}],["/tools/helper/shared.html",{loader:()=>b(()=>import("./shared.html-DpYjGugK.js"),[]),meta:{title:"Shared Methods",icon:"split"}}],["/tools/helper/style.html",{loader:()=>b(()=>import("./style.html-BGSu3aKP.js"),[]),meta:{title:"Styles",icon:"paintbrush-vertical"}}],["/zh/plugins/",{loader:()=>b(()=>import("./index.html-CPL0bNG_.js"),[]),meta:{title:"插件",icon:"unplug"}}],["/zh/themes/",{loader:()=>b(()=>import("./index.html-DXXVIy3F.js"),[]),meta:{title:"主题",icon:"palette"}}],["/zh/themes/guidelines.html",{loader:()=>b(()=>import("./guidelines.html-DzI1C0Yw.js"),[]),meta:{title:"主题指南",icon:"signpost"}}],["/zh/tools/",{loader:()=>b(()=>import("./index.html-ZYEX32dA.js"),[]),meta:{title:"工具包",icon:"hammer"}}],["/plugins/blog/blog/",{loader:()=>b(()=>import("./index.html-ChOsYK2P.js"),[]),meta:{title:"blog",icon:"la:blog"}}],["/plugins/blog/blog/config.html",{loader:()=>b(()=>import("./config.html-DHRyMP3M.js"),[]),meta:{title:"Config",icon:"settings-2"}}],["/plugins/blog/blog/guide.html",{loader:()=>b(()=>import("./guide.html-BBCKTuCo.js"),[]),meta:{title:"Guide",icon:"lightbulb"}}],["/plugins/blog/comment/",{loader:()=>b(()=>import("./index.html-IXMrrZZ_.js"),[]),meta:{title:"comment",icon:"message-circle-more"}}],["/plugins/blog/comment/guide.html",{loader:()=>b(()=>import("./guide.html-DFaX6qjM.js"),[]),meta:{title:"Guide",icon:"lightbulb"}}],["/plugins/blog/feed/",{loader:()=>b(()=>import("./index.html-CF_6rJ3i.js"),[]),meta:{title:"feed",icon:"rss"}}],["/plugins/blog/feed/channel.html",{loader:()=>b(()=>import("./channel.html-BAU0Dy54.js"),[]),meta:{title:"Channel Config",icon:"tv"}}],["/plugins/blog/feed/config.html",{loader:()=>b(()=>import("./config.html-DqG3d-_j.js"),[]),meta:{title:"Plugin Config",icon:"settings-2"}}],["/plugins/blog/feed/frontmatter.html",{loader:()=>b(()=>import("./frontmatter.html-_86ySUcn.js"),[]),meta:{title:"Frontmatter Config",icon:"captions"}}],["/plugins/blog/feed/getter.html",{loader:()=>b(()=>import("./getter.html-81_i_3R0.js"),[]),meta:{title:"Feed Getter",icon:"arrow-up-from-line"}}],["/plugins/blog/feed/guide.html",{loader:()=>b(()=>import("./guide.html-Dpe2TOMU.js"),[]),meta:{title:"Guide",icon:"lightbulb"}}],["/plugins/development/sass-palette/",{loader:()=>b(()=>import("./index.html-Cj2YTN1Z.js"),[]),meta:{title:"sass-palette",icon:"palette"}}],["/plugins/development/sass-palette/config.html",{loader:()=>b(()=>import("./config.html-wBMzL4GZ.js"),[]),meta:{title:"Config",icon:"settings-2"}}],["/plugins/development/sass-palette/guide.html",{loader:()=>b(()=>import("./guide.html-CEKeMtK3.js"),[]),meta:{title:"Guide",icon:"lightbulb"}}],["/plugins/markdown/revealjs/",{loader:()=>b(()=>import("./index.html-DM6xGsaW.js"),[]),meta:{title:"revealjs",icon:"presentation"}}],["/plugins/markdown/revealjs/demo.html",{loader:()=>b(()=>import("./demo.html-BVekhyL9.js"),[]),meta:{title:"Slide Demo",icon:"presentation"}}],["/plugins/markdown/revealjs/themes.html",{loader:()=>b(()=>import("./themes.html-KvQpTGWS.js"),[]),meta:{title:"Reveal.js Themes",icon:"palette"}}],["/plugins/pwa/pwa/",{loader:()=>b(()=>import("./index.html-Ep0GMIP5.js"),[]),meta:{title:"pwa",icon:"layout-grid"}}],["/plugins/pwa/pwa/config.html",{loader:()=>b(()=>import("./config.html-CovVYmLI.js"),[]),meta:{title:"Config",icon:"settings-2"}}],["/plugins/pwa/pwa/guide.html",{loader:()=>b(()=>import("./guide.html-D13J9mPg.js"),[]),meta:{title:"Guide",icon:"lightbulb"}}],["/plugins/seo/seo/",{loader:()=>b(()=>import("./index.html-DDVEjcym.js"),[]),meta:{title:"seo",icon:"scan-search"}}],["/plugins/seo/seo/config.html",{loader:()=>b(()=>import("./config.html-BRxUe6ez.js"),[]),meta:{title:"Config",icon:"settings-2"}}],["/plugins/seo/seo/guide.html",{loader:()=>b(()=>import("./guide.html-Bqmofd-a.js"),[]),meta:{title:"Guide",icon:"lightbulb"}}],["/plugins/seo/sitemap/",{loader:()=>b(()=>import("./index.html-Bm-ymDhq.js"),[]),meta:{title:"sitemap",icon:"network"}}],["/plugins/seo/sitemap/config.html",{loader:()=>b(()=>import("./config.html-BNfGVnBJ.js"),[]),meta:{title:"Config",icon:"settings-2"}}],["/plugins/seo/sitemap/frontmatter.html",{loader:()=>b(()=>import("./frontmatter.html-CHpTu1Yx.js"),[]),meta:{title:"Frontmatter",icon:"captions"}}],["/plugins/seo/sitemap/guide.html",{loader:()=>b(()=>import("./guide.html-BErABGpc.js"),[]),meta:{title:"Guide",icon:"lightbulb"}}],["/tools/helper/node/bundler.html",{loader:()=>b(()=>import("./bundler.html-DjlypUZy.js"),[]),meta:{title:"Bundler Related",icon:"package"}}],["/tools/helper/node/locales.html",{loader:()=>b(()=>import("./locales.html-CVMMpDLm.js"),[]),meta:{title:"Locales Related",icon:"languages"}}],["/tools/helper/node/page.html",{loader:()=>b(()=>import("./page.html-CYZJNEKL.js"),[]),meta:{title:"Page Related",icon:"panel-top"}}],["/zh/plugins/analytics/",{loader:()=>b(()=>import("./index.html-DXbSxJMN.js"),[]),meta:{title:"统计分析插件",icon:"chart-no-axes-combined"}}],["/zh/plugins/analytics/baidu-analytics.html",{loader:()=>b(()=>import("./baidu-analytics.html-Cx_yMDpU.js"),[]),meta:{title:"baidu-analytics",icon:"chart-no-axes-combined"}}],["/zh/plugins/analytics/google-analytics.html",{loader:()=>b(()=>import("./google-analytics.html-CpTfmtGD.js"),[]),meta:{title:"google-analytics",icon:"chart-no-axes-combined"}}],["/zh/plugins/analytics/umami-analytics.html",{loader:()=>b(()=>import("./umami-analytics.html-D0Fn2j6L.js"),[]),meta:{title:"umami-analytics",icon:"chart-no-axes-combined"}}],["/zh/plugins/blog/",{loader:()=>b(()=>import("./index.html-DnjkkHZN.js"),[]),meta:{title:"博客插件",icon:"la:blog"}}],["/zh/plugins/development/",{loader:()=>b(()=>import("./index.html-CtpxyVQG.js"),[]),meta:{title:"开发插件",icon:"server-cog"}}],["/zh/plugins/development/active-header-links.html",{loader:()=>b(()=>import("./active-header-links.html-GVuFxrx5.js"),[]),meta:{title:"active-header-links",icon:"link-2"}}],["/zh/plugins/development/git.html",{loader:()=>b(()=>import("./git.html-DMJ6r1NC.js"),[]),meta:{title:"git",icon:"la:git-alt"}}],["/zh/plugins/development/palette.html",{loader:()=>b(()=>import("./palette.html-BFCAtmug.js"),[]),meta:{title:"palette",icon:"palette"}}],["/zh/plugins/development/reading-time.html",{loader:()=>b(()=>import("./reading-time.html-CsB1CNx_.js"),[]),meta:{title:"reading-time",icon:"book-open-text"}}],["/zh/plugins/development/rtl.html",{loader:()=>b(()=>import("./rtl.html-B9Z-mGRZ.js"),[]),meta:{title:"rtl",icon:"pilcrow-left"}}],["/zh/plugins/development/theme-data.html",{loader:()=>b(()=>import("./theme-data.html-CHXk5itZ.js"),[]),meta:{title:"theme-data",icon:"database"}}],["/zh/plugins/development/toc.html",{loader:()=>b(()=>import("./toc.html-B0KUr4Xn.js"),[]),meta:{title:"toc",icon:"heading"}}],["/zh/plugins/features/",{loader:()=>b(()=>import("./index.html-D--V3n94.js"),[]),meta:{title:"功能插件",icon:"sparkles"}}],["/zh/plugins/features/back-to-top.html",{loader:()=>b(()=>import("./back-to-top.html-kfdrnnmT.js"),[]),meta:{title:"back-to-top",icon:"arrow-up-to-line"}}],["/zh/plugins/features/catalog.html",{loader:()=>b(()=>import("./catalog.html-DNJojp1d.js"),[]),meta:{title:"catalog",icon:"list-tree"}}],["/zh/plugins/features/copy-code.html",{loader:()=>b(()=>import("./copy-code.html-CzvPFuum.js"),[]),meta:{title:"copy-code",icon:"clipboard-copy"}}],["/zh/plugins/features/copyright.html",{loader:()=>b(()=>import("./copyright.html-BojlU_mQ.js"),[]),meta:{title:"copyright",icon:"fa-regular:copyright"}}],["/zh/plugins/features/icon.html",{loader:()=>b(()=>import("./icon.html-DAkcC-JX.js"),[]),meta:{title:"icon",icon:"fa6-solid:icons"}}],["/zh/plugins/features/medium-zoom.html",{loader:()=>b(()=>import("./medium-zoom.html-BJcDfXPR.js"),[]),meta:{title:"medium-zoom",icon:"fullscreen"}}],["/zh/plugins/features/notice.html",{loader:()=>b(()=>import("./notice.html-C55sWILp.js"),[]),meta:{title:"notice",icon:"bell"}}],["/zh/plugins/features/nprogress.html",{loader:()=>b(()=>import("./nprogress.html-N8q0Xhts.js"),[]),meta:{title:"nprogress",icon:"pajamas:progress"}}],["/zh/plugins/features/photo-swipe.html",{loader:()=>b(()=>import("./photo-swipe.html-QxNP3jNa.js"),[]),meta:{title:"photo-swipe",icon:"image-play"}}],["/zh/plugins/features/watermark.html",{loader:()=>b(()=>import("./watermark.html-CZSLXxBj.js"),[]),meta:{title:"watermark",icon:"droplet"}}],["/zh/plugins/markdown/",{loader:()=>b(()=>import("./index.html-ZsoNg47K.js"),[]),meta:{title:"Markdown 插件",icon:"octicon:markdown-16"}}],["/zh/plugins/markdown/append-date.html",{loader:()=>b(()=>import("./append-date.html-BjMb0TaS.js"),[]),meta:{title:"append-date",icon:"calendar"}}],["/zh/plugins/markdown/links-check.html",{loader:()=>b(()=>import("./links-check.html-DRU6ORHY.js"),[]),meta:{title:"links-check",icon:"list-checks"}}],["/zh/plugins/markdown/markdown-container.html",{loader:()=>b(()=>import("./markdown-container.html-CuhVtUOw.js"),[]),meta:{title:"markdown-container",icon:"package"}}],["/zh/plugins/markdown/markdown-ext.html",{loader:()=>b(()=>import("./markdown-ext.html-C8GXRNZk.js"),[]),meta:{title:"markdown-ext",icon:"expand"}}],["/zh/plugins/markdown/markdown-hint.html",{loader:()=>b(()=>import("./markdown-hint.html-Bn63fjxf.js"),[]),meta:{title:"markdown-hint",icon:"siren"}}],["/zh/plugins/markdown/markdown-image.html",{loader:()=>b(()=>import("./markdown-image.html-DEegF7wB.js"),__vite__mapDeps([4,1])),meta:{title:"markdown-image",icon:"image"}}],["/zh/plugins/markdown/markdown-include.html",{loader:()=>b(()=>import("./markdown-include.html-CEUI0KvE.js"),[]),meta:{title:"markdown-include",icon:"between-horizontal-end"}}],["/zh/plugins/markdown/markdown-math.html",{loader:()=>b(()=>import("./markdown-math.html-BnUDXB75.js"),[]),meta:{title:"markdown-math",icon:"sigma"}}],["/zh/plugins/markdown/markdown-stylize.html",{loader:()=>b(()=>import("./markdown-stylize.html-aktW9fPl.js"),[]),meta:{title:"markdown-stylize",icon:"paint-bucket"}}],["/zh/plugins/markdown/markdown-tab.html",{loader:()=>b(()=>import("./markdown-tab.html-B9Ar1A-B.js"),[]),meta:{title:"markdown-tab",icon:"columns-2"}}],["/zh/plugins/markdown/prismjs.html",{loader:()=>b(()=>import("./prismjs.html-Br_2OHuk.js"),[]),meta:{title:"prismjs",icon:"pyramid"}}],["/zh/plugins/markdown/shiki.html",{loader:()=>b(()=>import("./shiki.html-H1bsn05t.js"),[]),meta:{title:"shiki",icon:"highlighter"}}],["/zh/plugins/pwa/",{loader:()=>b(()=>import("./index.html-BrWpl2Tl.js"),[]),meta:{title:"渐进式应用插件",icon:"layout-grid"}}],["/zh/plugins/pwa/remove-pwa.html",{loader:()=>b(()=>import("./remove-pwa.html-HT7wRCfM.js"),[]),meta:{title:"remove-pwa",icon:"trash-2"}}],["/zh/plugins/search/",{loader:()=>b(()=>import("./index.html-BHQSDKl0.js"),[]),meta:{title:"搜索插件",icon:"search"}}],["/zh/plugins/search/docsearch.html",{loader:()=>b(()=>import("./docsearch.html-BBtoS61U.js"),[]),meta:{title:"docsearch",icon:"search"}}],["/zh/plugins/search/guidelines.html",{loader:()=>b(()=>import("./guidelines.html-DhPKHQFU.js"),[]),meta:{title:"搜索插件指南",icon:"signpost"}}],["/zh/plugins/search/search.html",{loader:()=>b(()=>import("./search.html-BdTBaysC.js"),[]),meta:{title:"search",icon:"search"}}],["/zh/plugins/search/slimsearch.html",{loader:()=>b(()=>import("./slimsearch.html-ODhQUhbP.js"),[]),meta:{title:"slimsearch",icon:"search"}}],["/zh/plugins/seo/",{loader:()=>b(()=>import("./index.html-DiBgfCkr.js"),[]),meta:{title:"搜索引擎优化插件",icon:"scan-search"}}],["/zh/plugins/tools/",{loader:()=>b(()=>import("./index.html-C6hF2YrT.js"),[]),meta:{title:"工具插件",icon:"hammer"}}],["/zh/plugins/tools/cache.html",{loader:()=>b(()=>import("./cache.html-BHKxviFx.js"),[]),meta:{title:"cache",icon:"database-zap"}}],["/zh/plugins/tools/google-tag-manager.html",{loader:()=>b(()=>import("./google-tag-manager.html-CmkkiADy.js"),[]),meta:{title:"google-tag-manager",icon:"logos:google-marketing-platform"}}],["/zh/plugins/tools/redirect.html",{loader:()=>b(()=>import("./redirect.html-CmnqlvSb.js"),[]),meta:{title:"redirect",icon:"forward"}}],["/zh/plugins/tools/register-components.html",{loader:()=>b(()=>import("./register-components.html-TWuPF2pd.js"),[]),meta:{title:"register-components",icon:"puzzle"}}],["/zh/themes/default/",{loader:()=>b(()=>import("./index.html-DCPVJ0ku.js"),[]),meta:{title:"默认主题",icon:"palette"}}],["/zh/themes/default/components.html",{loader:()=>b(()=>import("./components.html-B-1A-8tn.js"),[]),meta:{title:"内置组件",icon:"puzzle"}}],["/zh/themes/default/config.html",{loader:()=>b(()=>import("./config.html-AdNXcP9Y.js"),[]),meta:{title:"配置",icon:"settings-2"}}],["/zh/themes/default/extending.html",{loader:()=>b(()=>import("./extending.html-CSozc1NZ.js"),__vite__mapDeps([5,3])),meta:{title:"继承",icon:"cable"}}],["/zh/themes/default/frontmatter.html",{loader:()=>b(()=>import("./frontmatter.html-C7IlrI23.js"),[]),meta:{title:"Frontmatter",icon:"captions"}}],["/zh/themes/default/locale.html",{loader:()=>b(()=>import("./locale.html-CQ9bQo0d.js"),[]),meta:{title:"语言配置",icon:"languages"}}],["/zh/themes/default/markdown.html",{loader:()=>b(()=>import("./markdown.html-rO1omhNc.js"),[]),meta:{title:"Markdown",icon:"octicon:markdown-16"}}],["/zh/themes/default/plugin.html",{loader:()=>b(()=>import("./plugin.html-wW0E69c2.js"),[]),meta:{title:"插件配置",icon:"unplug"}}],["/zh/themes/default/styles.html",{loader:()=>b(()=>import("./styles.html-DJGJjW8y.js"),[]),meta:{title:"样式",icon:"paintbrush-vertical"}}],["/zh/tools/helper/",{loader:()=>b(()=>import("./index.html-BqLGmq4X.js"),[]),meta:{title:"@vuepress/helper"}}],["/zh/tools/helper/client.html",{loader:()=>b(()=>import("./client.html-Ca8lw7lN.js"),[]),meta:{title:"客户端相关",icon:"chrome"}}],["/zh/tools/helper/shared.html",{loader:()=>b(()=>import("./shared.html-BrkRqAFC.js"),[]),meta:{title:"共享方法",icon:"split"}}],["/zh/tools/helper/style.html",{loader:()=>b(()=>import("./style.html-CxZliGne.js"),[]),meta:{title:"样式",icon:"paintbrush-vertical"}}],["/plugins/blog/comment/artalk/",{loader:()=>b(()=>import("./index.html-_WIns1vI.js"),[]),meta:{title:"Artalk",icon:"https://artalk.js.org/favicon.png"}}],["/plugins/blog/comment/artalk/config.html",{loader:()=>b(()=>import("./config.html-BDCtOxkv.js"),[]),meta:{title:"Artalk Options"}}],["/plugins/blog/comment/giscus/",{loader:()=>b(()=>import("./index.html-Dc52JgdB.js"),[]),meta:{title:"Giscus",icon:"github"}}],["/plugins/blog/comment/giscus/config.html",{loader:()=>b(()=>import("./config.html-CT3TkZ-q.js"),[]),meta:{title:"Giscus Options"}}],["/plugins/blog/comment/twikoo/",{loader:()=>b(()=>import("./index.html-oU0BappG.js"),[]),meta:{title:"Twikoo",icon:"https://twikoo.js.org/twikoo-logo-mini.png"}}],["/plugins/blog/comment/twikoo/config.html",{loader:()=>b(()=>import("./config.html-BXNtQGi3.js"),[]),meta:{title:"Twikoo Options"}}],["/plugins/blog/comment/waline/",{loader:()=>b(()=>import("./index.html-C1HbKT7i.js"),__vite__mapDeps([6,7])),meta:{title:"Waline",icon:"https://waline.js.org/favicon.ico"}}],["/plugins/blog/comment/waline/config.html",{loader:()=>b(()=>import("./config.html-DQe4Klj6.js"),[]),meta:{title:"Waline Config"}}],["/zh/plugins/blog/blog/",{loader:()=>b(()=>import("./index.html-Be8ZMTuL.js"),[]),meta:{title:"blog",icon:"la:blog"}}],["/zh/plugins/blog/blog/config.html",{loader:()=>b(()=>import("./config.html-CpOM8tMg.js"),[]),meta:{title:"配置",icon:"settings-2"}}],["/zh/plugins/blog/blog/guide.html",{loader:()=>b(()=>import("./guide.html-BUw5pNmN.js"),[]),meta:{title:"指南",icon:"lightbulb"}}],["/zh/plugins/blog/comment/",{loader:()=>b(()=>import("./index.html-FwPKtQGY.js"),[]),meta:{title:"comment"}}],["/zh/plugins/blog/comment/guide.html",{loader:()=>b(()=>import("./guide.html-ylzay-ZE.js"),[]),meta:{title:"指南",icon:"lightbulb"}}],["/zh/plugins/blog/feed/",{loader:()=>b(()=>import("./index.html-C-TSl5sN.js"),[]),meta:{title:"feed",icon:"rss"}}],["/zh/plugins/blog/feed/channel.html",{loader:()=>b(()=>import("./channel.html-scYDdoWO.js"),[]),meta:{title:"频道设置",icon:"tv"}}],["/zh/plugins/blog/feed/config.html",{loader:()=>b(()=>import("./config.html-Cps2l2FY.js"),[]),meta:{title:"插件配置",icon:"settings-2"}}],["/zh/plugins/blog/feed/frontmatter.html",{loader:()=>b(()=>import("./frontmatter.html-DPeLkhHK.js"),[]),meta:{title:"Frontmatter 配置",icon:"captions"}}],["/zh/plugins/blog/feed/getter.html",{loader:()=>b(()=>import("./getter.html-Xs3m15Ly.js"),[]),meta:{title:"Feed 获取器",icon:"arrow-up-from-line"}}],["/zh/plugins/blog/feed/guide.html",{loader:()=>b(()=>import("./guide.html-D7jY5-hj.js"),[]),meta:{title:"指南",icon:"lightbulb"}}],["/zh/plugins/development/sass-palette/",{loader:()=>b(()=>import("./index.html-Bn7Abdfw.js"),[]),meta:{title:"sass-palette",icon:"palette"}}],["/zh/plugins/development/sass-palette/config.html",{loader:()=>b(()=>import("./config.html-BHBgSm1R.js"),[]),meta:{title:"配置",icon:"settings-2"}}],["/zh/plugins/development/sass-palette/guide.html",{loader:()=>b(()=>import("./guide.html-dZPrPu1h.js"),[]),meta:{title:"指南",icon:"lightbulb"}}],["/zh/plugins/markdown/revealjs/",{loader:()=>b(()=>import("./index.html-CywMmD2b.js"),[]),meta:{title:"revealjs",icon:"presentation"}}],["/zh/plugins/markdown/revealjs/demo.html",{loader:()=>b(()=>import("./demo.html-BGF2Yxf8.js"),[]),meta:{title:"幻灯片演示",icon:"presentation"}}],["/zh/plugins/markdown/revealjs/themes.html",{loader:()=>b(()=>import("./themes.html-IlZ51LeS.js"),[]),meta:{title:"幻灯片主题",icon:"palette"}}],["/zh/plugins/pwa/pwa/",{loader:()=>b(()=>import("./index.html-C4b7NzqA.js"),[]),meta:{title:"pwa",icon:"layout-grid"}}],["/zh/plugins/pwa/pwa/config.html",{loader:()=>b(()=>import("./config.html-Bw56fyyS.js"),[]),meta:{title:"配置",icon:"settings-2"}}],["/zh/plugins/pwa/pwa/guide.html",{loader:()=>b(()=>import("./guide.html-CpYz8CkZ.js"),[]),meta:{title:"指南",icon:"lightbulb"}}],["/zh/plugins/seo/seo/",{loader:()=>b(()=>import("./index.html-CcD2PRPX.js"),[]),meta:{title:"seo",icon:"scan-search"}}],["/zh/plugins/seo/seo/config.html",{loader:()=>b(()=>import("./config.html-DXSc_4Iq.js"),[]),meta:{title:"选项",icon:"settings-2"}}],["/zh/plugins/seo/seo/guide.html",{loader:()=>b(()=>import("./guide.html-D0HAKZc4.js"),[]),meta:{title:"指南",icon:"lightbulb"}}],["/zh/plugins/seo/sitemap/",{loader:()=>b(()=>import("./index.html-B_yuvmKh.js"),[]),meta:{title:"sitemap",icon:"network"}}],["/zh/plugins/seo/sitemap/config.html",{loader:()=>b(()=>import("./config.html-BokwSI7j.js"),[]),meta:{title:"配置",icon:"settings-2"}}],["/zh/plugins/seo/sitemap/frontmatter.html",{loader:()=>b(()=>import("./frontmatter.html-BLqgvLKE.js"),[]),meta:{title:"Frontmatter",icon:"captions"}}],["/zh/plugins/seo/sitemap/guide.html",{loader:()=>b(()=>import("./guide.html-BwpySlTO.js"),[]),meta:{title:"指南",icon:"lightbulb"}}],["/zh/tools/helper/node/bundler.html",{loader:()=>b(()=>import("./bundler.html-BDsjRobi.js"),[]),meta:{title:"打包器相关",icon:"package"}}],["/zh/tools/helper/node/locales.html",{loader:()=>b(()=>import("./locales.html-CG22G9ry.js"),[]),meta:{title:"多语言相关",icon:"languages"}}],["/zh/tools/helper/node/page.html",{loader:()=>b(()=>import("./page.html-CELAEiAC.js"),[]),meta:{title:"页面相关",icon:"panel-top"}}],["/zh/plugins/blog/comment/artalk/",{loader:()=>b(()=>import("./index.html-CZ4RdnoI.js"),[]),meta:{title:"Artalk",icon:"https://artalk.js.org/favicon.png"}}],["/zh/plugins/blog/comment/artalk/config.html",{loader:()=>b(()=>import("./config.html-BhAVdCSt.js"),[]),meta:{title:"Artalk 选项"}}],["/zh/plugins/blog/comment/giscus/",{loader:()=>b(()=>import("./index.html-SmbT7GJI.js"),[]),meta:{title:"Giscus",icon:"github"}}],["/zh/plugins/blog/comment/giscus/config.html",{loader:()=>b(()=>import("./config.html-CtB7tRPM.js"),[]),meta:{title:"Giscus 选项"}}],["/zh/plugins/blog/comment/twikoo/",{loader:()=>b(()=>import("./index.html-eiF2jIVT.js"),[]),meta:{title:"Twikoo",icon:"https://twikoo.js.org/twikoo-logo-mini.png"}}],["/zh/plugins/blog/comment/twikoo/config.html",{loader:()=>b(()=>import("./config.html-Dn-z9-9x.js"),[]),meta:{title:"Twikoo 选项"}}],["/zh/plugins/blog/comment/waline/",{loader:()=>b(()=>import("./index.html-dFD4TqPg.js"),__vite__mapDeps([8,7])),meta:{title:"Waline",icon:"https://waline.js.org/favicon.ico"}}],["/zh/plugins/blog/comment/waline/config.html",{loader:()=>b(()=>import("./config.html-CNruBaqA.js"),[]),meta:{title:"Waline 选项"}}],["/404.html",{loader:()=>b(()=>import("./404.html-Ddh2xQcK.js"),[]),meta:{title:""}}],["/tools/helper/node/",{loader:()=>b(()=>import("./index.html-C3SvV2uG.js"),[]),meta:{title:"Node"}}],["/zh/tools/helper/node/",{loader:()=>b(()=>import("./index.html-BAT88g1a.js"),[]),meta:{title:"Node"}}]]);function N_(){return Rd().__VUE_DEVTOOLS_GLOBAL_HOOK__}function Rd(){return typeof navigator<"u"&&typeof window<"u"?window:typeof globalThis<"u"?globalThis:{}}const M_=typeof Proxy=="function",z_="devtools-plugin:setup",$_="plugin:settings:set";let Yn,ds;function B_(){var e;return Yn!==void 0||(typeof window<"u"&&window.performance?(Yn=!0,ds=window.performance):typeof globalThis<"u"&&(!((e=globalThis.perf_hooks)===null||e===void 0)&&e.performance)?(Yn=!0,ds=globalThis.perf_hooks.performance):Yn=!1),Yn}function F_(){return B_()?ds.now():Date.now()}class H_{constructor(t,n){this.target=null,this.targetQueue=[],this.onQueue=[],this.plugin=t,this.hook=n;const r={};if(t.settings)for(const s in t.settings){const l=t.settings[s];r[s]=l.defaultValue}const o=`__vue-devtools-plugin-settings__${t.id}`;let i=Object.assign({},r);try{const s=localStorage.getItem(o),l=JSON.parse(s);Object.assign(i,l)}catch{}this.fallbacks={getSettings(){return i},setSettings(s){try{localStorage.setItem(o,JSON.stringify(s))}catch{}i=s},now(){return F_()}},n&&n.on($_,(s,l)=>{s===this.plugin.id&&this.fallbacks.setSettings(l)}),this.proxiedOn=new Proxy({},{get:(s,l)=>this.target?this.target.on[l]:(...a)=>{this.onQueue.push({method:l,args:a})}}),this.proxiedTarget=new Proxy({},{get:(s,l)=>this.target?this.target[l]:l==="on"?this.proxiedOn:Object.keys(this.fallbacks).includes(l)?(...a)=>(this.targetQueue.push({method:l,args:a,resolve:()=>{}}),this.fallbacks[l](...a)):(...a)=>new Promise(u=>{this.targetQueue.push({method:l,args:a,resolve:u})})})}async setRealTarget(t){this.target=t;for(const n of this.onQueue)this.target.on[n.method](...n.args);for(const n of this.targetQueue)n.resolve(await this.target[n.method](...n.args))}}function j_(e,t){const n=e,r=Rd(),o=N_(),i=M_&&n.enableEarlyProxy;if(o&&(r.__VUE_DEVTOOLS_PLUGIN_API_AVAILABLE__||!i))o.emit(z_,e,t);else{const s=i?new H_(n,o):null;(r.__VUE_DEVTOOLS_PLUGINS__=r.__VUE_DEVTOOLS_PLUGINS__||[]).push({pluginDescriptor:n,setupFn:t,proxy:s}),s&&t(s.proxiedTarget)}}/*! + * vue-router v4.5.0 + * (c) 2024 Eduardo San Martin Morote + * @license MIT + */const qt=typeof document<"u";function Dd(e){return typeof e=="object"||"displayName"in e||"props"in e||"__vccOpts"in e}function U_(e){return e.__esModule||e[Symbol.toStringTag]==="Module"||e.default&&Dd(e.default)}const ve=Object.assign;function zi(e,t){const n={};for(const r in t){const o=t[r];n[r]=vt(o)?o.map(e):e(o)}return n}const $r=()=>{},vt=Array.isArray,Vd=/#/g,K_=/&/g,W_=/\//g,G_=/=/g,q_=/\?/g,Nd=/\+/g,Y_=/%5B/g,X_=/%5D/g,Md=/%5E/g,Z_=/%60/g,zd=/%7B/g,J_=/%7C/g,$d=/%7D/g,Q_=/%20/g;function Xs(e){return encodeURI(""+e).replace(J_,"|").replace(Y_,"[").replace(X_,"]")}function eg(e){return Xs(e).replace(zd,"{").replace($d,"}").replace(Md,"^")}function fs(e){return Xs(e).replace(Nd,"%2B").replace(Q_,"+").replace(Vd,"%23").replace(K_,"%26").replace(Z_,"`").replace(zd,"{").replace($d,"}").replace(Md,"^")}function tg(e){return fs(e).replace(G_,"%3D")}function ng(e){return Xs(e).replace(Vd,"%23").replace(q_,"%3F")}function rg(e){return e==null?"":ng(e).replace(W_,"%2F")}function dr(e){try{return decodeURIComponent(""+e)}catch{}return""+e}const og=/\/$/,ig=e=>e.replace(og,"");function $i(e,t,n="/"){let r,o={},i="",s="";const l=t.indexOf("#");let a=t.indexOf("?");return l=0&&(a=-1),a>-1&&(r=t.slice(0,a),i=t.slice(a+1,l>-1?l:t.length),o=e(i)),l>-1&&(r=r||t.slice(0,l),s=t.slice(l,t.length)),r=ug(r??t,n),{fullPath:r+(i&&"?")+i+s,path:r,query:o,hash:dr(s)}}function sg(e,t){const n=t.query?e(t.query):"";return t.path+(n&&"?")+n+(t.hash||"")}function ma(e,t){return!t||!e.toLowerCase().startsWith(t.toLowerCase())?e:e.slice(t.length)||"/"}function lg(e,t,n){const r=t.matched.length-1,o=n.matched.length-1;return r>-1&&r===o&&En(t.matched[r],n.matched[o])&&Bd(t.params,n.params)&&e(t.query)===e(n.query)&&t.hash===n.hash}function En(e,t){return(e.aliasOf||e)===(t.aliasOf||t)}function Bd(e,t){if(Object.keys(e).length!==Object.keys(t).length)return!1;for(const n in e)if(!ag(e[n],t[n]))return!1;return!0}function ag(e,t){return vt(e)?_a(e,t):vt(t)?_a(t,e):e===t}function _a(e,t){return vt(t)?e.length===t.length&&e.every((n,r)=>n===t[r]):e.length===1&&e[0]===t}function ug(e,t){if(e.startsWith("/"))return e;if(!e)return t;const n=t.split("/"),r=e.split("/"),o=r[r.length-1];(o===".."||o===".")&&r.push("");let i=n.length-1,s,l;for(s=0;s1&&i--;else break;return n.slice(0,i).join("/")+"/"+r.slice(s).join("/")}const Wt={path:"/",name:void 0,params:{},query:{},hash:"",fullPath:"/",matched:[],meta:{},redirectedFrom:void 0};var Zr;(function(e){e.pop="pop",e.push="push"})(Zr||(Zr={}));var Br;(function(e){e.back="back",e.forward="forward",e.unknown=""})(Br||(Br={}));function cg(e){if(!e)if(qt){const t=document.querySelector("base");e=t&&t.getAttribute("href")||"/",e=e.replace(/^\w+:\/\/[^\/]+/,"")}else e="/";return e[0]!=="/"&&e[0]!=="#"&&(e="/"+e),ig(e)}const dg=/^[^#]+#/;function fg(e,t){return e.replace(dg,"#")+t}function pg(e,t){const n=document.documentElement.getBoundingClientRect(),r=e.getBoundingClientRect();return{behavior:t.behavior,left:r.left-n.left-(t.left||0),top:r.top-n.top-(t.top||0)}}const fi=()=>({left:window.scrollX,top:window.scrollY});function hg(e){let t;if("el"in e){const n=e.el,r=typeof n=="string"&&n.startsWith("#"),o=typeof n=="string"?r?document.getElementById(n.slice(1)):document.querySelector(n):n;if(!o)return;t=pg(o,e)}else t=e;"scrollBehavior"in document.documentElement.style?window.scrollTo(t):window.scrollTo(t.left!=null?t.left:window.scrollX,t.top!=null?t.top:window.scrollY)}function ga(e,t){return(history.state?history.state.position-t:-1)+e}const ps=new Map;function mg(e,t){ps.set(e,t)}function _g(e){const t=ps.get(e);return ps.delete(e),t}let gg=()=>location.protocol+"//"+location.host;function Fd(e,t){const{pathname:n,search:r,hash:o}=t,i=e.indexOf("#");if(i>-1){let l=o.includes(e.slice(i))?e.slice(i).length:1,a=o.slice(l);return a[0]!=="/"&&(a="/"+a),ma(a,"")}return ma(n,e)+r+o}function vg(e,t,n,r){let o=[],i=[],s=null;const l=({state:f})=>{const p=Fd(e,location),_=n.value,g=t.value;let v=0;if(f){if(n.value=p,t.value=f,s&&s===_){s=null;return}v=g?f.position-g.position:0}else r(p);o.forEach(y=>{y(n.value,_,{delta:v,type:Zr.pop,direction:v?v>0?Br.forward:Br.back:Br.unknown})})};function a(){s=n.value}function u(f){o.push(f);const p=()=>{const _=o.indexOf(f);_>-1&&o.splice(_,1)};return i.push(p),p}function d(){const{history:f}=window;f.state&&f.replaceState(ve({},f.state,{scroll:fi()}),"")}function c(){for(const f of i)f();i=[],window.removeEventListener("popstate",l),window.removeEventListener("beforeunload",d)}return window.addEventListener("popstate",l),window.addEventListener("beforeunload",d,{passive:!0}),{pauseListeners:a,listen:u,destroy:c}}function va(e,t,n,r=!1,o=!1){return{back:e,current:t,forward:n,replaced:r,position:window.history.length,scroll:o?fi():null}}function Eg(e){const{history:t,location:n}=window,r={value:Fd(e,n)},o={value:t.state};o.value||i(r.value,{back:null,current:r.value,forward:null,position:t.length-1,replaced:!0,scroll:null},!0);function i(a,u,d){const c=e.indexOf("#"),f=c>-1?(n.host&&document.querySelector("base")?e:e.slice(c))+a:gg()+e+a;try{t[d?"replaceState":"pushState"](u,"",f),o.value=u}catch(p){console.error(p),n[d?"replace":"assign"](f)}}function s(a,u){const d=ve({},t.state,va(o.value.back,a,o.value.forward,!0),u,{position:o.value.position});i(a,d,!0),r.value=a}function l(a,u){const d=ve({},o.value,t.state,{forward:a,scroll:fi()});i(d.current,d,!0);const c=ve({},va(r.value,a,null),{position:d.position+1},u);i(a,c,!1),r.value=a}return{location:r,state:o,push:l,replace:s}}function bg(e){e=cg(e);const t=Eg(e),n=vg(e,t.state,t.location,t.replace);function r(i,s=!0){s||n.pauseListeners(),history.go(i)}const o=ve({location:"",base:e,go:r,createHref:fg.bind(null,e)},t,n);return Object.defineProperty(o,"location",{enumerable:!0,get:()=>t.location.value}),Object.defineProperty(o,"state",{enumerable:!0,get:()=>t.state.value}),o}function Hd(e){return typeof e=="string"||e&&typeof e=="object"}function jd(e){return typeof e=="string"||typeof e=="symbol"}const Ud=Symbol("");var Ea;(function(e){e[e.aborted=4]="aborted",e[e.cancelled=8]="cancelled",e[e.duplicated=16]="duplicated"})(Ea||(Ea={}));function fr(e,t){return ve(new Error,{type:e,[Ud]:!0},t)}function Ut(e,t){return e instanceof Error&&Ud in e&&(t==null||!!(e.type&t))}const ba="[^/]+?",yg={sensitive:!1,strict:!1,start:!0,end:!0},Tg=/[.+*?^${}()[\]/\\]/g;function wg(e,t){const n=ve({},yg,t),r=[];let o=n.start?"^":"";const i=[];for(const u of e){const d=u.length?[]:[90];n.strict&&!u.length&&(o+="/");for(let c=0;ct.length?t.length===1&&t[0]===80?1:-1:0}function Kd(e,t){let n=0;const r=e.score,o=t.score;for(;n0&&t[t.length-1]<0}const Sg={type:0,value:""},kg=/[a-zA-Z0-9_]/;function Og(e){if(!e)return[[]];if(e==="/")return[[Sg]];if(!e.startsWith("/"))throw new Error(`Invalid path "${e}"`);function t(p){throw new Error(`ERR (${n})/"${u}": ${p}`)}let n=0,r=n;const o=[];let i;function s(){i&&o.push(i),i=[]}let l=0,a,u="",d="";function c(){u&&(n===0?i.push({type:0,value:u}):n===1||n===2||n===3?(i.length>1&&(a==="*"||a==="+")&&t(`A repeatable param (${u}) must be alone in its segment. eg: '/:ids+.`),i.push({type:1,value:u,regexp:d,repeatable:a==="*"||a==="+",optional:a==="*"||a==="?"})):t("Invalid state to consume buffer"),u="")}function f(){u+=a}for(;l{s(h)}:$r}function s(c){if(jd(c)){const f=r.get(c);f&&(r.delete(c),n.splice(n.indexOf(f),1),f.children.forEach(s),f.alias.forEach(s))}else{const f=n.indexOf(c);f>-1&&(n.splice(f,1),c.record.name&&r.delete(c.record.name),c.children.forEach(s),c.alias.forEach(s))}}function l(){return n}function a(c){const f=Lg(c,n);n.splice(f,0,c),c.record.name&&!Aa(c)&&r.set(c.record.name,c)}function u(c,f){let p,_={},g,v;if("name"in c&&c.name){if(p=r.get(c.name),!p)throw fr(1,{location:c});v=p.record.name,_=ve(Ta(f.params,p.keys.filter(h=>!h.optional).concat(p.parent?p.parent.keys.filter(h=>h.optional):[]).map(h=>h.name)),c.params&&Ta(c.params,p.keys.map(h=>h.name))),g=p.stringify(_)}else if(c.path!=null)g=c.path,p=n.find(h=>h.re.test(g)),p&&(_=p.parse(g),v=p.record.name);else{if(p=f.name?r.get(f.name):n.find(h=>h.re.test(f.path)),!p)throw fr(1,{location:c,currentLocation:f});v=p.record.name,_=ve({},f.params,c.params),g=p.stringify(_)}const y=[];let w=p;for(;w;)y.unshift(w.record),w=w.parent;return{name:v,path:g,params:_,matched:y,meta:Ig(y)}}e.forEach(c=>i(c));function d(){n.length=0,r.clear()}return{addRoute:i,resolve:u,removeRoute:s,clearRoutes:d,getRoutes:l,getRecordMatcher:o}}function Ta(e,t){const n={};for(const r of t)r in e&&(n[r]=e[r]);return n}function wa(e){const t={path:e.path,redirect:e.redirect,name:e.name,meta:e.meta||{},aliasOf:e.aliasOf,beforeEnter:e.beforeEnter,props:xg(e),children:e.children||[],instances:{},leaveGuards:new Set,updateGuards:new Set,enterCallbacks:{},components:"components"in e?e.components||null:e.component&&{default:e.component}};return Object.defineProperty(t,"mods",{value:{}}),t}function xg(e){const t={},n=e.props||!1;if("component"in e)t.default=n;else for(const r in e.components)t[r]=typeof n=="object"?n[r]:n;return t}function Aa(e){for(;e;){if(e.record.aliasOf)return!0;e=e.parent}return!1}function Ig(e){return e.reduce((t,n)=>ve(t,n.meta),{})}function Sa(e,t){const n={};for(const r in e)n[r]=r in t?t[r]:e[r];return n}function Lg(e,t){let n=0,r=t.length;for(;n!==r;){const i=n+r>>1;Kd(e,t[i])<0?r=i:n=i+1}const o=Rg(e);return o&&(r=t.lastIndexOf(o,r-1)),r}function Rg(e){let t=e;for(;t=t.parent;)if(Wd(t)&&Kd(e,t)===0)return t}function Wd({record:e}){return!!(e.name||e.components&&Object.keys(e.components).length||e.redirect)}function Dg(e){const t={};if(e===""||e==="?")return t;const r=(e[0]==="?"?e.slice(1):e).split("&");for(let o=0;oi&&fs(i)):[r&&fs(r)]).forEach(i=>{i!==void 0&&(t+=(t.length?"&":"")+n,i!=null&&(t+="="+i))})}return t}function Vg(e){const t={};for(const n in e){const r=e[n];r!==void 0&&(t[n]=vt(r)?r.map(o=>o==null?null:""+o):r==null?r:""+r)}return t}const Ng=Symbol(""),Oa=Symbol(""),pi=Symbol(""),Zs=Symbol(""),hs=Symbol("");function kr(){let e=[];function t(r){return e.push(r),()=>{const o=e.indexOf(r);o>-1&&e.splice(o,1)}}function n(){e=[]}return{add:t,list:()=>e.slice(),reset:n}}function fn(e,t,n,r,o,i=s=>s()){const s=r&&(r.enterCallbacks[o]=r.enterCallbacks[o]||[]);return()=>new Promise((l,a)=>{const u=f=>{f===!1?a(fr(4,{from:n,to:t})):f instanceof Error?a(f):Hd(f)?a(fr(2,{from:t,to:f})):(s&&r.enterCallbacks[o]===s&&typeof f=="function"&&s.push(f),l())},d=i(()=>e.call(r&&r.instances[o],t,n,u));let c=Promise.resolve(d);e.length<3&&(c=c.then(u)),c.catch(f=>a(f))})}function Bi(e,t,n,r,o=i=>i()){const i=[];for(const s of e)for(const l in s.components){let a=s.components[l];if(!(t!=="beforeRouteEnter"&&!s.instances[l]))if(Dd(a)){const d=(a.__vccOpts||a)[t];d&&i.push(fn(d,n,r,s,l,o))}else{let u=a();i.push(()=>u.then(d=>{if(!d)throw new Error(`Couldn't resolve component "${l}" at "${s.path}"`);const c=U_(d)?d.default:d;s.mods[l]=d,s.components[l]=c;const p=(c.__vccOpts||c)[t];return p&&fn(p,n,r,s,l,o)()}))}}return i}function Pa(e){const t=Ue(pi),n=Ue(Zs),r=N(()=>{const a=pn(e.to);return t.resolve(a)}),o=N(()=>{const{matched:a}=r.value,{length:u}=a,d=a[u-1],c=n.matched;if(!d||!c.length)return-1;const f=c.findIndex(En.bind(null,d));if(f>-1)return f;const p=Ca(a[u-2]);return u>1&&Ca(d)===p&&c[c.length-1].path!==p?c.findIndex(En.bind(null,a[u-2])):f}),i=N(()=>o.value>-1&&Fg(n.params,r.value.params)),s=N(()=>o.value>-1&&o.value===n.matched.length-1&&Bd(n.params,r.value.params));function l(a={}){if(Bg(a)){const u=t[pn(e.replace)?"replace":"push"](pn(e.to)).catch($r);return e.viewTransition&&typeof document<"u"&&"startViewTransition"in document&&document.startViewTransition(()=>u),u}return Promise.resolve()}if(qt){const a=yn();if(a){const u={route:r.value,isActive:i.value,isExactActive:s.value,error:null};a.__vrl_devtools=a.__vrl_devtools||[],a.__vrl_devtools.push(u),ud(()=>{u.route=r.value,u.isActive=i.value,u.isExactActive=s.value,u.error=Hd(pn(e.to))?null:'Invalid "to" value'},{flush:"post"})}}return{route:r,href:N(()=>r.value.href),isActive:i,isExactActive:s,navigate:l}}function Mg(e){return e.length===1?e[0]:e}const zg=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:Pa,setup(e,{slots:t}){const n=zn(Pa(e)),{options:r}=Ue(pi),o=N(()=>({[xa(e.activeClass,r.linkActiveClass,"router-link-active")]:n.isActive,[xa(e.exactActiveClass,r.linkExactActiveClass,"router-link-exact-active")]:n.isExactActive}));return()=>{const i=t.default&&Mg(t.default(n));return e.custom?i:H("a",{"aria-current":n.isExactActive?e.ariaCurrentValue:null,href:n.href,onClick:n.navigate,class:o.value},i)}}}),$g=zg;function Bg(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 Fg(e,t){for(const n in t){const r=t[n],o=e[n];if(typeof r=="string"){if(r!==o)return!1}else if(!vt(o)||o.length!==r.length||r.some((i,s)=>i!==o[s]))return!1}return!0}function Ca(e){return e?e.aliasOf?e.aliasOf.path:e.path:""}const xa=(e,t,n)=>e??t??n,Hg=fe({name:"RouterView",inheritAttrs:!1,props:{name:{type:String,default:"default"},route:Object},compatConfig:{MODE:3},setup(e,{attrs:t,slots:n}){const r=Ue(hs),o=N(()=>e.route||r.value),i=Ue(Oa,0),s=N(()=>{let u=pn(i);const{matched:d}=o.value;let c;for(;(c=d[u])&&!c.components;)u++;return u}),l=N(()=>o.value.matched[s.value]);Nn(Oa,N(()=>s.value+1)),Nn(Ng,l),Nn(hs,o);const a=ue();return Be(()=>[a.value,l.value,e.name],([u,d,c],[f,p,_])=>{d&&(d.instances[c]=u,p&&p!==d&&u&&u===f&&(d.leaveGuards.size||(d.leaveGuards=p.leaveGuards),d.updateGuards.size||(d.updateGuards=p.updateGuards))),u&&d&&(!p||!En(d,p)||!f)&&(d.enterCallbacks[c]||[]).forEach(g=>g(u))},{flush:"post"}),()=>{const u=o.value,d=e.name,c=l.value,f=c&&c.components[d];if(!f)return Ia(n.default,{Component:f,route:u});const p=c.props[d],_=p?p===!0?u.params:typeof p=="function"?p(u):p:null,v=H(f,ve({},_,t,{onVnodeUnmounted:y=>{y.component.isUnmounted&&(c.instances[d]=null)},ref:a}));if(qt&&v.ref){const y={depth:s.value,name:c.name,path:c.path,meta:c.meta};(vt(v.ref)?v.ref.map(h=>h.i):[v.ref.i]).forEach(h=>{h.__vrv_devtools=y})}return Ia(n.default,{Component:v,route:u})||v}}});function Ia(e,t){if(!e)return null;const n=e(t);return n.length===1?n[0]:n}const jg=Hg;function Or(e,t){const n=ve({},e,{matched:e.matched.map(r=>ev(r,["instances","children","aliasOf"]))});return{_custom:{type:null,readOnly:!0,display:e.fullPath,tooltip:t,value:n}}}function wo(e){return{_custom:{display:e}}}let Ug=0;function Kg(e,t,n){if(t.__hasDevtools)return;t.__hasDevtools=!0;const r=Ug++;j_({id:"org.vuejs.router"+(r?"."+r:""),label:"Vue Router",packageName:"vue-router",homepage:"https://router.vuejs.org",logo:"https://router.vuejs.org/logo.png",componentStateTypes:["Routing"],app:e},o=>{typeof o.now!="function"&&console.warn("[Vue Router]: You seem to be using an outdated version of Vue Devtools. Are you still using the Beta release instead of the stable one? You can find the links at https://devtools.vuejs.org/guide/installation.html."),o.on.inspectComponent((d,c)=>{d.instanceData&&d.instanceData.state.push({type:"Routing",key:"$route",editable:!1,value:Or(t.currentRoute.value,"Current Route")})}),o.on.visitComponentTree(({treeNode:d,componentInstance:c})=>{if(c.__vrv_devtools){const f=c.__vrv_devtools;d.tags.push({label:(f.name?`${f.name.toString()}: `:"")+f.path,textColor:0,tooltip:"This component is rendered by <router-view>",backgroundColor:Gd})}vt(c.__vrl_devtools)&&(c.__devtoolsApi=o,c.__vrl_devtools.forEach(f=>{let p=f.route.path,_=Xd,g="",v=0;f.error?(p=f.error,_=Xg,v=Zg):f.isExactActive?(_=Yd,g="This is exactly active"):f.isActive&&(_=qd,g="This link is active"),d.tags.push({label:p,textColor:v,tooltip:g,backgroundColor:_})}))}),Be(t.currentRoute,()=>{a(),o.notifyComponentUpdate(),o.sendInspectorTree(l),o.sendInspectorState(l)});const i="router:navigations:"+r;o.addTimelineLayer({id:i,label:`Router${r?" "+r:""} Navigations`,color:4237508}),t.onError((d,c)=>{o.addTimelineEvent({layerId:i,event:{title:"Error during Navigation",subtitle:c.fullPath,logType:"error",time:o.now(),data:{error:d},groupId:c.meta.__navigationId}})});let s=0;t.beforeEach((d,c)=>{const f={guard:wo("beforeEach"),from:Or(c,"Current Location during this navigation"),to:Or(d,"Target location")};Object.defineProperty(d.meta,"__navigationId",{value:s++}),o.addTimelineEvent({layerId:i,event:{time:o.now(),title:"Start of navigation",subtitle:d.fullPath,data:f,groupId:d.meta.__navigationId}})}),t.afterEach((d,c,f)=>{const p={guard:wo("afterEach")};f?(p.failure={_custom:{type:Error,readOnly:!0,display:f?f.message:"",tooltip:"Navigation Failure",value:f}},p.status=wo("❌")):p.status=wo("✅"),p.from=Or(c,"Current Location during this navigation"),p.to=Or(d,"Target location"),o.addTimelineEvent({layerId:i,event:{title:"End of navigation",subtitle:d.fullPath,time:o.now(),data:p,logType:f?"warning":"default",groupId:d.meta.__navigationId}})});const l="router-inspector:"+r;o.addInspector({id:l,label:"Routes"+(r?" "+r:""),icon:"book",treeFilterPlaceholder:"Search routes"});function a(){if(!u)return;const d=u;let c=n.getRoutes().filter(f=>!f.parent||!f.parent.record.components);c.forEach(Qd),d.filter&&(c=c.filter(f=>ms(f,d.filter.toLowerCase()))),c.forEach(f=>Jd(f,t.currentRoute.value)),d.rootNodes=c.map(Zd)}let u;o.on.getInspectorTree(d=>{u=d,d.app===e&&d.inspectorId===l&&a()}),o.on.getInspectorState(d=>{if(d.app===e&&d.inspectorId===l){const f=n.getRoutes().find(p=>p.record.__vd_id===d.nodeId);f&&(d.state={options:Gg(f)})}}),o.sendInspectorTree(l),o.sendInspectorState(l)})}function Wg(e){return e.optional?e.repeatable?"*":"?":e.repeatable?"+":""}function Gg(e){const{record:t}=e,n=[{editable:!1,key:"path",value:t.path}];return t.name!=null&&n.push({editable:!1,key:"name",value:t.name}),n.push({editable:!1,key:"regexp",value:e.re}),e.keys.length&&n.push({editable:!1,key:"keys",value:{_custom:{type:null,readOnly:!0,display:e.keys.map(r=>`${r.name}${Wg(r)}`).join(" "),tooltip:"Param keys",value:e.keys}}}),t.redirect!=null&&n.push({editable:!1,key:"redirect",value:t.redirect}),e.alias.length&&n.push({editable:!1,key:"aliases",value:e.alias.map(r=>r.record.path)}),Object.keys(e.record.meta).length&&n.push({editable:!1,key:"meta",value:e.record.meta}),n.push({key:"score",editable:!1,value:{_custom:{type:null,readOnly:!0,display:e.score.map(r=>r.join(", ")).join(" | "),tooltip:"Score used to sort routes",value:e.score}}}),n}const Gd=15485081,qd=2450411,Yd=8702998,qg=2282478,Xd=16486972,Yg=6710886,Xg=16704226,Zg=12131356;function Zd(e){const t=[],{record:n}=e;n.name!=null&&t.push({label:String(n.name),textColor:0,backgroundColor:qg}),n.aliasOf&&t.push({label:"alias",textColor:0,backgroundColor:Xd}),e.__vd_match&&t.push({label:"matches",textColor:0,backgroundColor:Gd}),e.__vd_exactActive&&t.push({label:"exact",textColor:0,backgroundColor:Yd}),e.__vd_active&&t.push({label:"active",textColor:0,backgroundColor:qd}),n.redirect&&t.push({label:typeof n.redirect=="string"?`redirect: ${n.redirect}`:"redirects",textColor:16777215,backgroundColor:Yg});let r=n.__vd_id;return r==null&&(r=String(Jg++),n.__vd_id=r),{id:r,label:n.path,tags:t,children:e.children.map(Zd)}}let Jg=0;const Qg=/^\/(.*)\/([a-z]*)$/;function Jd(e,t){const n=t.matched.length&&En(t.matched[t.matched.length-1],e.record);e.__vd_exactActive=e.__vd_active=n,n||(e.__vd_active=t.matched.some(r=>En(r,e.record))),e.children.forEach(r=>Jd(r,t))}function Qd(e){e.__vd_match=!1,e.children.forEach(Qd)}function ms(e,t){const n=String(e.re).match(Qg);if(e.__vd_match=!1,!n||n.length<3)return!1;if(new RegExp(n[1].replace(/\$$/,""),n[2]).test(t))return e.children.forEach(s=>ms(s,t)),e.record.path!=="/"||t==="/"?(e.__vd_match=e.re.test(t),!0):!1;const o=e.record.path.toLowerCase(),i=dr(o);return!t.startsWith("/")&&(i.includes(t)||o.includes(t))||i.startsWith(t)||o.startsWith(t)||e.record.name&&String(e.record.name).includes(t)?!0:e.children.some(s=>ms(s,t))}function ev(e,t){const n={};for(const r in e)t.includes(r)||(n[r]=e[r]);return n}function tv(e){const t=Cg(e.routes,e),n=e.parseQuery||Dg,r=e.stringifyQuery||ka,o=e.history,i=kr(),s=kr(),l=kr(),a=ct(Wt);let u=Wt;qt&&e.scrollBehavior&&"scrollRestoration"in history&&(history.scrollRestoration="manual");const d=zi.bind(null,C=>""+C),c=zi.bind(null,rg),f=zi.bind(null,dr);function p(C,Y){let G,ee;return jd(C)?(G=t.getRecordMatcher(C),ee=Y):ee=C,t.addRoute(ee,G)}function _(C){const Y=t.getRecordMatcher(C);Y&&t.removeRoute(Y)}function g(){return t.getRoutes().map(C=>C.record)}function v(C){return!!t.getRecordMatcher(C)}function y(C,Y){if(Y=ve({},Y||a.value),typeof C=="string"){const E=$i(n,C,Y.path),A=t.resolve({path:E.path},Y),M=o.createHref(E.fullPath);return ve(E,A,{params:f(A.params),hash:dr(E.hash),redirectedFrom:void 0,href:M})}let G;if(C.path!=null)G=ve({},C,{path:$i(n,C.path,Y.path).path});else{const E=ve({},C.params);for(const A in E)E[A]==null&&delete E[A];G=ve({},C,{params:c(E)}),Y.params=c(Y.params)}const ee=t.resolve(G,Y),pe=C.hash||"";ee.params=d(f(ee.params));const ge=sg(r,ve({},C,{hash:eg(pe),path:ee.path})),m=o.createHref(ge);return ve({fullPath:ge,hash:pe,query:r===ka?Vg(C.query):C.query||{}},ee,{redirectedFrom:void 0,href:m})}function w(C){return typeof C=="string"?$i(n,C,a.value.path):ve({},C)}function h(C,Y){if(u!==C)return fr(8,{from:Y,to:C})}function T(C){return k(C)}function R(C){return T(ve(w(C),{replace:!0}))}function j(C){const Y=C.matched[C.matched.length-1];if(Y&&Y.redirect){const{redirect:G}=Y;let ee=typeof G=="function"?G(C):G;return typeof ee=="string"&&(ee=ee.includes("?")||ee.includes("#")?ee=w(ee):{path:ee},ee.params={}),ve({query:C.query,hash:C.hash,params:ee.path!=null?{}:C.params},ee)}}function k(C,Y){const G=u=y(C),ee=a.value,pe=C.state,ge=C.force,m=C.replace===!0,E=j(G);if(E)return k(ve(w(E),{state:typeof E=="object"?ve({},pe,E.state):pe,force:ge,replace:m}),Y||G);const A=G;A.redirectedFrom=Y;let M;return!ge&&lg(r,ee,G)&&(M=fr(16,{to:A,from:ee}),Ie(ee,ee,!0,!1)),(M?Promise.resolve(M):$(A,ee)).catch(x=>Ut(x)?Ut(x,2)?x:Ee(x):K(x,A,ee)).then(x=>{if(x){if(Ut(x,2))return k(ve({replace:m},w(x.to),{state:typeof x.to=="object"?ve({},pe,x.to.state):pe,force:ge}),Y||A)}else x=O(A,ee,!0,m,pe);return I(A,ee,x),x})}function S(C,Y){const G=h(C,Y);return G?Promise.reject(G):Promise.resolve()}function V(C){const Y=lt.values().next().value;return Y&&typeof Y.runWithContext=="function"?Y.runWithContext(C):C()}function $(C,Y){let G;const[ee,pe,ge]=nv(C,Y);G=Bi(ee.reverse(),"beforeRouteLeave",C,Y);for(const E of ee)E.leaveGuards.forEach(A=>{G.push(fn(A,C,Y))});const m=S.bind(null,C,Y);return G.push(m),Ze(G).then(()=>{G=[];for(const E of i.list())G.push(fn(E,C,Y));return G.push(m),Ze(G)}).then(()=>{G=Bi(pe,"beforeRouteUpdate",C,Y);for(const E of pe)E.updateGuards.forEach(A=>{G.push(fn(A,C,Y))});return G.push(m),Ze(G)}).then(()=>{G=[];for(const E of ge)if(E.beforeEnter)if(vt(E.beforeEnter))for(const A of E.beforeEnter)G.push(fn(A,C,Y));else G.push(fn(E.beforeEnter,C,Y));return G.push(m),Ze(G)}).then(()=>(C.matched.forEach(E=>E.enterCallbacks={}),G=Bi(ge,"beforeRouteEnter",C,Y,V),G.push(m),Ze(G))).then(()=>{G=[];for(const E of s.list())G.push(fn(E,C,Y));return G.push(m),Ze(G)}).catch(E=>Ut(E,8)?E:Promise.reject(E))}function I(C,Y,G){l.list().forEach(ee=>V(()=>ee(C,Y,G)))}function O(C,Y,G,ee,pe){const ge=h(C,Y);if(ge)return ge;const m=Y===Wt,E=qt?history.state:{};G&&(ee||m?o.replace(C.fullPath,ve({scroll:m&&E&&E.scroll},pe)):o.push(C.fullPath,pe)),a.value=C,Ie(C,Y,G,m),Ee()}let P;function X(){P||(P=o.listen((C,Y,G)=>{if(!je.listening)return;const ee=y(C),pe=j(ee);if(pe){k(ve(pe,{replace:!0,force:!0}),ee).catch($r);return}u=ee;const ge=a.value;qt&&mg(ga(ge.fullPath,G.delta),fi()),$(ee,ge).catch(m=>Ut(m,12)?m:Ut(m,2)?(k(ve(w(m.to),{force:!0}),ee).then(E=>{Ut(E,20)&&!G.delta&&G.type===Zr.pop&&o.go(-1,!1)}).catch($r),Promise.reject()):(G.delta&&o.go(-G.delta,!1),K(m,ee,ge))).then(m=>{m=m||O(ee,ge,!1),m&&(G.delta&&!Ut(m,8)?o.go(-G.delta,!1):G.type===Zr.pop&&Ut(m,20)&&o.go(-1,!1)),I(ee,ge,m)}).catch($r)}))}let te=kr(),L=kr(),D;function K(C,Y,G){Ee(C);const ee=L.list();return ee.length?ee.forEach(pe=>pe(C,Y,G)):console.error(C),Promise.reject(C)}function re(){return D&&a.value!==Wt?Promise.resolve():new Promise((C,Y)=>{te.add([C,Y])})}function Ee(C){return D||(D=!C,X(),te.list().forEach(([Y,G])=>C?G(C):Y()),te.reset()),C}function Ie(C,Y,G,ee){const{scrollBehavior:pe}=e;if(!qt||!pe)return Promise.resolve();const ge=!G&&_g(ga(C.fullPath,0))||(ee||!G)&&history.state&&history.state.scroll||null;return Kn().then(()=>pe(C,Y,ge)).then(m=>m&&hg(m)).catch(m=>K(m,C,Y))}const _e=C=>o.go(C);let Fe;const lt=new Set,je={currentRoute:a,listening:!0,addRoute:p,removeRoute:_,clearRoutes:t.clearRoutes,hasRoute:v,getRoutes:g,resolve:y,options:e,push:T,replace:R,go:_e,back:()=>_e(-1),forward:()=>_e(1),beforeEach:i.add,beforeResolve:s.add,afterEach:l.add,onError:L.add,isReady:re,install(C){const Y=this;C.component("RouterLink",$g),C.component("RouterView",jg),C.config.globalProperties.$router=Y,Object.defineProperty(C.config.globalProperties,"$route",{enumerable:!0,get:()=>pn(a)}),qt&&!Fe&&a.value===Wt&&(Fe=!0,T(o.location).catch(pe=>{}));const G={};for(const pe in Wt)Object.defineProperty(G,pe,{get:()=>a.value[pe],enumerable:!0});C.provide(pi,Y),C.provide(Zs,Tc(G)),C.provide(hs,a);const ee=C.unmount;lt.add(C),C.unmount=function(){lt.delete(C),lt.size<1&&(u=Wt,P&&P(),P=null,a.value=Wt,Fe=!1,D=!1),ee()},qt&&Kg(C,Y,t)}};function Ze(C){return C.reduce((Y,G)=>Y.then(()=>V(G)),Promise.resolve())}return je}function nv(e,t){const n=[],r=[],o=[],i=Math.max(t.matched.length,e.matched.length);for(let s=0;sEn(u,l))?r.push(l):n.push(l));const a=e.matched[s];a&&(t.matched.find(u=>En(u,a))||o.push(a))}return[n,r,o]}function Ft(){return Ue(pi)}function rn(e){return Ue(Zs)}var Js=Symbol(""),Rt=()=>{const e=Ue(Js);if(!e)throw new Error("useClientData() is called without provider.");return e},rv=()=>Rt().pageComponent,Tn=()=>Rt().pageData,bt=()=>Rt().pageFrontmatter,ov=()=>Rt().pageHead,Qs=()=>Rt().pageLang,ef=()=>Rt().pageLayout,Ht=()=>Rt().routeLocale,iv=()=>Rt().routePath,tf=()=>Rt().routes,el=()=>Rt().siteData,tl=()=>Rt().siteLocaleData,sv=Symbol(""),_s=ct(D_),sr=ct(V_),nf=(e,t)=>{const n=S_(e,t);if(sr.value[n])return n;const r=encodeURI(n);if(sr.value[r])return r;const o=_s.value[n]||_s.value[r];return o||n},Jr=(e,t)=>{const{pathname:n,hashAndQueries:r}=Cd(e),o=nf(n,t),i=o+r;return sr.value[o]?{...sr.value[o],path:i,notFound:!1}:{...sr.value["/404.html"],path:i,notFound:!0}},lv=(e,t)=>{const{pathname:n,hashAndQueries:r}=Cd(e);return nf(n,t)+r},av=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}},hn=fe({name:"RouteLink",props:{to:{type:String,required:!0},active:Boolean,activeClass:{type:String,default:"route-link-active"}},slots:Object,setup(e,{slots:t}){const n=Ft(),r=rn(),o=N(()=>e.to.startsWith("#")||e.to.startsWith("?")?e.to:`/ecosystem/${lv(e.to,r.path).substring(1)}`);return()=>H("a",{class:["route-link",{[e.activeClass]:e.active}],href:o.value,onClick:(i={})=>{av(i)&&n.push(e.to).catch()}},t.default())}}),uv=fe({name:"AutoLink",props:{config:{type:Object,required:!0}},slots:Object,setup(e,{slots:t}){const n=Oc(e,"config"),r=rn(),o=el(),i=N(()=>uo(n.value.link)),s=N(()=>n.value.target||(i.value?"_blank":void 0)),l=N(()=>s.value==="_blank"),a=N(()=>!i.value&&!l.value),u=N(()=>n.value.rel||(l.value?"noopener noreferrer":null)),d=N(()=>n.value.ariaLabel??n.value.text),c=N(()=>{if(n.value.exact)return!1;const p=Object.keys(o.value.locales);return p.length?p.every(_=>_!==n.value.link):n.value.link!=="/"}),f=N(()=>a.value?n.value.activeMatch?(n.value.activeMatch instanceof RegExp?n.value.activeMatch:new RegExp(n.value.activeMatch,"u")).test(r.path):c.value?r.path.startsWith(n.value.link):r.path===n.value.link:!1);return()=>{const{before:p,after:_,default:g}=t,v=(g==null?void 0:g(n.value))??[p==null?void 0:p(n.value),n.value.text,_==null?void 0:_(n.value)];return a.value?H(hn,{class:"auto-link",to:n.value.link,active:f.value,"aria-label":d.value},()=>v):H("a",{class:"auto-link external-link",href:n.value.link,"aria-label":d.value,rel:u.value,target:s.value},v)}}}),nl=fe({name:"ClientOnly",setup(e,t){const n=ue(!1);return Ke(()=>{n.value=!0}),()=>{var r,o;return n.value?(o=(r=t.slots).default)==null?void 0:o.call(r):null}}}),hi=fe({name:"Content",props:{path:{type:String,required:!1,default:""}},setup(e){const t=rv(),n=N(()=>{if(!e.path)return t.value;const r=Jr(e.path);return Pn(async()=>r.loader().then(({comp:o})=>o))});return()=>H(n.value)}}),cv="Layout",dv="en-US",kn=zn({resolveLayouts:e=>e.reduce((t,n)=>({...t,...n.layouts}),{}),resolvePageHead:(e,t,n)=>{const r=at(t.description)?t.description:n.description,o=[...Array.isArray(t.head)?t.head:[],...n.head,["title",{},e],["meta",{name:"description",content:r}]];return L_(o)},resolvePageHeadTitle:(e,t)=>[e.title,t.title].filter(n=>!!n).join(" | "),resolvePageLang:(e,t)=>e.lang||t.lang||dv,resolvePageLayout:(e,t)=>{const n=at(e.frontmatter.layout)?e.frontmatter.layout:cv;if(!t[n])throw new Error(`[vuepress] Cannot resolve layout: ${n}`);return t[n]},resolveRouteLocale:(e,t)=>k_(e,decodeURI(t)),resolveSiteLocaleData:({base:e,locales:t,...n},r)=>{var o;return{...n,...t[r],head:[...((o=t[r])==null?void 0:o.head)??[],...n.head]}}}),Pt=(e={})=>e,mi=e=>co(e)?e:`/ecosystem/${Ys(e)}`;function fo(e){return lc()?(Up(e),!0):!1}const Fi=new WeakMap,fv=(...e)=>{var t;const n=e[0],r=(t=yn())==null?void 0:t.proxy;if(r==null&&!Xc())throw new Error("injectLocal must be called in setup");return r&&Fi.has(r)&&n in Fi.get(r)?Fi.get(r)[n]:Ue(...e)},po=typeof window<"u"&&typeof document<"u";typeof WorkerGlobalScope<"u"&&globalThis instanceof WorkerGlobalScope;const pv=Object.prototype.toString,hv=e=>pv.call(e)==="[object Object]",Ot=()=>{},gs=mv();function mv(){var e,t;return po&&((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 rl(e,t){function n(...r){return new Promise((o,i)=>{Promise.resolve(e(()=>t.apply(this,r),{fn:t,thisArg:this,args:r})).then(o).catch(i)})}return n}const rf=e=>e();function _v(e,t={}){let n,r,o=Ot;const i=l=>{clearTimeout(l),o(),o=Ot};return l=>{const a=we(e),u=we(t.maxWait);return n&&i(n),a<=0||u!==void 0&&u<=0?(r&&(i(r),r=null),Promise.resolve(l())):new Promise((d,c)=>{o=t.rejectOnCancel?c:d,u&&!r&&(r=setTimeout(()=>{n&&i(n),r=null,d(l())},u)),n=setTimeout(()=>{r&&i(r),r=null,d(l())},a)})}}function gv(...e){let t=0,n,r=!0,o=Ot,i,s,l,a,u;!Me(e[0])&&typeof e[0]=="object"?{delay:s,trailing:l=!0,leading:a=!0,rejectOnCancel:u=!1}=e[0]:[s,l=!0,a=!0,u=!1]=e;const d=()=>{n&&(clearTimeout(n),n=void 0,o(),o=Ot)};return f=>{const p=we(s),_=Date.now()-t,g=()=>i=f();return d(),p<=0?(t=Date.now(),g()):(_>p&&(a||!r)?(t=Date.now(),g()):l&&(i=new Promise((v,y)=>{o=u?y:v,n=setTimeout(()=>{t=Date.now(),r=!0,v(g()),d()},Math.max(0,p-_))})),!a&&!n&&(n=setTimeout(()=>r=!0,p)),r=!1,i)}}function vv(e=rf){const t=ue(!0);function n(){t.value=!1}function r(){t.value=!0}const o=(...i)=>{t.value&&e(...i)};return{isActive:Un(t),pause:n,resume:r,eventFilter:o}}function Ev(e){let t;function n(){return t||(t=e()),t}return n.reset=async()=>{const r=t;t=void 0,r&&await r},n}function La(e){return e.endsWith("rem")?Number.parseFloat(e)*16:Number.parseFloat(e)}function of(e){return yn()}function vs(e){return Array.isArray(e)?e:[e]}function bv(...e){if(e.length!==1)return Oc(...e);const t=e[0];return typeof t=="function"?Un(Sc(()=>({get:t,set:Ot}))):ue(t)}function sf(e,t=200,n={}){return rl(_v(t,n),e)}function yv(e,t=200,n=!1,r=!0,o=!1){return rl(gv(t,n,r,o),e)}function Tv(e,t,n={}){const{eventFilter:r=rf,...o}=n;return Be(e,rl(r,t),o)}function wv(e,t,n={}){const{eventFilter:r,...o}=n,{eventFilter:i,pause:s,resume:l,isActive:a}=vv(r);return{stop:Tv(e,t,{...o,eventFilter:i}),pause:s,resume:l,isActive:a}}function ho(e,t=!0,n){of()?Ke(e,n):t?e():Kn(e)}function Av(e,t){of()&&Wn(e,t)}function Sv(e,t,n={}){const{immediate:r=!0}=n,o=ue(!1);let i=null;function s(){i&&(clearTimeout(i),i=null)}function l(){o.value=!1,s()}function a(...u){s(),o.value=!0,i=setTimeout(()=>{o.value=!1,i=null,e(...u)},we(t))}return r&&(o.value=!0,po&&a()),fo(l),{isPending:Un(o),start:a,stop:l}}function lf(e=!1,t={}){const{truthyValue:n=!0,falsyValue:r=!1}=t,o=Me(e),i=ue(e);function s(l){if(arguments.length)return i.value=l,i.value;{const a=we(n);return i.value=i.value===a?we(r):a,i.value}}return o?s:[i,s]}function pr(e,t,n){return Be(e,t,{...n,immediate:!0})}const Et=po?window:void 0,kv=po?window.document:void 0,af=po?window.navigator:void 0;function St(e){var t;const n=we(e);return(t=n==null?void 0:n.$el)!=null?t:n}function He(...e){let t,n,r,o;if(typeof e[0]=="string"||Array.isArray(e[0])?([n,r,o]=e,t=Et):[t,n,r,o]=e,!t)return Ot;n=vs(n),r=vs(r);const i=[],s=()=>{i.forEach(d=>d()),i.length=0},l=(d,c,f,p)=>(d.addEventListener(c,f,p),()=>d.removeEventListener(c,f,p)),a=Be(()=>[St(t),we(o)],([d,c])=>{if(s(),!d)return;const f=hv(c)?{...c}:c;i.push(...n.flatMap(p=>r.map(_=>l(d,p,_,f))))},{immediate:!0,flush:"post"}),u=()=>{a(),s()};return fo(u),u}let Ra=!1;function Ov(e,t,n={}){const{window:r=Et,ignore:o=[],capture:i=!0,detectIframe:s=!1}=n;if(!r)return Ot;gs&&!Ra&&(Ra=!0,Array.from(r.document.body.children).forEach(g=>g.addEventListener("click",Ot)),r.document.documentElement.addEventListener("click",Ot));let l=!0;const a=g=>we(o).some(v=>{if(typeof v=="string")return Array.from(r.document.querySelectorAll(v)).some(y=>y===g.target||g.composedPath().includes(y));{const y=St(v);return y&&(g.target===y||g.composedPath().includes(y))}});function u(g){const v=we(g);return v&&v.$.subTree.shapeFlag===16}function d(g,v){const y=we(g),w=y.$.subTree&&y.$.subTree.children;return w==null||!Array.isArray(w)?!1:w.some(h=>h.el===v.target||v.composedPath().includes(h.el))}const c=g=>{const v=St(e);if(g.target!=null&&!(!(v instanceof Element)&&u(e)&&d(e,g))&&!(!v||v===g.target||g.composedPath().includes(v))){if(g.detail===0&&(l=!a(g)),!l){l=!0;return}t(g)}};let f=!1;const p=[He(r,"click",g=>{f||(f=!0,setTimeout(()=>{f=!1},0),c(g))},{passive:!0,capture:i}),He(r,"pointerdown",g=>{const v=St(e);l=!a(g)&&!!(v&&!g.composedPath().includes(v))},{passive:!0}),s&&He(r,"blur",g=>{setTimeout(()=>{var v;const y=St(e);((v=r.document.activeElement)==null?void 0:v.tagName)==="IFRAME"&&!(y!=null&&y.contains(r.document.activeElement))&&t(g)},0)},{passive:!0})].filter(Boolean);return()=>p.forEach(g=>g())}function Pv(){const e=ue(!1),t=yn();return t&&Ke(()=>{e.value=!0},t),e}function _i(e){const t=Pv();return N(()=>(t.value,!!e()))}const Cv=Symbol("vueuse-ssr-width");function xv(){const e=Xc()?fv(Cv,null):null;return typeof e=="number"?e:void 0}function ol(e,t={}){const{window:n=Et,ssrWidth:r=xv()}=t,o=_i(()=>n&&"matchMedia"in n&&typeof n.matchMedia=="function"),i=ue(typeof r=="number");let s;const l=ue(!1),a=c=>{l.value=c.matches},u=()=>{s&&("removeEventListener"in s?s.removeEventListener("change",a):s.removeListener(a))},d=ud(()=>{if(i.value){i.value=!o.value;const c=we(e).split(",");l.value=c.some(f=>{const p=f.includes("not all"),_=f.match(/\(\s*min-width:\s*(-?\d+(?:\.\d*)?[a-z]+\s*)\)/),g=f.match(/\(\s*max-width:\s*(-?\d+(?:\.\d*)?[a-z]+\s*)\)/);let v=!!(_||g);return _&&v&&(v=r>=La(_[1])),g&&v&&(v=r<=La(g[1])),p?!v:v});return}o.value&&(u(),s=n.matchMedia(we(e)),"addEventListener"in s?s.addEventListener("change",a):s.addListener(a),l.value=s.matches)});return fo(()=>{d(),u(),s=void 0}),N(()=>l.value)}function Da(e,t={}){const{controls:n=!1,navigator:r=af}=t,o=_i(()=>r&&"permissions"in r),i=ct(),s=typeof e=="string"?{name:e}:e,l=ct(),a=()=>{var d,c;l.value=(c=(d=i.value)==null?void 0:d.state)!=null?c:"prompt"};He(i,"change",a,{passive:!0});const u=Ev(async()=>{if(o.value){if(!i.value)try{i.value=await r.permissions.query(s)}catch{i.value=void 0}finally{a()}if(n)return he(i.value)}});return u(),n?{state:l,isSupported:o,query:u}:l}function Iv(e={}){const{navigator:t=af,read:n=!1,source:r,copiedDuring:o=1500,legacy:i=!1}=e,s=_i(()=>t&&"clipboard"in t),l=Da("clipboard-read"),a=Da("clipboard-write"),u=N(()=>s.value||i),d=ue(""),c=ue(!1),f=Sv(()=>c.value=!1,o,{immediate:!1});function p(){s.value&&y(l.value)?t.clipboard.readText().then(w=>{d.value=w}):d.value=v()}u.value&&n&&He(["copy","cut"],p,{passive:!0});async function _(w=we(r)){u.value&&w!=null&&(s.value&&y(a.value)?await t.clipboard.writeText(w):g(w),d.value=w,c.value=!0,f.start())}function g(w){const h=document.createElement("textarea");h.value=w??"",h.style.position="absolute",h.style.opacity="0",document.body.appendChild(h),h.select(),document.execCommand("copy"),h.remove()}function v(){var w,h,T;return(T=(h=(w=document==null?void 0:document.getSelection)==null?void 0:w.call(document))==null?void 0:h.toString())!=null?T:""}function y(w){return w==="granted"||w==="prompt"}return{isSupported:u,text:d,copied:c,copy:_}}const Ao=typeof globalThis<"u"?globalThis:typeof window<"u"?window:typeof global<"u"?global:typeof self<"u"?self:{},So="__vueuse_ssr_handlers__",Lv=Rv();function Rv(){return So in Ao||(Ao[So]=Ao[So]||{}),Ao[So]}function Dv(e,t){return Lv[e]||t}function Vv(e){return ol("(prefers-color-scheme: dark)",e)}function Nv(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 Mv={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()}},Va="vueuse-storage";function mo(e,t,n,r={}){var o;const{flush:i="pre",deep:s=!0,listenToStorageChanges:l=!0,writeDefaults:a=!0,mergeDefaults:u=!1,shallow:d,window:c=Et,eventFilter:f,onError:p=I=>{console.error(I)},initOnMounted:_}=r,g=(d?ct:ue)(typeof t=="function"?t():t),v=N(()=>we(e));if(!n)try{n=Dv("getDefaultStorage",()=>{var I;return(I=Et)==null?void 0:I.localStorage})()}catch(I){p(I)}if(!n)return g;const y=we(t),w=Nv(y),h=(o=r.serializer)!=null?o:Mv[w],{pause:T,resume:R}=wv(g,()=>k(g.value),{flush:i,deep:s,eventFilter:f});Be(v,()=>V(),{flush:i}),c&&l&&ho(()=>{n instanceof Storage?He(c,"storage",V,{passive:!0}):He(c,Va,$),_&&V()}),_||V();function j(I,O){if(c){const P={key:v.value,oldValue:I,newValue:O,storageArea:n};c.dispatchEvent(n instanceof Storage?new StorageEvent("storage",P):new CustomEvent(Va,{detail:P}))}}function k(I){try{const O=n.getItem(v.value);if(I==null)j(O,null),n.removeItem(v.value);else{const P=h.write(I);O!==P&&(n.setItem(v.value,P),j(O,P))}}catch(O){p(O)}}function S(I){const O=I?I.newValue:n.getItem(v.value);if(O==null)return a&&y!=null&&n.setItem(v.value,h.write(y)),y;if(!I&&u){const P=h.read(O);return typeof u=="function"?u(P,y):w==="object"&&!Array.isArray(P)?{...y,...P}:P}else return typeof O!="string"?O:h.read(O)}function V(I){if(!(I&&I.storageArea!==n)){if(I&&I.key==null){g.value=y;return}if(!(I&&I.key!==v.value)){T();try{(I==null?void 0:I.newValue)!==h.write(g.value)&&(g.value=S(I))}catch(O){p(O)}finally{I?Kn(R):R()}}}}function $(I){V(I.detail)}return g}function zv(e,t,n={}){const{window:r=Et,...o}=n;let i;const s=_i(()=>r&&"ResizeObserver"in r),l=()=>{i&&(i.disconnect(),i=void 0)},a=N(()=>{const c=we(e);return Array.isArray(c)?c.map(f=>St(f)):[St(c)]}),u=Be(a,c=>{if(l(),s.value&&r){i=new ResizeObserver(t);for(const f of c)f&&i.observe(f,o)}},{immediate:!0,flush:"post"}),d=()=>{l(),u()};return fo(d),{isSupported:s,stop:d}}function $v(e,t={width:0,height:0},n={}){const{window:r=Et,box:o="content-box"}=n,i=N(()=>{var c,f;return(f=(c=St(e))==null?void 0:c.namespaceURI)==null?void 0:f.includes("svg")}),s=ue(t.width),l=ue(t.height),{stop:a}=zv(e,([c])=>{const f=o==="border-box"?c.borderBoxSize:o==="content-box"?c.contentBoxSize:c.devicePixelContentBoxSize;if(r&&i.value){const p=St(e);if(p){const _=p.getBoundingClientRect();s.value=_.width,l.value=_.height}}else if(f){const p=vs(f);s.value=p.reduce((_,{inlineSize:g})=>_+g,0),l.value=p.reduce((_,{blockSize:g})=>_+g,0)}else s.value=c.contentRect.width,l.value=c.contentRect.height},n);ho(()=>{const c=St(e);c&&(s.value="offsetWidth"in c?c.offsetWidth:t.width,l.value="offsetHeight"in c?c.offsetHeight:t.height)});const u=Be(()=>St(e),c=>{s.value=c?t.width:0,l.value=c?t.height:0});function d(){a(),u()}return{width:s,height:l,stop:d}}function Hi(e){return typeof Window<"u"&&e instanceof Window?e.document.documentElement:typeof Document<"u"&&e instanceof Document?e.documentElement:e}const Na=1;function Bv(e,t={}){const{throttle:n=0,idle:r=200,onStop:o=Ot,onScroll:i=Ot,offset:s={left:0,right:0,top:0,bottom:0},eventListenerOptions:l={capture:!1,passive:!0},behavior:a="auto",window:u=Et,onError:d=k=>{console.error(k)}}=t,c=ue(0),f=ue(0),p=N({get(){return c.value},set(k){g(k,void 0)}}),_=N({get(){return f.value},set(k){g(void 0,k)}});function g(k,S){var V,$,I,O;if(!u)return;const P=we(e);if(!P)return;(I=P instanceof Document?u.document.body:P)==null||I.scrollTo({top:(V=we(S))!=null?V:_.value,left:($=we(k))!=null?$:p.value,behavior:we(a)});const X=((O=P==null?void 0:P.document)==null?void 0:O.documentElement)||(P==null?void 0:P.documentElement)||P;p!=null&&(c.value=X.scrollLeft),_!=null&&(f.value=X.scrollTop)}const v=ue(!1),y=zn({left:!0,right:!1,top:!0,bottom:!1}),w=zn({left:!1,right:!1,top:!1,bottom:!1}),h=k=>{v.value&&(v.value=!1,w.left=!1,w.right=!1,w.top=!1,w.bottom=!1,o(k))},T=sf(h,n+r),R=k=>{var S;if(!u)return;const V=((S=k==null?void 0:k.document)==null?void 0:S.documentElement)||(k==null?void 0:k.documentElement)||St(k),{display:$,flexDirection:I,direction:O}=getComputedStyle(V),P=O==="rtl"?-1:1,X=V.scrollLeft;w.left=Xc.value;const te=X*P<=(s.left||0),L=X*P+V.clientWidth>=V.scrollWidth-(s.right||0)-Na;$==="flex"&&I==="row-reverse"?(y.left=L,y.right=te):(y.left=te,y.right=L),c.value=X;let D=V.scrollTop;k===u.document&&!D&&(D=u.document.body.scrollTop),w.top=Df.value;const K=D<=(s.top||0),re=D+V.clientHeight>=V.scrollHeight-(s.bottom||0)-Na;$==="flex"&&I==="column-reverse"?(y.top=re,y.bottom=K):(y.top=K,y.bottom=re),f.value=D},j=k=>{var S;if(!u)return;const V=(S=k.target.documentElement)!=null?S:k.target;R(V),v.value=!0,T(k),i(k)};return He(e,"scroll",n?yv(j,n,!0,!1):j,l),ho(()=>{try{const k=we(e);if(!k)return;R(k)}catch(k){d(k)}}),He(e,"scrollend",h,l),{x:p,y:_,isScrolling:v,arrivedState:y,directions:w,measure(){const k=we(e);u&&k&&R(k)}}}function Fv(e,t,n={}){const{window:r=Et}=n;return mo(e,t,r==null?void 0:r.localStorage,n)}function Hv(e={}){const{window:t=Et}=e;if(!t)return ue(["en"]);const n=t.navigator,r=ue(n.languages);return He(t,"languagechange",()=>{r.value=n.languages},{passive:!0}),r}function jv(e,t=Ot,n={}){const{immediate:r=!0,manual:o=!1,type:i="text/javascript",async:s=!0,crossOrigin:l,referrerPolicy:a,noModule:u,defer:d,document:c=kv,attrs:f={}}=n,p=ue(null);let _=null;const g=w=>new Promise((h,T)=>{const R=S=>(p.value=S,h(S),S);if(!c){h(!1);return}let j=!1,k=c.querySelector(`script[src="${we(e)}"]`);k?k.hasAttribute("data-loaded")&&R(k):(k=c.createElement("script"),k.type=i,k.async=s,k.src=we(e),d&&(k.defer=d),l&&(k.crossOrigin=l),u&&(k.noModule=u),a&&(k.referrerPolicy=a),Object.entries(f).forEach(([S,V])=>k==null?void 0:k.setAttribute(S,V)),j=!0),k.addEventListener("error",S=>T(S)),k.addEventListener("abort",S=>T(S)),k.addEventListener("load",()=>{k.setAttribute("data-loaded","true"),t(k),R(k)}),j&&(k=c.head.appendChild(k)),w||R(k)}),v=(w=!0)=>(_||(_=g(w)),_),y=()=>{if(!c)return;_=null,p.value&&(p.value=null);const w=c.querySelector(`script[src="${we(e)}"]`);w&&c.head.removeChild(w)};return r&&!o&&ho(v),o||Av(y),{scriptTag:p,load:v,unload:y}}function uf(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 ji=new WeakMap;function Kv(e,t=!1){const n=ue(t);let r=null,o="";Be(bv(e),l=>{const a=Hi(we(l));if(a){const u=a;if(ji.get(u)||ji.set(u,u.style.overflow),u.style.overflow!=="hidden"&&(o=u.style.overflow),u.style.overflow==="hidden")return n.value=!0;if(n.value)return u.style.overflow="hidden"}},{immediate:!0});const i=()=>{const l=Hi(we(e));!l||n.value||(gs&&(r=He(l,"touchmove",a=>{Uv(a)},{passive:!1})),l.style.overflow="hidden",n.value=!0)},s=()=>{const l=Hi(we(e));!l||!n.value||(gs&&(r==null||r()),l.style.overflow=o,ji.delete(l),n.value=!1)};return fo(s),N({get(){return n.value},set(l){l?i():s()}})}function Wv(e,t,n={}){const{window:r=Et}=n;return mo(e,t,r==null?void 0:r.sessionStorage,n)}function Gv(e={}){const{window:t=Et,...n}=e;return Bv(t,n)}function qv(e={}){const{window:t=Et,initialWidth:n=Number.POSITIVE_INFINITY,initialHeight:r=Number.POSITIVE_INFINITY,listenOrientation:o=!0,includeScrollbar:i=!0,type:s="inner"}=e,l=ue(n),a=ue(r),u=()=>{if(t)if(s==="outer")l.value=t.outerWidth,a.value=t.outerHeight;else if(s==="visual"&&t.visualViewport){const{width:c,height:f,scale:p}=t.visualViewport;l.value=Math.round(c*p),a.value=Math.round(f*p)}else i?(l.value=t.innerWidth,a.value=t.innerHeight):(l.value=t.document.documentElement.clientWidth,a.value=t.document.documentElement.clientHeight)};u(),ho(u);const d={passive:!0};if(He("resize",u,d),t&&s==="visual"&&t.visualViewport&&He(t.visualViewport,"resize",u,d),o){const c=ol("(orientation: portrait)");Be(c,()=>u())}return{width:l,height:a}}const Ma=async(e,t)=>{const{path:n,query:r}=e.currentRoute.value,{scrollBehavior:o}=e.options;e.options.scrollBehavior=void 0,await e.replace({path:n,query:r,hash:t}),e.options.scrollBehavior=o},Yv=({headerLinkSelector:e,headerAnchorSelector:t,delay:n,offset:r=5})=>{const o=Ft();He("scroll",sf(()=>{var _,g;const s=Math.max(window.scrollY,document.documentElement.scrollTop,document.body.scrollTop);if(Math.abs(s-0)c.some(y=>y.hash===v.hash));for(let v=0;v=(((_=y.parentElement)==null?void 0:_.offsetTop)??0)-r,T=!w||s<(((g=w.parentElement)==null?void 0:g.offsetTop)??0)-r;if(!(h&&T))continue;const j=decodeURIComponent(o.currentRoute.value.hash),k=decodeURIComponent(y.hash);if(j===k)return;if(d){for(let S=v+1;S{const o=H("svg",{xmlns:"http://www.w3.org/2000/svg",width:e,height:e,preserveAspectRatio:"xMidYMid",viewBox:"25 25 50 50"},[H("animateTransform",{attributeName:"transform",type:"rotate",dur:"2s",keyTimes:"0;1",repeatCount:"indefinite",values:"0;360"}),H("circle",{cx:"50",cy:"50",r:"20",fill:"none",stroke:"currentColor","stroke-width":t,"stroke-linecap":"round"},[H("animate",{attributeName:"stroke-dasharray",dur:"1.5s",keyTimes:"0;0.5;1",repeatCount:"indefinite",values:"1,200;90,200;1,200"}),H("animate",{attributeName:"stroke-dashoffset",dur:"1.5s",keyTimes:"0;0.5;1",repeatCount:"indefinite",values:"0;-35px;-125px"})])]);return n?H("div",{class:"loading-icon-wrapper",style:`display:flex;align-items:center;justify-content:center;height:${r}px`},o):o};il.displayName="LoadingIcon";var _t=Uint8Array,Jn=Uint16Array,n0=Int32Array,cf=new _t([0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0,0,0,0]),df=new _t([0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13,0,0]),r0=new _t([16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15]),ff=function(e,t){for(var n=new Jn(31),r=0;r<31;++r)n[r]=t+=1<>1|(Le&21845)<<1;sn=(sn&52428)>>2|(sn&13107)<<2,sn=(sn&61680)>>4|(sn&3855)<<4,Es[Le]=((sn&65280)>>8|(sn&255)<<8)>>1}var Fr=function(e,t,n){for(var r=e.length,o=0,i=new Jn(t);o>a]=u}else for(l=new Jn(r),o=0;o>15-e[o]);return l},_o=new _t(288);for(var Le=0;Le<144;++Le)_o[Le]=8;for(var Le=144;Le<256;++Le)_o[Le]=9;for(var Le=256;Le<280;++Le)_o[Le]=7;for(var Le=280;Le<288;++Le)_o[Le]=8;var mf=new _t(32);for(var Le=0;Le<32;++Le)mf[Le]=5;var l0=Fr(_o,9,1),a0=Fr(mf,5,1),Ui=function(e){for(var t=e[0],n=1;nt&&(t=e[n]);return t},xt=function(e,t,n){var r=t/8|0;return(e[r]|e[r+1]<<8)>>(t&7)&n},Ki=function(e,t){var n=t/8|0;return(e[n]|e[n+1]<<8|e[n+2]<<16)>>(t&7)},u0=function(e){return(e+7)/8|0},_f=function(e,t,n){return(t==null||t<0)&&(t=0),(n==null||n>e.length)&&(n=e.length),new _t(e.subarray(t,n))},c0=["unexpected EOF","invalid block type","invalid length/literal","invalid distance","stream finished","no stream handler",,"no callback","invalid UTF-8 data","extra field too long","date not in range 1980-2099","filename too long","stream finishing","invalid zip data"],wt=function(e,t,n){var r=new Error(t||c0[e]);if(r.code=e,Error.captureStackTrace&&Error.captureStackTrace(r,wt),!n)throw r;return r},d0=function(e,t,n,r){var o=e.length,i=0;if(!o||t.f&&!t.l)return n||new _t(0);var s=!n,l=s||t.i!=2,a=t.i;s&&(n=new _t(o*3));var u=function(pe){var ge=n.length;if(pe>ge){var m=new _t(Math.max(ge*2,pe));m.set(n),n=m}},d=t.f||0,c=t.p||0,f=t.b||0,p=t.l,_=t.d,g=t.m,v=t.n,y=o*8;do{if(!p){d=xt(e,c,1);var w=xt(e,c+1,3);if(c+=3,w)if(w==1)p=l0,_=a0,g=9,v=5;else if(w==2){var j=xt(e,c,31)+257,k=xt(e,c+10,15)+4,S=j+xt(e,c+5,31)+1;c+=14;for(var V=new _t(S),$=new _t(19),I=0;I>4;if(h<16)V[I++]=h;else{var L=0,D=0;for(h==16?(D=3+xt(e,c,3),c+=2,L=V[I-1]):h==17?(D=3+xt(e,c,7),c+=3):h==18&&(D=11+xt(e,c,127),c+=7);D--;)V[I++]=L}}var K=V.subarray(0,j),re=V.subarray(j);g=Ui(K),v=Ui(re),p=Fr(K,g,1),_=Fr(re,v,1)}else wt(1);else{var h=u0(c)+4,T=e[h-4]|e[h-3]<<8,R=h+T;if(R>o){a&&wt(0);break}l&&u(f+T),n.set(e.subarray(h,R),f),t.b=f+=T,t.p=c=R*8,t.f=d;continue}if(c>y){a&&wt(0);break}}l&&u(f+131072);for(var Ee=(1<>4;if(c+=L&15,c>y){a&&wt(0);break}if(L||wt(2),Fe<256)n[f++]=Fe;else if(Fe==256){_e=c,p=null;break}else{var lt=Fe-254;if(Fe>264){var I=Fe-257,je=cf[I];lt=xt(e,c,(1<>4;Ze||wt(3),c+=Ze&15;var re=s0[C];if(C>3){var je=df[C];re+=Ki(e,c)&(1<y){a&&wt(0);break}l&&u(f+131072);var Y=f+lt;if(f>4>7||(e[0]<<8|e[1])%31)&&wt(6,"invalid zlib data"),(e[1]>>5&1)==1&&wt(6,"invalid zlib data: "+(e[1]&32?"need":"unexpected")+" dictionary"),(e[1]>>3&4)+2};function h0(e,t){return d0(e.subarray(p0(e),-4),{i:2},t,t)}var bs=typeof TextDecoder<"u"&&new TextDecoder,m0=0;try{bs.decode(f0,{stream:!0}),m0=1}catch{}var _0=function(e){for(var t="",n=0;;){var r=e[n++],o=(r>127)+(r>223)+(r>239);if(n+o>e.length)return{s:t,r:_f(e,n-1)};o?o==3?(r=((r&15)<<18|(e[n++]&63)<<12|(e[n++]&63)<<6|e[n++]&63)-65536,t+=String.fromCharCode(55296|r>>10,56320|r&1023)):o&1?t+=String.fromCharCode((r&31)<<6|e[n++]&63):t+=String.fromCharCode((r&15)<<12|(e[n++]&63)<<6|e[n++]&63):t+=String.fromCharCode(r)}};function g0(e,t){{for(var n=new _t(e.length),r=0;r{const t=atob(e);return v0(h0(g0(t)))},b0=(e,t=2)=>{if(t===!1)return[];const[n,r]=typeof t=="number"?[t,t]:t==="deep"?[2,6]:t,o=e.filter(s=>s.level>=n&&s.level<=r),i=[];e:for(let s=0;s=0;a--){const u=o[a];if(u.level{let n;if(t.length){const r=e.cloneNode(!0);r.querySelectorAll(t.join(",")).forEach(o=>{o.remove()}),n=r.textContent||""}else n=e.textContent||"";return n.trim()},T0=({selector:e=[...new Array(6)].map((r,o)=>`[vp-content] h${o+1}`).join(","),levels:t=2,ignore:n=[]}={})=>{const r=Array.from(document.querySelectorAll(e)).filter(o=>o.id&&o.hasChildNodes()).map(o=>{const i=Number(o.tagName[1]);return{element:o,title:y0(o,n),link:`#${o.id}`,slug:o.id,level:i}});return b0(r,t)},gi=(e,t)=>{var r;const n=(r=(t==null?void 0:t._instance)??yn())==null?void 0:r.appContext.components;return n?e in n||ut(e)in n||io(ut(e))in n:!1},sl=e=>new Promise(t=>{setTimeout(t,e)}),ll=e=>{const t=Ht();return N(()=>e[t.value]??{})},w0=()=>{const e=tf();return N(()=>Object.keys(e.value))},A0=e=>typeof e<"u",Wi=e=>typeof e=="number",{isArray:S0}=Array,ei=(e,t)=>at(e)&&e.startsWith(t),k0=(e,t)=>at(e)&&e.endsWith(t),{entries:al}=Object,{keys:ul}=Object,Hr=(e,...t)=>{if(t.length===0)return e;const n=t.shift();return n&&al(n).forEach(([r,o])=>{r==="__proto__"||r==="constructor"||(Mn(e[r])&&Mn(o)?Hr(e[r],o):S0(o)?e[r]=[...o]:Mn(o)?e[r]={...o}:e[r]=n[r])}),Hr(e,...t)},cl=e=>ei(e,"/")&&e[1]!=="/",gf=e=>!w_(e)&&!uo(e);var O0={"/":{backToTop:"Back to top"},"/zh/":{backToTop:"返回顶部"}};const P0=fe({name:"BackToTop",setup(){const e=bt(),t=ll(O0),n=ct(),{height:r}=$v(n),{height:o}=qv(),{y:i}=Gv(),s=N(()=>e.value.backToTop!==!1&&i.value>100),l=N(()=>i.value/(r.value-o.value)*100);return Ke(()=>{n.value=document.body}),()=>H(qs,{name:"back-to-top"},()=>s.value?H("button",{type:"button",class:"vp-back-to-top-button","aria-label":t.value.backToTop,onClick:()=>{window.scrollTo({top:0,behavior:"smooth"})}},[H("span",{class:"vp-scroll-progress",role:"progressbar","aria-labelledby":"loadinglabel","aria-valuenow":l.value},H("svg",H("circle",{cx:"26",cy:"26",r:"24",fill:"none",stroke:"currentColor","stroke-width":"4","stroke-dasharray":`${Math.PI*l.value*.48} ${Math.PI*(100-l.value)*.48}`}))),H("div",{class:"back-to-top-icon"})]):null)}}),C0=Pt({rootComponents:[P0]}),x0=Object.freeze(Object.defineProperty({__proto__:null,default:C0},Symbol.toStringTag,{value:"Module"})),I0=/language-(shellscript|shell|bash|sh|zsh)/,L0=({delay:e=500,duration:t=2e3,locales:n,selector:r,showInMobile:o,ignoreSelector:i=[],transform:s})=>{const l=ol("(max-width: 419px)"),a=N(()=>!l.value||o),u=ll(n),d=Tn(),c=v=>{var w;if(v.hasAttribute("copy-code"))return;const y=document.createElement("button");y.type="button",y.classList.add("vp-copy-code-button"),y.setAttribute("aria-label",u.value.copy),y.setAttribute("data-copied",u.value.copied),(w=v.parentElement)==null||w.insertBefore(y,v),v.setAttribute("copy-code","")};pr(()=>[d.value.path,a.value],async()=>{document.body.classList.toggle("no-copy-code",!a.value),a.value&&(await Kn(),await sl(e),document.querySelectorAll(r.join(",")).forEach(c))});const{copy:p}=Iv({legacy:!0}),_=new WeakMap,g=async(v,y,w)=>{const h=y.cloneNode(!0);i.length&&h.querySelectorAll(i.join(",")).forEach(j=>{j.remove()}),s&&s(h);let T=h.textContent||"";if(I0.test(v.className)&&(T=T.replace(/^ *(\$|>) /gm,"")),await p(T),t<=0)return;w.classList.add("copied"),clearTimeout(_.get(w));const R=setTimeout(()=>{w.classList.remove("copied"),w.blur(),_.delete(w)},t);_.set(w,R)};He("click",v=>{const y=v.target;if(a.value&&y.matches('div[class*="language-"] > button.vp-copy-code-button')){const w=y.parentElement,h=y.nextElementSibling;if(!w||!h)return;g(w,h,y)}})};var R0=[],D0={"/":{copy:"Copy code",copied:"Copied"},"/zh/":{copy:"复制代码",copied:"已复制"}},V0=['[vp-content] div[class*="language-"] pre'];const N0=Pt({setup:()=>{L0({selector:V0,ignoreSelector:R0,locales:D0,duration:2e3,delay:500,showInMobile:!1})}}),M0=Object.freeze(Object.defineProperty({__proto__:null,default:N0},Symbol.toStringTag,{value:"Module"})),z0=Pt({setup(){He("beforeprint",()=>{document.querySelectorAll("details").forEach(e=>{e.open=!0})})}}),$0=Object.freeze(Object.defineProperty({__proto__:null,default:z0},Symbol.toStringTag,{value:"Module"}));/*! medium-zoom 1.1.0 | MIT License | https://github.com/francoischalifour/medium-zoom */var Cn=Object.assign||function(e){for(var t=1;t1&&arguments[1]!==void 0?arguments[1]:{},r=window.Promise||function(O){function P(){}O(P,P)},o=function(O){var P=O.target;if(P===V){_();return}h.indexOf(P)!==-1&&g({target:P})},i=function(){if(!(R||!S.original)){var O=window.pageYOffset||document.documentElement.scrollTop||document.body.scrollTop||0;Math.abs(j-O)>k.scrollOffset&&setTimeout(_,150)}},s=function(O){var P=O.key||O.keyCode;(P==="Escape"||P==="Esc"||P===27)&&_()},l=function(){var O=arguments.length>0&&arguments[0]!==void 0?arguments[0]:{},P=O;if(O.background&&(V.style.background=O.background),O.container&&O.container instanceof Object&&(P.container=Cn({},k.container,O.container)),O.template){var X=Vo(O.template)?O.template:document.querySelector(O.template);P.template=X}return k=Cn({},k,P),h.forEach(function(te){te.dispatchEvent(Xn("medium-zoom:update",{detail:{zoom:$}}))}),$},a=function(){var O=arguments.length>0&&arguments[0]!==void 0?arguments[0]:{};return e(Cn({},k,O))},u=function(){for(var O=arguments.length,P=Array(O),X=0;X0?P.reduce(function(L,D){return[].concat(L,$a(D))},[]):h;return te.forEach(function(L){L.classList.remove("medium-zoom-image"),L.dispatchEvent(Xn("medium-zoom:detach",{detail:{zoom:$}}))}),h=h.filter(function(L){return te.indexOf(L)===-1}),$},c=function(O,P){var X=arguments.length>2&&arguments[2]!==void 0?arguments[2]:{};return h.forEach(function(te){te.addEventListener("medium-zoom:"+O,P,X)}),T.push({type:"medium-zoom:"+O,listener:P,options:X}),$},f=function(O,P){var X=arguments.length>2&&arguments[2]!==void 0?arguments[2]:{};return h.forEach(function(te){te.removeEventListener("medium-zoom:"+O,P,X)}),T=T.filter(function(te){return!(te.type==="medium-zoom:"+O&&te.listener.toString()===P.toString())}),$},p=function(){var O=arguments.length>0&&arguments[0]!==void 0?arguments[0]:{},P=O.target,X=function(){var L={width:document.documentElement.clientWidth,height:document.documentElement.clientHeight,left:0,top:0,right:0,bottom:0},D=void 0,K=void 0;if(k.container)if(k.container instanceof Object)L=Cn({},L,k.container),D=L.width-L.left-L.right-k.margin*2,K=L.height-L.top-L.bottom-k.margin*2;else{var re=Vo(k.container)?k.container:document.querySelector(k.container),Ee=re.getBoundingClientRect(),Ie=Ee.width,_e=Ee.height,Fe=Ee.left,lt=Ee.top;L=Cn({},L,{width:Ie,height:_e,left:Fe,top:lt})}D=D||L.width-k.margin*2,K=K||L.height-k.margin*2;var je=S.zoomedHd||S.original,Ze=za(je)?D:je.naturalWidth||D,C=za(je)?K:je.naturalHeight||K,Y=je.getBoundingClientRect(),G=Y.top,ee=Y.left,pe=Y.width,ge=Y.height,m=Math.min(Math.max(pe,Ze),D)/pe,E=Math.min(Math.max(ge,C),K)/ge,A=Math.min(m,E),M=(-ee+(D-pe)/2+k.margin+L.left)/A,x=(-G+(K-ge)/2+k.margin+L.top)/A,B="scale("+A+") translate3d("+M+"px, "+x+"px, 0)";S.zoomed.style.transform=B,S.zoomedHd&&(S.zoomedHd.style.transform=B)};return new r(function(te){if(P&&h.indexOf(P)===-1){te($);return}var L=function Ie(){R=!1,S.zoomed.removeEventListener("transitionend",Ie),S.original.dispatchEvent(Xn("medium-zoom:opened",{detail:{zoom:$}})),te($)};if(S.zoomed){te($);return}if(P)S.original=P;else if(h.length>0){var D=h;S.original=D[0]}else{te($);return}if(S.original.dispatchEvent(Xn("medium-zoom:open",{detail:{zoom:$}})),j=window.pageYOffset||document.documentElement.scrollTop||document.body.scrollTop||0,R=!0,S.zoomed=H0(S.original),document.body.appendChild(V),k.template){var K=Vo(k.template)?k.template:document.querySelector(k.template);S.template=document.createElement("div"),S.template.appendChild(K.content.cloneNode(!0)),document.body.appendChild(S.template)}if(S.original.parentElement&&S.original.parentElement.tagName==="PICTURE"&&S.original.currentSrc&&(S.zoomed.src=S.original.currentSrc),document.body.appendChild(S.zoomed),window.requestAnimationFrame(function(){document.body.classList.add("medium-zoom--opened")}),S.original.classList.add("medium-zoom-image--hidden"),S.zoomed.classList.add("medium-zoom-image--opened"),S.zoomed.addEventListener("click",_),S.zoomed.addEventListener("transitionend",L),S.original.getAttribute("data-zoom-src")){S.zoomedHd=S.zoomed.cloneNode(),S.zoomedHd.removeAttribute("srcset"),S.zoomedHd.removeAttribute("sizes"),S.zoomedHd.removeAttribute("loading"),S.zoomedHd.src=S.zoomed.getAttribute("data-zoom-src"),S.zoomedHd.onerror=function(){clearInterval(re),console.warn("Unable to reach the zoom image target "+S.zoomedHd.src),S.zoomedHd=null,X()};var re=setInterval(function(){S.zoomedHd.complete&&(clearInterval(re),S.zoomedHd.classList.add("medium-zoom-image--opened"),S.zoomedHd.addEventListener("click",_),document.body.appendChild(S.zoomedHd),X())},10)}else if(S.original.hasAttribute("srcset")){S.zoomedHd=S.zoomed.cloneNode(),S.zoomedHd.removeAttribute("sizes"),S.zoomedHd.removeAttribute("loading");var Ee=S.zoomedHd.addEventListener("load",function(){S.zoomedHd.removeEventListener("load",Ee),S.zoomedHd.classList.add("medium-zoom-image--opened"),S.zoomedHd.addEventListener("click",_),document.body.appendChild(S.zoomedHd),X()})}else X()})},_=function(){return new r(function(O){if(R||!S.original){O($);return}var P=function X(){S.original.classList.remove("medium-zoom-image--hidden"),document.body.removeChild(S.zoomed),S.zoomedHd&&document.body.removeChild(S.zoomedHd),document.body.removeChild(V),S.zoomed.classList.remove("medium-zoom-image--opened"),S.template&&document.body.removeChild(S.template),R=!1,S.zoomed.removeEventListener("transitionend",X),S.original.dispatchEvent(Xn("medium-zoom:closed",{detail:{zoom:$}})),S.original=null,S.zoomed=null,S.zoomedHd=null,S.template=null,O($)};R=!0,document.body.classList.remove("medium-zoom--opened"),S.zoomed.style.transform="",S.zoomedHd&&(S.zoomedHd.style.transform=""),S.template&&(S.template.style.transition="opacity 150ms",S.template.style.opacity=0),S.original.dispatchEvent(Xn("medium-zoom:close",{detail:{zoom:$}})),S.zoomed.addEventListener("transitionend",P)})},g=function(){var O=arguments.length>0&&arguments[0]!==void 0?arguments[0]:{},P=O.target;return S.original?_():p({target:P})},v=function(){return k},y=function(){return h},w=function(){return S.original},h=[],T=[],R=!1,j=0,k=n,S={original:null,zoomed:null,zoomedHd:null,template:null};Object.prototype.toString.call(t)==="[object Object]"?k=t:(t||typeof t=="string")&&u(t),k=Cn({margin:0,background:"#fff",scrollOffset:40,container:null,template:null},k);var V=F0(k.background);document.addEventListener("click",o),document.addEventListener("keyup",s),document.addEventListener("scroll",i),window.addEventListener("resize",_);var $={open:p,close:_,toggle:g,update:l,clone:a,attach:u,detach:d,on:c,off:f,getOptions:v,getImages:y,getZoomedImage:w};return $};function U0(e,t){t===void 0&&(t={});var n=t.insertAt;if(!(typeof document>"u")){var r=document.head||document.getElementsByTagName("head")[0],o=document.createElement("style");o.type="text/css",n==="top"&&r.firstChild?r.insertBefore(o,r.firstChild):r.appendChild(o),o.styleSheet?o.styleSheet.cssText=e:o.appendChild(document.createTextNode(e))}}var K0=".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}";U0(K0);const W0=Symbol("mediumZoom");var G0={};const q0="[vp-content] > img, [vp-content] :not(a) > img",Y0=G0,X0=300,Z0=Pt({enhance({app:e,router:t}){const n=j0(Y0);n.refresh=(r=q0)=>{n.detach(),n.attach(r)},e.provide(W0,n),t.afterEach(()=>{sl(X0).then(()=>{n.refresh()})})}}),J0=Object.freeze(Object.defineProperty({__proto__:null,default:Z0},Symbol.toStringTag,{value:"Module"}));/** + * NProgress, (c) 2013, 2014 Rico Sta. Cruz - http://ricostacruz.com/nprogress + * @license MIT + */const Ba=(e,t)=>{e.classList.add(t)},Fa=(e,t)=>{e.classList.remove(t)},Q0=e=>{var t;(t=e==null?void 0:e.parentNode)==null||t.removeChild(e)},Gi=(e,t,n)=>en?n:e,Ha=e=>(-1+e)*100,eE=(()=>{const e=[],t=()=>{const n=e.shift();n&&n(t)};return n=>{e.push(n),e.length===1&&t()}})(),tE=e=>e.replace(/^-ms-/,"ms-").replace(/-([\da-z])/gi,(t,n)=>n.toUpperCase()),Oo=(()=>{const e=["Webkit","O","Moz","ms"],t={},n=i=>{const{style:s}=document.body;if(i in s)return i;const l=i.charAt(0).toUpperCase()+i.slice(1);let a=e.length;for(;a--;){const u=`${e[a]}${l}`;if(u in s)return u}return i},r=i=>{const s=tE(i);return t[s]??(t[s]=n(s))},o=(i,s,l)=>{i.style[r(s)]=l};return(i,s)=>{for(const l in s){const a=s[l];Object.hasOwn(s,l)&&A0(a)&&o(i,l,a)}}})(),Kt={minimum:.08,easing:"ease",speed:200,trickle:!0,trickleRate:.02,trickleSpeed:800,barSelector:'[role="bar"]',parent:"body",template:'
'},ze={percent:null,isRendered:()=>!!document.getElementById("nprogress"),set:e=>{const{speed:t,easing:n}=Kt,r=ze.isStarted(),o=Gi(e,Kt.minimum,1);ze.percent=o===1?null:o;const i=ze.render(!r),s=i.querySelector(Kt.barSelector);return i.offsetWidth,eE(l=>{Oo(s,{transform:`translate3d(${Ha(o)}%,0,0)`,transition:`all ${t}ms ${n}`}),o===1?(Oo(i,{transition:"none",opacity:"1"}),i.offsetWidth,setTimeout(()=>{Oo(i,{transition:`all ${t}ms linear`,opacity:"0"}),setTimeout(()=>{ze.remove(),l()},t)},t)):setTimeout(()=>{l()},t)}),ze},isStarted:()=>typeof ze.percent=="number",start:()=>{ze.percent||ze.set(0);const e=()=>{setTimeout(()=>{ze.percent&&(ze.trickle(),e())},Kt.trickleSpeed)};return e(),ze},done:e=>!e&&!ze.percent?ze:ze.increase(.3+.5*Math.random()).set(1),increase:e=>{let{percent:t}=ze;return t?(t=Gi(t+(typeof e=="number"?e:(1-t)*Gi(Math.random()*t,.1,.95)),0,.994),ze.set(t)):ze.start()},trickle:()=>ze.increase(Math.random()*Kt.trickleRate),render:e=>{if(ze.isRendered())return document.getElementById("nprogress");Ba(document.documentElement,"nprogress-busy");const t=document.createElement("div");t.id="nprogress",t.innerHTML=Kt.template;const n=t.querySelector(Kt.barSelector),r=document.querySelector(Kt.parent),o=e?"-100":Ha(ze.percent??0);return Oo(n,{transition:"all 0 linear",transform:`translate3d(${o}%,0,0)`}),r&&(r!==document.body&&Ba(r,"nprogress-custom-parent"),r.appendChild(t)),t},remove:()=>{Fa(document.documentElement,"nprogress-busy"),Fa(document.querySelector(Kt.parent),"nprogress-custom-parent"),Q0(document.getElementById("nprogress"))}},nE=()=>{Ke(()=>{const e=Ft(),t=new Set;t.add(e.currentRoute.value.path),e.beforeEach(n=>{t.has(n.path)||ze.start()}),e.afterEach(n=>{t.add(n.path),ze.done()})})},rE=Pt({setup(){nE()}}),oE=Object.freeze(Object.defineProperty({__proto__:null,default:rE},Symbol.toStringTag,{value:"Module"})),iE="VUEPRESS_CODE_TAB_STORE",Po=mo(iE,{}),sE=fe({name:"CodeTabs",props:{active:{type:Number,default:0},data:{type:Array,required:!0},id:{type:String,required:!0},tabId:{type:String,default:""}},slots:Object,setup(e,{slots:t}){const n=ue(e.active),r=ct([]),o=()=>{e.tabId&&(Po.value[e.tabId]=e.data[n.value].id)},i=(u=n.value)=>{n.value=u{n.value=u>0?u-1:r.value.length-1,r.value[n.value].focus()},l=(u,d)=>{u.key===" "||u.key==="Enter"?(u.preventDefault(),n.value=d):u.key==="ArrowRight"?(u.preventDefault(),i()):u.key==="ArrowLeft"&&(u.preventDefault(),s()),e.tabId&&(Po.value[e.tabId]=e.data[n.value].id)},a=()=>{if(e.tabId){const u=e.data.findIndex(({id:d})=>Po.value[e.tabId]===d);if(u!==-1)return u}return e.active};return Ke(()=>{n.value=a(),Be(()=>Po.value[e.tabId],(u,d)=>{if(e.tabId&&u!==d){const c=e.data.findIndex(({id:f})=>f===u);c!==-1&&(n.value=c)}})}),()=>e.data.length?H("div",{class:"vp-code-tabs"},[H("div",{class:"vp-code-tabs-nav",role:"tablist"},e.data.map(({id:u},d)=>{const c=d===n.value;return H("button",{type:"button",ref:f=>{f&&(r.value[d]=f)},class:["vp-code-tab-nav",{active:c}],role:"tab","aria-controls":`codetab-${e.id}-${d}`,"aria-selected":c,onClick:()=>{n.value=d,o()},onKeydown:f=>{l(f,d)}},t[`title${d}`]({value:u,isActive:c}))})),e.data.map(({id:u},d)=>{const c=d===n.value;return H("div",{class:["vp-code-tab",{active:c}],id:`codetab-${e.id}-${d}`,role:"tabpanel","aria-expanded":c},[H("div",{class:"vp-code-tab-title"},t[`title${d}`]({value:u,isActive:c})),t[`tab${d}`]({value:u,isActive:c})])})]):null}}),lE="VUEPRESS_TAB_STORE",qi=mo(lE,{}),aE=fe({name:"Tabs",props:{active:{type:Number,default:0},data:{type:Array,required:!0},id:{type:String,required:!0},tabId:{type:String,default:""}},slots:Object,setup(e,{slots:t}){const n=ue(e.active),r=ct([]),o=()=>{e.tabId&&(qi.value[e.tabId]=e.data[n.value].id)},i=(u=n.value)=>{n.value=u{n.value=u>0?u-1:r.value.length-1,r.value[n.value].focus()},l=(u,d)=>{u.key===" "||u.key==="Enter"?(u.preventDefault(),n.value=d):u.key==="ArrowRight"?(u.preventDefault(),i()):u.key==="ArrowLeft"&&(u.preventDefault(),s()),o()},a=()=>{if(e.tabId){const u=e.data.findIndex(({id:d})=>qi.value[e.tabId]===d);if(u!==-1)return u}return e.active};return Ke(()=>{n.value=a(),Be(()=>qi.value[e.tabId],(u,d)=>{if(e.tabId&&u!==d){const c=e.data.findIndex(({id:f})=>f===u);c!==-1&&(n.value=c)}})}),()=>e.data.length?H("div",{class:"vp-tabs"},[H("div",{class:"vp-tabs-nav",role:"tablist"},e.data.map(({id:u},d)=>{const c=d===n.value;return H("button",{type:"button",ref:f=>{f&&(r.value[d]=f)},class:["vp-tab-nav",{active:c}],role:"tab","aria-controls":`tab-${e.id}-${d}`,"aria-selected":c,onClick:()=>{n.value=d,o()},onKeydown:f=>{l(f,d)}},t[`title${d}`]({value:u,isActive:c}))})),e.data.map(({id:u},d)=>{const c=d===n.value;return H("div",{class:["vp-tab",{active:c}],id:`tab-${e.id}-${d}`,role:"tabpanel","aria-expanded":c},[H("div",{class:"vp-tab-title"},t[`title${d}`]({value:u,isActive:c})),t[`tab${d}`]({value:u,isActive:c})])})]):null}}),uE={enhance:({app:e})=>{e.component("CodeTabs",sE),e.component("Tabs",aE)}},cE=Object.freeze(Object.defineProperty({__proto__:null,default:uE},Symbol.toStringTag,{value:"Module"}));var dE=Object.create,vf=Object.defineProperty,fE=Object.getOwnPropertyDescriptor,dl=Object.getOwnPropertyNames,pE=Object.getPrototypeOf,hE=Object.prototype.hasOwnProperty,mE=(e,t)=>function(){return e&&(t=(0,e[dl(e)[0]])(e=0)),t},_E=(e,t)=>function(){return t||(0,e[dl(e)[0]])((t={exports:{}}).exports,t),t.exports},gE=(e,t,n,r)=>{if(t&&typeof t=="object"||typeof t=="function")for(let o of dl(t))!hE.call(e,o)&&o!==n&&vf(e,o,{get:()=>t[o],enumerable:!(r=fE(t,o))||r.enumerable});return e},vE=(e,t,n)=>(n=e!=null?dE(pE(e)):{},gE(vf(n,"default",{value:e,enumerable:!0}),e)),go=mE({"../../node_modules/.pnpm/tsup@8.3.5_@microsoft+api-extractor@7.48.1_@types+node@22.10.5__jiti@2.4.2_postcss@8.4.49_tsx_s7k37zks4wtn7x2grzma6lrsfa/node_modules/tsup/assets/esm_shims.js"(){}}),EE=_E({"../../node_modules/.pnpm/rfdc@1.4.1/node_modules/rfdc/index.js"(e,t){go(),t.exports=r;function n(i){return i instanceof Buffer?Buffer.from(i):new i.constructor(i.buffer.slice(),i.byteOffset,i.length)}function r(i){if(i=i||{},i.circles)return o(i);const s=new Map;if(s.set(Date,c=>new Date(c)),s.set(Map,(c,f)=>new Map(a(Array.from(c),f))),s.set(Set,(c,f)=>new Set(a(Array.from(c),f))),i.constructorHandlers)for(const c of i.constructorHandlers)s.set(c[0],c[1]);let l=null;return i.proto?d:u;function a(c,f){const p=Object.keys(c),_=new Array(p.length);for(let g=0;gnew Date(p)),a.set(Map,(p,_)=>new Map(d(Array.from(p),_))),a.set(Set,(p,_)=>new Set(d(Array.from(p),_))),i.constructorHandlers)for(const p of i.constructorHandlers)a.set(p[0],p[1]);let u=null;return i.proto?f:c;function d(p,_){const g=Object.keys(p),v=new Array(g.length);for(let y=0;y(s=kE(e,u,d),s.finally(()=>{if(s=null,n.trailing&&l&&!o){const c=a(u,l);return l=null,c}}),s);return function(...u){return s?(n.trailing&&(l=u),s):new Promise(d=>{const c=!o&&n.leading;clearTimeout(o),o=setTimeout(()=>{o=null;const f=n.leading?r:a(this,u);for(const p of i)p(f);i=[]},t),c?(r=a(this,u),d(r)):i.push(d)})}}async function kE(e,t,n){return await e.apply(t,n)}function ys(e,t={},n){for(const r in e){const o=e[r],i=n?`${n}:${r}`:r;typeof o=="object"&&o!==null?ys(o,t,i):typeof o=="function"&&(t[i]=o)}return t}const OE={run:e=>e()},PE=()=>OE,bf=typeof console.createTask<"u"?console.createTask:PE;function CE(e,t){const n=t.shift(),r=bf(n);return e.reduce((o,i)=>o.then(()=>r.run(()=>i(...t))),Promise.resolve())}function xE(e,t){const n=t.shift(),r=bf(n);return Promise.all(e.map(o=>r.run(()=>o(...t))))}function Yi(e,t){for(const n of[...e])n(t)}class IE{constructor(){this._hooks={},this._before=void 0,this._after=void 0,this._deprecatedMessages=void 0,this._deprecatedHooks={},this.hook=this.hook.bind(this),this.callHook=this.callHook.bind(this),this.callHookWith=this.callHookWith.bind(this)}hook(t,n,r={}){if(!t||typeof n!="function")return()=>{};const o=t;let i;for(;this._deprecatedHooks[t];)i=this._deprecatedHooks[t],t=i.to;if(i&&!r.allowDeprecated){let s=i.message;s||(s=`${o} hook has been deprecated`+(i.to?`, please use ${i.to}`:"")),this._deprecatedMessages||(this._deprecatedMessages=new Set),this._deprecatedMessages.has(s)||(console.warn(s),this._deprecatedMessages.add(s))}if(!n.name)try{Object.defineProperty(n,"name",{get:()=>"_"+t.replace(/\W+/g,"_")+"_hook_cb",configurable:!0})}catch{}return this._hooks[t]=this._hooks[t]||[],this._hooks[t].push(n),()=>{n&&(this.removeHook(t,n),n=void 0)}}hookOnce(t,n){let r,o=(...i)=>(typeof r=="function"&&r(),r=void 0,o=void 0,n(...i));return r=this.hook(t,o),r}removeHook(t,n){if(this._hooks[t]){const r=this._hooks[t].indexOf(n);r!==-1&&this._hooks[t].splice(r,1),this._hooks[t].length===0&&delete this._hooks[t]}}deprecateHook(t,n){this._deprecatedHooks[t]=typeof n=="string"?{to:n}:n;const r=this._hooks[t]||[];delete this._hooks[t];for(const o of r)this.hook(t,o)}deprecateHooks(t){Object.assign(this._deprecatedHooks,t);for(const n in t)this.deprecateHook(n,t[n])}addHooks(t){const n=ys(t),r=Object.keys(n).map(o=>this.hook(o,n[o]));return()=>{for(const o of r.splice(0,r.length))o()}}removeHooks(t){const n=ys(t);for(const r in n)this.removeHook(r,n[r])}removeAllHooks(){for(const t in this._hooks)delete this._hooks[t]}callHook(t,...n){return n.unshift(t),this.callHookWith(CE,t,...n)}callHookParallel(t,...n){return n.unshift(t),this.callHookWith(xE,t,...n)}callHookWith(t,n,...r){const o=this._before||this._after?{name:n,args:r,context:{}}:void 0;this._before&&Yi(this._before,o);const i=t(n in this._hooks?[...this._hooks[n]]:[],r);return i instanceof Promise?i.finally(()=>{this._after&&o&&Yi(this._after,o)}):(this._after&&o&&Yi(this._after,o),i)}beforeEach(t){return this._before=this._before||[],this._before.push(t),()=>{if(this._before!==void 0){const n=this._before.indexOf(t);n!==-1&&this._before.splice(n,1)}}}afterEach(t){return this._after=this._after||[],this._after.push(t),()=>{if(this._after!==void 0){const n=this._after.indexOf(t);n!==-1&&this._after.splice(n,1)}}}}function yf(){return new IE}var LE=Object.create,Tf=Object.defineProperty,RE=Object.getOwnPropertyDescriptor,fl=Object.getOwnPropertyNames,DE=Object.getPrototypeOf,VE=Object.prototype.hasOwnProperty,NE=(e,t)=>function(){return e&&(t=(0,e[fl(e)[0]])(e=0)),t},wf=(e,t)=>function(){return t||(0,e[fl(e)[0]])((t={exports:{}}).exports,t),t.exports},ME=(e,t,n,r)=>{if(t&&typeof t=="object"||typeof t=="function")for(let o of fl(t))!VE.call(e,o)&&o!==n&&Tf(e,o,{get:()=>t[o],enumerable:!(r=RE(t,o))||r.enumerable});return e},zE=(e,t,n)=>(n=e!=null?LE(DE(e)):{},ME(Tf(n,"default",{value:e,enumerable:!0}),e)),z=NE({"../../node_modules/.pnpm/tsup@8.3.5_@microsoft+api-extractor@7.48.1_@types+node@22.10.5__jiti@2.4.2_postcss@8.4.49_tsx_s7k37zks4wtn7x2grzma6lrsfa/node_modules/tsup/assets/esm_shims.js"(){}}),$E=wf({"../../node_modules/.pnpm/speakingurl@14.0.1/node_modules/speakingurl/lib/speakingurl.js"(e,t){z(),function(n){var r={À:"A",Á:"A",Â:"A",Ã:"A",Ä:"Ae",Å:"A",Æ:"AE",Ç:"C",È:"E",É:"E",Ê:"E",Ë:"E",Ì:"I",Í:"I",Î:"I",Ï:"I",Ð:"D",Ñ:"N",Ò:"O",Ó:"O",Ô:"O",Õ:"O",Ö:"Oe",Ő:"O",Ø:"O",Ù:"U",Ú:"U",Û:"U",Ü:"Ue",Ű:"U",Ý:"Y",Þ:"TH",ß:"ss",à:"a",á:"a",â:"a",ã:"a",ä:"ae",å:"a",æ:"ae",ç:"c",è:"e",é:"e",ê:"e",ë:"e",ì:"i",í:"i",î:"i",ï:"i",ð:"d",ñ:"n",ò:"o",ó:"o",ô:"o",õ:"o",ö:"oe",ő:"o",ø:"o",ù:"u",ú:"u",û:"u",ü:"ue",ű:"u",ý:"y",þ:"th",ÿ:"y","ẞ":"SS",ا:"a",أ:"a",إ:"i",آ:"aa",ؤ:"u",ئ:"e",ء:"a",ب:"b",ت:"t",ث:"th",ج:"j",ح:"h",خ:"kh",د:"d",ذ:"th",ر:"r",ز:"z",س:"s",ش:"sh",ص:"s",ض:"dh",ط:"t",ظ:"z",ع:"a",غ:"gh",ف:"f",ق:"q",ك:"k",ل:"l",م:"m",ن:"n",ه:"h",و:"w",ي:"y",ى:"a",ة:"h",ﻻ:"la",ﻷ:"laa",ﻹ:"lai",ﻵ:"laa",گ:"g",چ:"ch",پ:"p",ژ:"zh",ک:"k",ی:"y","َ":"a","ً":"an","ِ":"e","ٍ":"en","ُ":"u","ٌ":"on","ْ":"","٠":"0","١":"1","٢":"2","٣":"3","٤":"4","٥":"5","٦":"6","٧":"7","٨":"8","٩":"9","۰":"0","۱":"1","۲":"2","۳":"3","۴":"4","۵":"5","۶":"6","۷":"7","۸":"8","۹":"9",က:"k",ခ:"kh",ဂ:"g",ဃ:"ga",င:"ng",စ:"s",ဆ:"sa",ဇ:"z","စျ":"za",ည:"ny",ဋ:"t",ဌ:"ta",ဍ:"d",ဎ:"da",ဏ:"na",တ:"t",ထ:"ta",ဒ:"d",ဓ:"da",န:"n",ပ:"p",ဖ:"pa",ဗ:"b",ဘ:"ba",မ:"m",ယ:"y",ရ:"ya",လ:"l",ဝ:"w",သ:"th",ဟ:"h",ဠ:"la",အ:"a","ြ":"y","ျ":"ya","ွ":"w","ြွ":"yw","ျွ":"ywa","ှ":"h",ဧ:"e","၏":"-e",ဣ:"i",ဤ:"-i",ဉ:"u",ဦ:"-u",ဩ:"aw","သြော":"aw",ဪ:"aw","၀":"0","၁":"1","၂":"2","၃":"3","၄":"4","၅":"5","၆":"6","၇":"7","၈":"8","၉":"9","္":"","့":"","း":"",č:"c",ď:"d",ě:"e",ň:"n",ř:"r",š:"s",ť:"t",ů:"u",ž:"z",Č:"C",Ď:"D",Ě:"E",Ň:"N",Ř:"R",Š:"S",Ť:"T",Ů:"U",Ž:"Z",ހ:"h",ށ:"sh",ނ:"n",ރ:"r",ބ:"b",ޅ:"lh",ކ:"k",އ:"a",ވ:"v",މ:"m",ފ:"f",ދ:"dh",ތ:"th",ލ:"l",ގ:"g",ޏ:"gn",ސ:"s",ޑ:"d",ޒ:"z",ޓ:"t",ޔ:"y",ޕ:"p",ޖ:"j",ޗ:"ch",ޘ:"tt",ޙ:"hh",ޚ:"kh",ޛ:"th",ޜ:"z",ޝ:"sh",ޞ:"s",ޟ:"d",ޠ:"t",ޡ:"z",ޢ:"a",ޣ:"gh",ޤ:"q",ޥ:"w","ަ":"a","ާ":"aa","ި":"i","ީ":"ee","ު":"u","ޫ":"oo","ެ":"e","ޭ":"ey","ޮ":"o","ޯ":"oa","ް":"",ა:"a",ბ:"b",გ:"g",დ:"d",ე:"e",ვ:"v",ზ:"z",თ:"t",ი:"i",კ:"k",ლ:"l",მ:"m",ნ:"n",ო:"o",პ:"p",ჟ:"zh",რ:"r",ს:"s",ტ:"t",უ:"u",ფ:"p",ქ:"k",ღ:"gh",ყ:"q",შ:"sh",ჩ:"ch",ც:"ts",ძ:"dz",წ:"ts",ჭ:"ch",ხ:"kh",ჯ:"j",ჰ:"h",α:"a",β:"v",γ:"g",δ:"d",ε:"e",ζ:"z",η:"i",θ:"th",ι:"i",κ:"k",λ:"l",μ:"m",ν:"n",ξ:"ks",ο:"o",π:"p",ρ:"r",σ:"s",τ:"t",υ:"y",φ:"f",χ:"x",ψ:"ps",ω:"o",ά:"a",έ:"e",ί:"i",ό:"o",ύ:"y",ή:"i",ώ:"o",ς:"s",ϊ:"i",ΰ:"y",ϋ:"y",ΐ:"i",Α:"A",Β:"B",Γ:"G",Δ:"D",Ε:"E",Ζ:"Z",Η:"I",Θ:"TH",Ι:"I",Κ:"K",Λ:"L",Μ:"M",Ν:"N",Ξ:"KS",Ο:"O",Π:"P",Ρ:"R",Σ:"S",Τ:"T",Υ:"Y",Φ:"F",Χ:"X",Ψ:"PS",Ω:"O",Ά:"A",Έ:"E",Ί:"I",Ό:"O",Ύ:"Y",Ή:"I",Ώ:"O",Ϊ:"I",Ϋ:"Y",ā:"a",ē:"e",ģ:"g",ī:"i",ķ:"k",ļ:"l",ņ:"n",ū:"u",Ā:"A",Ē:"E",Ģ:"G",Ī:"I",Ķ:"k",Ļ:"L",Ņ:"N",Ū:"U",Ќ:"Kj",ќ:"kj",Љ:"Lj",љ:"lj",Њ:"Nj",њ:"nj",Тс:"Ts",тс:"ts",ą:"a",ć:"c",ę:"e",ł:"l",ń:"n",ś:"s",ź:"z",ż:"z",Ą:"A",Ć:"C",Ę:"E",Ł:"L",Ń:"N",Ś:"S",Ź:"Z",Ż:"Z",Є:"Ye",І:"I",Ї:"Yi",Ґ:"G",є:"ye",і:"i",ї:"yi",ґ:"g",ă:"a",Ă:"A",ș:"s",Ș:"S",ț:"t",Ț:"T",ţ:"t",Ţ:"T",а:"a",б:"b",в:"v",г:"g",д:"d",е:"e",ё:"yo",ж:"zh",з:"z",и:"i",й:"i",к:"k",л:"l",м:"m",н:"n",о:"o",п:"p",р:"r",с:"s",т:"t",у:"u",ф:"f",х:"kh",ц:"c",ч:"ch",ш:"sh",щ:"sh",ъ:"",ы:"y",ь:"",э:"e",ю:"yu",я:"ya",А:"A",Б:"B",В:"V",Г:"G",Д:"D",Е:"E",Ё:"Yo",Ж:"Zh",З:"Z",И:"I",Й:"I",К:"K",Л:"L",М:"M",Н:"N",О:"O",П:"P",Р:"R",С:"S",Т:"T",У:"U",Ф:"F",Х:"Kh",Ц:"C",Ч:"Ch",Ш:"Sh",Щ:"Sh",Ъ:"",Ы:"Y",Ь:"",Э:"E",Ю:"Yu",Я:"Ya",ђ:"dj",ј:"j",ћ:"c",џ:"dz",Ђ:"Dj",Ј:"j",Ћ:"C",Џ:"Dz",ľ:"l",ĺ:"l",ŕ:"r",Ľ:"L",Ĺ:"L",Ŕ:"R",ş:"s",Ş:"S",ı:"i",İ:"I",ğ:"g",Ğ:"G",ả:"a",Ả:"A",ẳ:"a",Ẳ:"A",ẩ:"a",Ẩ:"A",đ:"d",Đ:"D",ẹ:"e",Ẹ:"E",ẽ:"e",Ẽ:"E",ẻ:"e",Ẻ:"E",ế:"e",Ế:"E",ề:"e",Ề:"E",ệ:"e",Ệ:"E",ễ:"e",Ễ:"E",ể:"e",Ể:"E",ỏ:"o",ọ:"o",Ọ:"o",ố:"o",Ố:"O",ồ:"o",Ồ:"O",ổ:"o",Ổ:"O",ộ:"o",Ộ:"O",ỗ:"o",Ỗ:"O",ơ:"o",Ơ:"O",ớ:"o",Ớ:"O",ờ:"o",Ờ:"O",ợ:"o",Ợ:"O",ỡ:"o",Ỡ:"O",Ở:"o",ở:"o",ị:"i",Ị:"I",ĩ:"i",Ĩ:"I",ỉ:"i",Ỉ:"i",ủ:"u",Ủ:"U",ụ:"u",Ụ:"U",ũ:"u",Ũ:"U",ư:"u",Ư:"U",ứ:"u",Ứ:"U",ừ:"u",Ừ:"U",ự:"u",Ự:"U",ữ:"u",Ữ:"U",ử:"u",Ử:"ư",ỷ:"y",Ỷ:"y",ỳ:"y",Ỳ:"Y",ỵ:"y",Ỵ:"Y",ỹ:"y",Ỹ:"Y",ạ:"a",Ạ:"A",ấ:"a",Ấ:"A",ầ:"a",Ầ:"A",ậ:"a",Ậ:"A",ẫ:"a",Ẫ:"A",ắ:"a",Ắ:"A",ằ:"a",Ằ:"A",ặ:"a",Ặ:"A",ẵ:"a",Ẵ:"A","⓪":"0","①":"1","②":"2","③":"3","④":"4","⑤":"5","⑥":"6","⑦":"7","⑧":"8","⑨":"9","⑩":"10","⑪":"11","⑫":"12","⑬":"13","⑭":"14","⑮":"15","⑯":"16","⑰":"17","⑱":"18","⑲":"18","⑳":"18","⓵":"1","⓶":"2","⓷":"3","⓸":"4","⓹":"5","⓺":"6","⓻":"7","⓼":"8","⓽":"9","⓾":"10","⓿":"0","⓫":"11","⓬":"12","⓭":"13","⓮":"14","⓯":"15","⓰":"16","⓱":"17","⓲":"18","⓳":"19","⓴":"20","Ⓐ":"A","Ⓑ":"B","Ⓒ":"C","Ⓓ":"D","Ⓔ":"E","Ⓕ":"F","Ⓖ":"G","Ⓗ":"H","Ⓘ":"I","Ⓙ":"J","Ⓚ":"K","Ⓛ":"L","Ⓜ":"M","Ⓝ":"N","Ⓞ":"O","Ⓟ":"P","Ⓠ":"Q","Ⓡ":"R","Ⓢ":"S","Ⓣ":"T","Ⓤ":"U","Ⓥ":"V","Ⓦ":"W","Ⓧ":"X","Ⓨ":"Y","Ⓩ":"Z","ⓐ":"a","ⓑ":"b","ⓒ":"c","ⓓ":"d","ⓔ":"e","ⓕ":"f","ⓖ":"g","ⓗ":"h","ⓘ":"i","ⓙ":"j","ⓚ":"k","ⓛ":"l","ⓜ":"m","ⓝ":"n","ⓞ":"o","ⓟ":"p","ⓠ":"q","ⓡ":"r","ⓢ":"s","ⓣ":"t","ⓤ":"u","ⓦ":"v","ⓥ":"w","ⓧ":"x","ⓨ":"y","ⓩ":"z","“":'"',"”":'"',"‘":"'","’":"'","∂":"d",ƒ:"f","™":"(TM)","©":"(C)",œ:"oe",Œ:"OE","®":"(R)","†":"+","℠":"(SM)","…":"...","˚":"o",º:"o",ª:"a","•":"*","၊":",","။":".",$:"USD","€":"EUR","₢":"BRN","₣":"FRF","£":"GBP","₤":"ITL","₦":"NGN","₧":"ESP","₩":"KRW","₪":"ILS","₫":"VND","₭":"LAK","₮":"MNT","₯":"GRD","₱":"ARS","₲":"PYG","₳":"ARA","₴":"UAH","₵":"GHS","¢":"cent","¥":"CNY",元:"CNY",円:"YEN","﷼":"IRR","₠":"EWE","฿":"THB","₨":"INR","₹":"INR","₰":"PF","₺":"TRY","؋":"AFN","₼":"AZN",лв:"BGN","៛":"KHR","₡":"CRC","₸":"KZT",ден:"MKD",zł:"PLN","₽":"RUB","₾":"GEL"},o=["်","ް"],i={"ာ":"a","ါ":"a","ေ":"e","ဲ":"e","ိ":"i","ီ":"i","ို":"o","ု":"u","ူ":"u","ေါင်":"aung","ော":"aw","ော်":"aw","ေါ":"aw","ေါ်":"aw","်":"်","က်":"et","ိုက်":"aik","ောက်":"auk","င်":"in","ိုင်":"aing","ောင်":"aung","စ်":"it","ည်":"i","တ်":"at","ိတ်":"eik","ုတ်":"ok","ွတ်":"ut","ေတ်":"it","ဒ်":"d","ိုဒ်":"ok","ုဒ်":"ait","န်":"an","ာန်":"an","ိန်":"ein","ုန်":"on","ွန်":"un","ပ်":"at","ိပ်":"eik","ုပ်":"ok","ွပ်":"ut","န်ုပ်":"nub","မ်":"an","ိမ်":"ein","ုမ်":"on","ွမ်":"un","ယ်":"e","ိုလ်":"ol","ဉ်":"in","ံ":"an","ိံ":"ein","ုံ":"on","ައް":"ah","ަށް":"ah"},s={en:{},az:{ç:"c",ə:"e",ğ:"g",ı:"i",ö:"o",ş:"s",ü:"u",Ç:"C",Ə:"E",Ğ:"G",İ:"I",Ö:"O",Ş:"S",Ü:"U"},cs:{č:"c",ď:"d",ě:"e",ň:"n",ř:"r",š:"s",ť:"t",ů:"u",ž:"z",Č:"C",Ď:"D",Ě:"E",Ň:"N",Ř:"R",Š:"S",Ť:"T",Ů:"U",Ž:"Z"},fi:{ä:"a",Ä:"A",ö:"o",Ö:"O"},hu:{ä:"a",Ä:"A",ö:"o",Ö:"O",ü:"u",Ü:"U",ű:"u",Ű:"U"},lt:{ą:"a",č:"c",ę:"e",ė:"e",į:"i",š:"s",ų:"u",ū:"u",ž:"z",Ą:"A",Č:"C",Ę:"E",Ė:"E",Į:"I",Š:"S",Ų:"U",Ū:"U"},lv:{ā:"a",č:"c",ē:"e",ģ:"g",ī:"i",ķ:"k",ļ:"l",ņ:"n",š:"s",ū:"u",ž:"z",Ā:"A",Č:"C",Ē:"E",Ģ:"G",Ī:"i",Ķ:"k",Ļ:"L",Ņ:"N",Š:"S",Ū:"u",Ž:"Z"},pl:{ą:"a",ć:"c",ę:"e",ł:"l",ń:"n",ó:"o",ś:"s",ź:"z",ż:"z",Ą:"A",Ć:"C",Ę:"e",Ł:"L",Ń:"N",Ó:"O",Ś:"S",Ź:"Z",Ż:"Z"},sv:{ä:"a",Ä:"A",ö:"o",Ö:"O"},sk:{ä:"a",Ä:"A"},sr:{љ:"lj",њ:"nj",Љ:"Lj",Њ:"Nj",đ:"dj",Đ:"Dj"},tr:{Ü:"U",Ö:"O",ü:"u",ö:"o"}},l={ar:{"∆":"delta","∞":"la-nihaya","♥":"hob","&":"wa","|":"aw","<":"aqal-men",">":"akbar-men","∑":"majmou","¤":"omla"},az:{},ca:{"∆":"delta","∞":"infinit","♥":"amor","&":"i","|":"o","<":"menys que",">":"mes que","∑":"suma dels","¤":"moneda"},cs:{"∆":"delta","∞":"nekonecno","♥":"laska","&":"a","|":"nebo","<":"mensi nez",">":"vetsi nez","∑":"soucet","¤":"mena"},de:{"∆":"delta","∞":"unendlich","♥":"Liebe","&":"und","|":"oder","<":"kleiner als",">":"groesser als","∑":"Summe von","¤":"Waehrung"},dv:{"∆":"delta","∞":"kolunulaa","♥":"loabi","&":"aai","|":"noonee","<":"ah vure kuda",">":"ah vure bodu","∑":"jumula","¤":"faisaa"},en:{"∆":"delta","∞":"infinity","♥":"love","&":"and","|":"or","<":"less than",">":"greater than","∑":"sum","¤":"currency"},es:{"∆":"delta","∞":"infinito","♥":"amor","&":"y","|":"u","<":"menos que",">":"mas que","∑":"suma de los","¤":"moneda"},fa:{"∆":"delta","∞":"bi-nahayat","♥":"eshgh","&":"va","|":"ya","<":"kamtar-az",">":"bishtar-az","∑":"majmooe","¤":"vahed"},fi:{"∆":"delta","∞":"aarettomyys","♥":"rakkaus","&":"ja","|":"tai","<":"pienempi kuin",">":"suurempi kuin","∑":"summa","¤":"valuutta"},fr:{"∆":"delta","∞":"infiniment","♥":"Amour","&":"et","|":"ou","<":"moins que",">":"superieure a","∑":"somme des","¤":"monnaie"},ge:{"∆":"delta","∞":"usasruloba","♥":"siqvaruli","&":"da","|":"an","<":"naklebi",">":"meti","∑":"jami","¤":"valuta"},gr:{},hu:{"∆":"delta","∞":"vegtelen","♥":"szerelem","&":"es","|":"vagy","<":"kisebb mint",">":"nagyobb mint","∑":"szumma","¤":"penznem"},it:{"∆":"delta","∞":"infinito","♥":"amore","&":"e","|":"o","<":"minore di",">":"maggiore di","∑":"somma","¤":"moneta"},lt:{"∆":"delta","∞":"begalybe","♥":"meile","&":"ir","|":"ar","<":"maziau nei",">":"daugiau nei","∑":"suma","¤":"valiuta"},lv:{"∆":"delta","∞":"bezgaliba","♥":"milestiba","&":"un","|":"vai","<":"mazak neka",">":"lielaks neka","∑":"summa","¤":"valuta"},my:{"∆":"kwahkhyaet","∞":"asaonasme","♥":"akhyait","&":"nhin","|":"tho","<":"ngethaw",">":"kyithaw","∑":"paungld","¤":"ngwekye"},mk:{},nl:{"∆":"delta","∞":"oneindig","♥":"liefde","&":"en","|":"of","<":"kleiner dan",">":"groter dan","∑":"som","¤":"valuta"},pl:{"∆":"delta","∞":"nieskonczonosc","♥":"milosc","&":"i","|":"lub","<":"mniejsze niz",">":"wieksze niz","∑":"suma","¤":"waluta"},pt:{"∆":"delta","∞":"infinito","♥":"amor","&":"e","|":"ou","<":"menor que",">":"maior que","∑":"soma","¤":"moeda"},ro:{"∆":"delta","∞":"infinit","♥":"dragoste","&":"si","|":"sau","<":"mai mic ca",">":"mai mare ca","∑":"suma","¤":"valuta"},ru:{"∆":"delta","∞":"beskonechno","♥":"lubov","&":"i","|":"ili","<":"menshe",">":"bolshe","∑":"summa","¤":"valjuta"},sk:{"∆":"delta","∞":"nekonecno","♥":"laska","&":"a","|":"alebo","<":"menej ako",">":"viac ako","∑":"sucet","¤":"mena"},sr:{},tr:{"∆":"delta","∞":"sonsuzluk","♥":"ask","&":"ve","|":"veya","<":"kucuktur",">":"buyuktur","∑":"toplam","¤":"para birimi"},uk:{"∆":"delta","∞":"bezkinechnist","♥":"lubov","&":"i","|":"abo","<":"menshe",">":"bilshe","∑":"suma","¤":"valjuta"},vn:{"∆":"delta","∞":"vo cuc","♥":"yeu","&":"va","|":"hoac","<":"nho hon",">":"lon hon","∑":"tong","¤":"tien te"}},a=[";","?",":","@","&","=","+","$",",","/"].join(""),u=[";","?",":","@","&","=","+","$",","].join(""),d=[".","!","~","*","'","(",")"].join(""),c=function(v,y){var w="-",h="",T="",R=!0,j={},k,S,V,$,I,O,P,X,te,L,D,K,re,Ee,Ie="";if(typeof v!="string")return"";if(typeof y=="string"&&(w=y),P=l.en,X=s.en,typeof y=="object"){k=y.maintainCase||!1,j=y.custom&&typeof y.custom=="object"?y.custom:j,V=+y.truncate>1&&y.truncate||!1,$=y.uric||!1,I=y.uricNoSlash||!1,O=y.mark||!1,R=!(y.symbols===!1||y.lang===!1),w=y.separator||w,$&&(Ie+=a),I&&(Ie+=u),O&&(Ie+=d),P=y.lang&&l[y.lang]&&R?l[y.lang]:R?l.en:{},X=y.lang&&s[y.lang]?s[y.lang]:y.lang===!1||y.lang===!0?{}:s.en,y.titleCase&&typeof y.titleCase.length=="number"&&Array.prototype.toString.call(y.titleCase)?(y.titleCase.forEach(function(_e){j[_e+""]=_e+""}),S=!0):S=!!y.titleCase,y.custom&&typeof y.custom.length=="number"&&Array.prototype.toString.call(y.custom)&&y.custom.forEach(function(_e){j[_e+""]=_e+""}),Object.keys(j).forEach(function(_e){var Fe;_e.length>1?Fe=new RegExp("\\b"+p(_e)+"\\b","gi"):Fe=new RegExp(p(_e),"gi"),v=v.replace(Fe,j[_e])});for(D in j)Ie+=D}for(Ie+=w,Ie=p(Ie),v=v.replace(/(^\s+|\s+$)/g,""),re=!1,Ee=!1,L=0,K=v.length;L=0?(T+=D,D=""):Ee===!0?(D=i[T]+r[D],T=""):D=re&&r[D].match(/[A-Za-z0-9]/)?" "+r[D]:r[D],re=!1,Ee=!1):D in i?(T+=D,D="",L===K-1&&(D=i[T]),Ee=!0):P[D]&&!($&&a.indexOf(D)!==-1)&&!(I&&u.indexOf(D)!==-1)?(D=re||h.substr(-1).match(/[A-Za-z0-9]/)?w+P[D]:P[D],D+=v[L+1]!==void 0&&v[L+1].match(/[A-Za-z0-9]/)?w:"",re=!0):(Ee===!0?(D=i[T]+D,T="",Ee=!1):re&&(/[A-Za-z0-9]/.test(D)||h.substr(-1).match(/A-Za-z0-9]/))&&(D=" "+D),re=!1),h+=D.replace(new RegExp("[^\\w\\s"+Ie+"_-]","g"),w);return S&&(h=h.replace(/(\w)(\S*)/g,function(_e,Fe,lt){var je=Fe.toUpperCase()+(lt!==null?lt:"");return Object.keys(j).indexOf(je.toLowerCase())<0?je:je.toLowerCase()})),h=h.replace(/\s+/g,w).replace(new RegExp("\\"+w+"+","g"),w).replace(new RegExp("(^\\"+w+"+|\\"+w+"+$)","g"),""),V&&h.length>V&&(te=h.charAt(V)===w,h=h.slice(0,V),te||(h=h.slice(0,h.lastIndexOf(w)))),!k&&!S&&(h=h.toLowerCase()),h},f=function(v){return function(w){return c(w,v)}},p=function(v){return v.replace(/[-\\^$*+?.()|[\]{}\/]/g,"\\$&")},_=function(g,v){for(var y in v)if(v[y]===g)return!0};if(typeof t<"u"&&t.exports)t.exports=c,t.exports.createSlug=f;else if(typeof define<"u"&&define.amd)define([],function(){return c});else try{if(n.getSlug||n.createSlug)throw"speakingurl: globals exists /(getSlug|createSlug)/";n.getSlug=c,n.createSlug=f}catch{}}(e)}}),BE=wf({"../../node_modules/.pnpm/speakingurl@14.0.1/node_modules/speakingurl/index.js"(e,t){z(),t.exports=$E()}});z();z();z();z();z();z();z();z();function FE(e){var t;const n=e.name||e._componentTag||e.__VUE_DEVTOOLS_COMPONENT_GUSSED_NAME__||e.__name;return n==="index"&&((t=e.__file)!=null&&t.endsWith("index.vue"))?"":n}function HE(e){const t=e.__file;if(t)return wE(AE(t,".vue"))}function Ka(e,t){return e.type.__VUE_DEVTOOLS_COMPONENT_GUSSED_NAME__=t,t}function pl(e){if(e.__VUE_DEVTOOLS_NEXT_APP_RECORD__)return e.__VUE_DEVTOOLS_NEXT_APP_RECORD__;if(e.root)return e.appContext.app.__VUE_DEVTOOLS_NEXT_APP_RECORD__}function Af(e){var t,n;const r=(t=e.subTree)==null?void 0:t.type,o=pl(e);return o?((n=o==null?void 0:o.types)==null?void 0:n.Fragment)===r:!1}function vi(e){var t,n,r;const o=FE((e==null?void 0:e.type)||{});if(o)return o;if((e==null?void 0:e.root)===e)return"Root";for(const s in(n=(t=e.parent)==null?void 0:t.type)==null?void 0:n.components)if(e.parent.type.components[s]===(e==null?void 0:e.type))return Ka(e,s);for(const s in(r=e.appContext)==null?void 0:r.components)if(e.appContext.components[s]===(e==null?void 0:e.type))return Ka(e,s);const i=HE((e==null?void 0:e.type)||{});return i||"Anonymous Component"}function jE(e){var t,n,r;const o=(r=(n=(t=e==null?void 0:e.appContext)==null?void 0:t.app)==null?void 0:n.__VUE_DEVTOOLS_NEXT_APP_RECORD_ID__)!=null?r:0,i=e===(e==null?void 0:e.root)?"root":e.uid;return`${o}:${i}`}function Ts(e,t){return t=t||`${e.id}:root`,e.instanceMap.get(t)||e.instanceMap.get(":root")}function UE(){const e={top:0,bottom:0,left:0,right:0,get width(){return e.right-e.left},get height(){return e.bottom-e.top}};return e}var Co;function KE(e){return Co||(Co=document.createRange()),Co.selectNode(e),Co.getBoundingClientRect()}function WE(e){const t=UE();if(!e.children)return t;for(let n=0,r=e.children.length;ne.bottom)&&(e.bottom=t.bottom),(!e.left||t.lefte.right)&&(e.right=t.right),e}var Wa={top:0,left:0,right:0,bottom:0,width:0,height:0};function Hn(e){const t=e.subTree.el;return typeof window>"u"?Wa:Af(e)?WE(e.subTree):(t==null?void 0:t.nodeType)===1?t==null?void 0:t.getBoundingClientRect():e.subTree.component?Hn(e.subTree.component):Wa}z();function hl(e){return Af(e)?qE(e.subTree):e.subTree?[e.subTree.el]:[]}function qE(e){if(!e.children)return[];const t=[];return e.children.forEach(n=>{n.component?t.push(...hl(n.component)):n!=null&&n.el&&t.push(n.el)}),t}var Sf="__vue-devtools-component-inspector__",kf="__vue-devtools-component-inspector__card__",Of="__vue-devtools-component-inspector__name__",Pf="__vue-devtools-component-inspector__indicator__",Cf={display:"block",zIndex:2147483640,position:"fixed",backgroundColor:"#42b88325",border:"1px solid #42b88350",borderRadius:"5px",transition:"all 0.1s ease-in",pointerEvents:"none"},YE={fontFamily:"Arial, Helvetica, sans-serif",padding:"5px 8px",borderRadius:"4px",textAlign:"left",position:"absolute",left:0,color:"#e9e9e9",fontSize:"14px",fontWeight:600,lineHeight:"24px",backgroundColor:"#42b883",boxShadow:"0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px -1px rgba(0, 0, 0, 0.1)"},XE={display:"inline-block",fontWeight:400,fontStyle:"normal",fontSize:"12px",opacity:.7};function br(){return document.getElementById(Sf)}function ZE(){return document.getElementById(kf)}function JE(){return document.getElementById(Pf)}function QE(){return document.getElementById(Of)}function ml(e){return{left:`${Math.round(e.left*100)/100}px`,top:`${Math.round(e.top*100)/100}px`,width:`${Math.round(e.width*100)/100}px`,height:`${Math.round(e.height*100)/100}px`}}function _l(e){var t;const n=document.createElement("div");n.id=(t=e.elementId)!=null?t:Sf,Object.assign(n.style,{...Cf,...ml(e.bounds),...e.style});const r=document.createElement("span");r.id=kf,Object.assign(r.style,{...YE,top:e.bounds.top<35?0:"-35px"});const o=document.createElement("span");o.id=Of,o.innerHTML=`<${e.name}>  `;const i=document.createElement("i");return i.id=Pf,i.innerHTML=`${Math.round(e.bounds.width*100)/100} x ${Math.round(e.bounds.height*100)/100}`,Object.assign(i.style,XE),r.appendChild(o),r.appendChild(i),n.appendChild(r),document.body.appendChild(n),n}function gl(e){const t=br(),n=ZE(),r=QE(),o=JE();t&&(Object.assign(t.style,{...Cf,...ml(e.bounds)}),Object.assign(n.style,{top:e.bounds.top<35?0:"-35px"}),r.innerHTML=`<${e.name}>  `,o.innerHTML=`${Math.round(e.bounds.width*100)/100} x ${Math.round(e.bounds.height*100)/100}`)}function eb(e){const t=Hn(e);if(!t.width&&!t.height)return;const n=vi(e);br()?gl({bounds:t,name:n}):_l({bounds:t,name:n})}function xf(){const e=br();e&&(e.style.display="none")}var ws=null;function As(e){const t=e.target;if(t){const n=t.__vueParentComponent;if(n&&(ws=n,n.vnode.el)){const o=Hn(n),i=vi(n);br()?gl({bounds:o,name:i}):_l({bounds:o,name:i})}}}function tb(e,t){if(e.preventDefault(),e.stopPropagation(),ws){const n=jE(ws);t(n)}}var ti=null;function nb(){xf(),window.removeEventListener("mouseover",As),window.removeEventListener("click",ti,!0),ti=null}function rb(){return window.addEventListener("mouseover",As),new Promise(e=>{function t(n){n.preventDefault(),n.stopPropagation(),tb(n,r=>{window.removeEventListener("click",t,!0),ti=null,window.removeEventListener("mouseover",As);const o=br();o&&(o.style.display="none"),e(JSON.stringify({id:r}))})}ti=t,window.addEventListener("click",t,!0)})}function ob(e){const t=Ts(it.value,e.id);if(t){const[n]=hl(t);if(typeof n.scrollIntoView=="function")n.scrollIntoView({behavior:"smooth"});else{const r=Hn(t),o=document.createElement("div"),i={...ml(r),position:"absolute"};Object.assign(o.style,i),document.body.appendChild(o),o.scrollIntoView({behavior:"smooth"}),setTimeout(()=>{document.body.removeChild(o)},2e3)}setTimeout(()=>{const r=Hn(t);if(r.width||r.height){const o=vi(t),i=br();i?gl({...e,name:o,bounds:r}):_l({...e,name:o,bounds:r}),setTimeout(()=>{i&&(i.style.display="none")},1500)}},1200)}}z();var Ga,qa;(qa=(Ga=Q).__VUE_DEVTOOLS_COMPONENT_INSPECTOR_ENABLED__)!=null||(Ga.__VUE_DEVTOOLS_COMPONENT_INSPECTOR_ENABLED__=!0);function ib(e){let t=0;const n=setInterval(()=>{Q.__VUE_INSPECTOR__&&(clearInterval(n),t+=30,e()),t>=5e3&&clearInterval(n)},30)}function sb(){const e=Q.__VUE_INSPECTOR__,t=e.openInEditor;e.openInEditor=async(...n)=>{e.disable(),t(...n)}}function lb(){return new Promise(e=>{function t(){sb(),e(Q.__VUE_INSPECTOR__)}Q.__VUE_INSPECTOR__?t():ib(()=>{t()})})}z();z();function ab(e){return!!(e&&e.__v_isReadonly)}function If(e){return ab(e)?If(e.__v_raw):!!(e&&e.__v_isReactive)}function Xi(e){return!!(e&&e.__v_isRef===!0)}function Lr(e){const t=e&&e.__v_raw;return t?Lr(t):e}var ub=class{constructor(){this.refEditor=new cb}set(e,t,n,r){const o=Array.isArray(t)?t:t.split(".");for(;o.length>1;){const l=o.shift();e instanceof Map&&(e=e.get(l)),e instanceof Set?e=Array.from(e.values())[l]:e=e[l],this.refEditor.isRef(e)&&(e=this.refEditor.get(e))}const i=o[0],s=this.refEditor.get(e)[i];r?r(e,i,n):this.refEditor.isRef(s)?this.refEditor.set(s,n):e[i]=n}get(e,t){const n=Array.isArray(t)?t:t.split(".");for(let r=0;r"u")return!1;const r=Array.isArray(t)?t.slice():t.split("."),o=n?2:1;for(;e&&r.length>o;){const i=r.shift();e=e[i],this.refEditor.isRef(e)&&(e=this.refEditor.get(e))}return e!=null&&Object.prototype.hasOwnProperty.call(e,r[0])}createDefaultSetCallback(e){return(t,n,r)=>{if((e.remove||e.newKey)&&(Array.isArray(t)?t.splice(n,1):Lr(t)instanceof Map?t.delete(n):Lr(t)instanceof Set?t.delete(Array.from(t.values())[n]):Reflect.deleteProperty(t,n)),!e.remove){const o=t[e.newKey||n];this.refEditor.isRef(o)?this.refEditor.set(o,r):Lr(t)instanceof Map?t.set(e.newKey||n,r):Lr(t)instanceof Set?t.add(r):t[e.newKey||n]=r}}}},cb=class{set(e,t){if(Xi(e))e.value=t;else{if(e instanceof Set&&Array.isArray(t)){e.clear(),t.forEach(o=>e.add(o));return}const n=Object.keys(t);if(e instanceof Map){const o=new Set(e.keys());n.forEach(i=>{e.set(i,Reflect.get(t,i)),o.delete(i)}),o.forEach(i=>e.delete(i));return}const r=new Set(Object.keys(e));n.forEach(o=>{Reflect.set(e,o,Reflect.get(t,o)),r.delete(o)}),r.forEach(o=>Reflect.deleteProperty(e,o))}}get(e){return Xi(e)?e.value:e}isRef(e){return Xi(e)||If(e)}};z();z();z();var db="__VUE_DEVTOOLS_KIT_TIMELINE_LAYERS_STATE__";function fb(){if(!Ef||typeof localStorage>"u"||localStorage===null)return{recordingState:!1,mouseEventEnabled:!1,keyboardEventEnabled:!1,componentEventEnabled:!1,performanceEventEnabled:!1,selected:""};const e=localStorage.getItem(db);return e?JSON.parse(e):{recordingState:!1,mouseEventEnabled:!1,keyboardEventEnabled:!1,componentEventEnabled:!1,performanceEventEnabled:!1,selected:""}}z();z();z();var Ya,Xa;(Xa=(Ya=Q).__VUE_DEVTOOLS_KIT_TIMELINE_LAYERS)!=null||(Ya.__VUE_DEVTOOLS_KIT_TIMELINE_LAYERS=[]);var pb=new Proxy(Q.__VUE_DEVTOOLS_KIT_TIMELINE_LAYERS,{get(e,t,n){return Reflect.get(e,t,n)}});function hb(e,t){Ge.timelineLayersState[t.id]=!1,pb.push({...e,descriptorId:t.id,appRecord:pl(t.app)})}var Za,Ja;(Ja=(Za=Q).__VUE_DEVTOOLS_KIT_INSPECTOR__)!=null||(Za.__VUE_DEVTOOLS_KIT_INSPECTOR__=[]);var vl=new Proxy(Q.__VUE_DEVTOOLS_KIT_INSPECTOR__,{get(e,t,n){return Reflect.get(e,t,n)}}),Lf=hr(()=>{yr.hooks.callHook("sendInspectorToClient",Rf())});function mb(e,t){var n,r;vl.push({options:e,descriptor:t,treeFilterPlaceholder:(n=e.treeFilterPlaceholder)!=null?n:"Search tree...",stateFilterPlaceholder:(r=e.stateFilterPlaceholder)!=null?r:"Search state...",treeFilter:"",selectedNodeId:"",appRecord:pl(t.app)}),Lf()}function Rf(){return vl.filter(e=>e.descriptor.app===it.value.app).filter(e=>e.descriptor.id!=="components").map(e=>{var t;const n=e.descriptor,r=e.options;return{id:r.id,label:r.label,logo:n.logo,icon:`custom-ic-baseline-${(t=r==null?void 0:r.icon)==null?void 0:t.replace(/_/g,"-")}`,packageName:n.packageName,homepage:n.homepage,pluginId:n.id}})}function No(e,t){return vl.find(n=>n.options.id===e&&(t?n.descriptor.app===t:!0))}function _b(){const e=yf();e.hook("addInspector",({inspector:r,plugin:o})=>{mb(r,o.descriptor)});const t=hr(async({inspectorId:r,plugin:o})=>{var i;if(!r||!((i=o==null?void 0:o.descriptor)!=null&&i.app)||Ge.highPerfModeEnabled)return;const s=No(r,o.descriptor.app),l={app:o.descriptor.app,inspectorId:r,filter:(s==null?void 0:s.treeFilter)||"",rootNodes:[]};await new Promise(a=>{e.callHookWith(async u=>{await Promise.all(u.map(d=>d(l))),a()},"getInspectorTree")}),e.callHookWith(async a=>{await Promise.all(a.map(u=>u({inspectorId:r,rootNodes:l.rootNodes})))},"sendInspectorTreeToClient")},120);e.hook("sendInspectorTree",t);const n=hr(async({inspectorId:r,plugin:o})=>{var i;if(!r||!((i=o==null?void 0:o.descriptor)!=null&&i.app)||Ge.highPerfModeEnabled)return;const s=No(r,o.descriptor.app),l={app:o.descriptor.app,inspectorId:r,nodeId:(s==null?void 0:s.selectedNodeId)||"",state:null},a={currentTab:`custom-inspector:${r}`};l.nodeId&&await new Promise(u=>{e.callHookWith(async d=>{await Promise.all(d.map(c=>c(l,a))),u()},"getInspectorState")}),e.callHookWith(async u=>{await Promise.all(u.map(d=>d({inspectorId:r,nodeId:l.nodeId,state:l.state})))},"sendInspectorStateToClient")},120);return e.hook("sendInspectorState",n),e.hook("customInspectorSelectNode",({inspectorId:r,nodeId:o,plugin:i})=>{const s=No(r,i.descriptor.app);s&&(s.selectedNodeId=o)}),e.hook("timelineLayerAdded",({options:r,plugin:o})=>{hb(r,o.descriptor)}),e.hook("timelineEventAdded",({options:r,plugin:o})=>{var i;const s=["performance","component-event","keyboard","mouse"];Ge.highPerfModeEnabled||!((i=Ge.timelineLayersState)!=null&&i[o.descriptor.id])&&!s.includes(r.layerId)||e.callHookWith(async l=>{await Promise.all(l.map(a=>a(r)))},"sendTimelineEventToClient")}),e.hook("getComponentInstances",async({app:r})=>{const o=r.__VUE_DEVTOOLS_NEXT_APP_RECORD__;if(!o)return null;const i=o.id.toString();return[...o.instanceMap].filter(([l])=>l.split(":")[0]===i).map(([,l])=>l)}),e.hook("getComponentBounds",async({instance:r})=>Hn(r)),e.hook("getComponentName",({instance:r})=>vi(r)),e.hook("componentHighlight",({uid:r})=>{const o=it.value.instanceMap.get(r);o&&eb(o)}),e.hook("componentUnhighlight",()=>{xf()}),e}var Qa,eu;(eu=(Qa=Q).__VUE_DEVTOOLS_KIT_APP_RECORDS__)!=null||(Qa.__VUE_DEVTOOLS_KIT_APP_RECORDS__=[]);var tu,nu;(nu=(tu=Q).__VUE_DEVTOOLS_KIT_ACTIVE_APP_RECORD__)!=null||(tu.__VUE_DEVTOOLS_KIT_ACTIVE_APP_RECORD__={});var ru,ou;(ou=(ru=Q).__VUE_DEVTOOLS_KIT_ACTIVE_APP_RECORD_ID__)!=null||(ru.__VUE_DEVTOOLS_KIT_ACTIVE_APP_RECORD_ID__="");var iu,su;(su=(iu=Q).__VUE_DEVTOOLS_KIT_CUSTOM_TABS__)!=null||(iu.__VUE_DEVTOOLS_KIT_CUSTOM_TABS__=[]);var lu,au;(au=(lu=Q).__VUE_DEVTOOLS_KIT_CUSTOM_COMMANDS__)!=null||(lu.__VUE_DEVTOOLS_KIT_CUSTOM_COMMANDS__=[]);var Ln="__VUE_DEVTOOLS_KIT_GLOBAL_STATE__";function gb(){return{connected:!1,clientConnected:!1,vitePluginDetected:!0,appRecords:[],activeAppRecordId:"",tabs:[],commands:[],highPerfModeEnabled:!0,devtoolsClientDetected:{},perfUniqueGroupId:0,timelineLayersState:fb()}}var uu,cu;(cu=(uu=Q)[Ln])!=null||(uu[Ln]=gb());var vb=hr(e=>{yr.hooks.callHook("devtoolsStateUpdated",{state:e})});hr((e,t)=>{yr.hooks.callHook("devtoolsConnectedUpdated",{state:e,oldState:t})});var Ei=new Proxy(Q.__VUE_DEVTOOLS_KIT_APP_RECORDS__,{get(e,t,n){return t==="value"?Q.__VUE_DEVTOOLS_KIT_APP_RECORDS__:Q.__VUE_DEVTOOLS_KIT_APP_RECORDS__[t]}}),it=new Proxy(Q.__VUE_DEVTOOLS_KIT_ACTIVE_APP_RECORD__,{get(e,t,n){return t==="value"?Q.__VUE_DEVTOOLS_KIT_ACTIVE_APP_RECORD__:t==="id"?Q.__VUE_DEVTOOLS_KIT_ACTIVE_APP_RECORD_ID__:Q.__VUE_DEVTOOLS_KIT_ACTIVE_APP_RECORD__[t]}});function Df(){vb({...Q[Ln],appRecords:Ei.value,activeAppRecordId:it.id,tabs:Q.__VUE_DEVTOOLS_KIT_CUSTOM_TABS__,commands:Q.__VUE_DEVTOOLS_KIT_CUSTOM_COMMANDS__})}function Eb(e){Q.__VUE_DEVTOOLS_KIT_ACTIVE_APP_RECORD__=e,Df()}function bb(e){Q.__VUE_DEVTOOLS_KIT_ACTIVE_APP_RECORD_ID__=e,Df()}var Ge=new Proxy(Q[Ln],{get(e,t){return t==="appRecords"?Ei:t==="activeAppRecordId"?it.id:t==="tabs"?Q.__VUE_DEVTOOLS_KIT_CUSTOM_TABS__:t==="commands"?Q.__VUE_DEVTOOLS_KIT_CUSTOM_COMMANDS__:Q[Ln][t]},deleteProperty(e,t){return delete e[t],!0},set(e,t,n){return{...Q[Ln]},e[t]=n,Q[Ln][t]=n,!0}});function yb(e={}){var t,n,r;const{file:o,host:i,baseUrl:s=window.location.origin,line:l=0,column:a=0}=e;if(o){if(i==="chrome-extension"){const u=o.replace(/\\/g,"\\\\"),d=(n=(t=window.VUE_DEVTOOLS_CONFIG)==null?void 0:t.openInEditorHost)!=null?n:"/";fetch(`${d}__open-in-editor?file=${encodeURI(o)}`).then(c=>{if(!c.ok){const f=`Opening component ${u} failed`;console.log(`%c${f}`,"color:red")}})}else if(Ge.vitePluginDetected){const u=(r=Q.__VUE_DEVTOOLS_OPEN_IN_EDITOR_BASE_URL__)!=null?r:s;Q.__VUE_INSPECTOR__.openInEditor(u,o,l,a)}}}z();z();z();z();z();var du,fu;(fu=(du=Q).__VUE_DEVTOOLS_KIT_PLUGIN_BUFFER__)!=null||(du.__VUE_DEVTOOLS_KIT_PLUGIN_BUFFER__=[]);var El=new Proxy(Q.__VUE_DEVTOOLS_KIT_PLUGIN_BUFFER__,{get(e,t,n){return Reflect.get(e,t,n)}});function Ss(e){const t={};return Object.keys(e).forEach(n=>{t[n]=e[n].defaultValue}),t}function bl(e){return`__VUE_DEVTOOLS_NEXT_PLUGIN_SETTINGS__${e}__`}function Tb(e){var t,n,r;const o=(n=(t=El.find(i=>{var s;return i[0].id===e&&!!((s=i[0])!=null&&s.settings)}))==null?void 0:t[0])!=null?n:null;return(r=o==null?void 0:o.settings)!=null?r:null}function Vf(e,t){var n,r,o;const i=bl(e);if(i){const s=localStorage.getItem(i);if(s)return JSON.parse(s)}if(e){const s=(r=(n=El.find(l=>l[0].id===e))==null?void 0:n[0])!=null?r:null;return Ss((o=s==null?void 0:s.settings)!=null?o:{})}return Ss(t)}function wb(e,t){const n=bl(e);localStorage.getItem(n)||localStorage.setItem(n,JSON.stringify(Ss(t)))}function Ab(e,t,n){const r=bl(e),o=localStorage.getItem(r),i=JSON.parse(o||"{}"),s={...i,[t]:n};localStorage.setItem(r,JSON.stringify(s)),yr.hooks.callHookWith(l=>{l.forEach(a=>a({pluginId:e,key:t,oldValue:i[t],newValue:n,settings:s}))},"setPluginSettings")}z();z();z();z();z();z();z();z();z();z();z();var pu,hu,mt=(hu=(pu=Q).__VUE_DEVTOOLS_HOOK)!=null?hu:pu.__VUE_DEVTOOLS_HOOK=yf(),Sb={vueAppInit(e){mt.hook("app:init",e)},vueAppUnmount(e){mt.hook("app:unmount",e)},vueAppConnected(e){mt.hook("app:connected",e)},componentAdded(e){return mt.hook("component:added",e)},componentEmit(e){return mt.hook("component:emit",e)},componentUpdated(e){return mt.hook("component:updated",e)},componentRemoved(e){return mt.hook("component:removed",e)},setupDevtoolsPlugin(e){mt.hook("devtools-plugin:setup",e)},perfStart(e){return mt.hook("perf:start",e)},perfEnd(e){return mt.hook("perf:end",e)}},Nf={on:Sb,setupDevToolsPlugin(e,t){return mt.callHook("devtools-plugin:setup",e,t)}},kb=class{constructor({plugin:e,ctx:t}){this.hooks=t.hooks,this.plugin=e}get on(){return{visitComponentTree:e=>{this.hooks.hook("visitComponentTree",e)},inspectComponent:e=>{this.hooks.hook("inspectComponent",e)},editComponentState:e=>{this.hooks.hook("editComponentState",e)},getInspectorTree:e=>{this.hooks.hook("getInspectorTree",e)},getInspectorState:e=>{this.hooks.hook("getInspectorState",e)},editInspectorState:e=>{this.hooks.hook("editInspectorState",e)},inspectTimelineEvent:e=>{this.hooks.hook("inspectTimelineEvent",e)},timelineCleared:e=>{this.hooks.hook("timelineCleared",e)},setPluginSettings:e=>{this.hooks.hook("setPluginSettings",e)}}}notifyComponentUpdate(e){var t;if(Ge.highPerfModeEnabled)return;const n=Rf().find(r=>r.packageName===this.plugin.descriptor.packageName);if(n!=null&&n.id){if(e){const r=[e.appContext.app,e.uid,(t=e.parent)==null?void 0:t.uid,e];mt.callHook("component:updated",...r)}else mt.callHook("component:updated");this.hooks.callHook("sendInspectorState",{inspectorId:n.id,plugin:this.plugin})}}addInspector(e){this.hooks.callHook("addInspector",{inspector:e,plugin:this.plugin}),this.plugin.descriptor.settings&&wb(e.id,this.plugin.descriptor.settings)}sendInspectorTree(e){Ge.highPerfModeEnabled||this.hooks.callHook("sendInspectorTree",{inspectorId:e,plugin:this.plugin})}sendInspectorState(e){Ge.highPerfModeEnabled||this.hooks.callHook("sendInspectorState",{inspectorId:e,plugin:this.plugin})}selectInspectorNode(e,t){this.hooks.callHook("customInspectorSelectNode",{inspectorId:e,nodeId:t,plugin:this.plugin})}visitComponentTree(e){return this.hooks.callHook("visitComponentTree",e)}now(){return Ge.highPerfModeEnabled?0:Date.now()}addTimelineLayer(e){this.hooks.callHook("timelineLayerAdded",{options:e,plugin:this.plugin})}addTimelineEvent(e){Ge.highPerfModeEnabled||this.hooks.callHook("timelineEventAdded",{options:e,plugin:this.plugin})}getSettings(e){return Vf(e??this.plugin.descriptor.id,this.plugin.descriptor.settings)}getComponentInstances(e){return this.hooks.callHook("getComponentInstances",{app:e})}getComponentBounds(e){return this.hooks.callHook("getComponentBounds",{instance:e})}getComponentName(e){return this.hooks.callHook("getComponentName",{instance:e})}highlightElement(e){const t=e.__VUE_DEVTOOLS_NEXT_UID__;return this.hooks.callHook("componentHighlight",{uid:t})}unhighlightElement(){return this.hooks.callHook("componentUnhighlight")}},Ob=kb;z();z();z();z();var Pb="__vue_devtool_undefined__",Cb="__vue_devtool_infinity__",xb="__vue_devtool_negative_infinity__",Ib="__vue_devtool_nan__";z();z();var Lb={[Pb]:"undefined",[Ib]:"NaN",[Cb]:"Infinity",[xb]:"-Infinity"};Object.entries(Lb).reduce((e,[t,n])=>(e[n]=t,e),{});z();z();z();z();z();var mu,_u;(_u=(mu=Q).__VUE_DEVTOOLS_KIT__REGISTERED_PLUGIN_APPS__)!=null||(mu.__VUE_DEVTOOLS_KIT__REGISTERED_PLUGIN_APPS__=new Set);function Rb(e,t){return Nf.setupDevToolsPlugin(e,t)}function Db(e,t){const[n,r]=e;if(n.app!==t)return;const o=new Ob({plugin:{setupFn:r,descriptor:n},ctx:yr});n.packageName==="vuex"&&o.on.editInspectorState(i=>{o.sendInspectorState(i.inspectorId)}),r(o)}function Mf(e,t){Q.__VUE_DEVTOOLS_KIT__REGISTERED_PLUGIN_APPS__.has(e)||Ge.highPerfModeEnabled&&!(t!=null&&t.inspectingComponent)||(Q.__VUE_DEVTOOLS_KIT__REGISTERED_PLUGIN_APPS__.add(e),El.forEach(n=>{Db(n,e)}))}z();z();var Qr="__VUE_DEVTOOLS_ROUTER__",mr="__VUE_DEVTOOLS_ROUTER_INFO__",gu,vu;(vu=(gu=Q)[mr])!=null||(gu[mr]={currentRoute:null,routes:[]});var Eu,bu;(bu=(Eu=Q)[Qr])!=null||(Eu[Qr]={});new Proxy(Q[mr],{get(e,t){return Q[mr][t]}});new Proxy(Q[Qr],{get(e,t){if(t==="value")return Q[Qr]}});function Vb(e){const t=new Map;return((e==null?void 0:e.getRoutes())||[]).filter(n=>!t.has(n.path)&&t.set(n.path,1))}function yl(e){return e.map(t=>{let{path:n,name:r,children:o,meta:i}=t;return o!=null&&o.length&&(o=yl(o)),{path:n,name:r,children:o,meta:i}})}function Nb(e){if(e){const{fullPath:t,hash:n,href:r,path:o,name:i,matched:s,params:l,query:a}=e;return{fullPath:t,hash:n,href:r,path:o,name:i,params:l,query:a,matched:yl(s)}}return e}function Mb(e,t){function n(){var r;const o=(r=e.app)==null?void 0:r.config.globalProperties.$router,i=Nb(o==null?void 0:o.currentRoute.value),s=yl(Vb(o)),l=console.warn;console.warn=()=>{},Q[mr]={currentRoute:i?Ua(i):{},routes:Ua(s)},Q[Qr]=o,console.warn=l}n(),Nf.on.componentUpdated(hr(()=>{var r;((r=t.value)==null?void 0:r.app)===e.app&&(n(),!Ge.highPerfModeEnabled&&yr.hooks.callHook("routerInfoUpdated",{state:Q[mr]}))},200))}function zb(e){return{async getInspectorTree(t){const n={...t,app:it.value.app,rootNodes:[]};return await new Promise(r=>{e.callHookWith(async o=>{await Promise.all(o.map(i=>i(n))),r()},"getInspectorTree")}),n.rootNodes},async getInspectorState(t){const n={...t,app:it.value.app,state:null},r={currentTab:`custom-inspector:${t.inspectorId}`};return await new Promise(o=>{e.callHookWith(async i=>{await Promise.all(i.map(s=>s(n,r))),o()},"getInspectorState")}),n.state},editInspectorState(t){const n=new ub,r={...t,app:it.value.app,set:(o,i=t.path,s=t.state.value,l)=>{n.set(o,i,s,l||n.createDefaultSetCallback(t.state))}};e.callHookWith(o=>{o.forEach(i=>i(r))},"editInspectorState")},sendInspectorState(t){const n=No(t);e.callHook("sendInspectorState",{inspectorId:t,plugin:{descriptor:n.descriptor,setupFn:()=>({})}})},inspectComponentInspector(){return rb()},cancelInspectComponentInspector(){return nb()},getComponentRenderCode(t){const n=Ts(it.value,t);if(n)return(n==null?void 0:n.type)instanceof Function?n.type.toString():n.render.toString()},scrollToComponent(t){return ob({id:t})},openInEditor:yb,getVueInspector:lb,toggleApp(t,n){const r=Ei.value.find(o=>o.id===t);r&&(bb(t),Eb(r),Mb(r,it),Lf(),Mf(r.app,n))},inspectDOM(t){const n=Ts(it.value,t);if(n){const[r]=hl(n);r&&(Q.__VUE_DEVTOOLS_INSPECT_DOM_TARGET__=r)}},updatePluginSettings(t,n,r){Ab(t,n,r)},getPluginSettings(t){return{options:Tb(t),values:Vf(t)}}}}z();var yu,Tu;(Tu=(yu=Q).__VUE_DEVTOOLS_ENV__)!=null||(yu.__VUE_DEVTOOLS_ENV__={vitePluginDetected:!1});var wu=_b(),Au,Su;(Su=(Au=Q).__VUE_DEVTOOLS_KIT_CONTEXT__)!=null||(Au.__VUE_DEVTOOLS_KIT_CONTEXT__={hooks:wu,get state(){return{...Ge,activeAppRecordId:it.id,activeAppRecord:it.value,appRecords:Ei.value}},api:zb(wu)});var yr=Q.__VUE_DEVTOOLS_KIT_CONTEXT__;z();zE(BE());var ku,Ou;(Ou=(ku=Q).__VUE_DEVTOOLS_NEXT_APP_RECORD_INFO__)!=null||(ku.__VUE_DEVTOOLS_NEXT_APP_RECORD_INFO__={id:0,appIds:new Set});z();function $b(e){Ge.highPerfModeEnabled=e??!Ge.highPerfModeEnabled,!e&&it.value&&Mf(it.value.app)}z();z();z();function Bb(e){Ge.devtoolsClientDetected={...Ge.devtoolsClientDetected,...e};const t=Object.values(Ge.devtoolsClientDetected).some(Boolean);$b(!t)}var Pu,Cu;(Cu=(Pu=Q).__VUE_DEVTOOLS_UPDATE_CLIENT_DETECTED__)!=null||(Pu.__VUE_DEVTOOLS_UPDATE_CLIENT_DETECTED__=Bb);z();z();z();z();z();z();z();var Fb=class{constructor(){this.keyToValue=new Map,this.valueToKey=new Map}set(e,t){this.keyToValue.set(e,t),this.valueToKey.set(t,e)}getByKey(e){return this.keyToValue.get(e)}getByValue(e){return this.valueToKey.get(e)}clear(){this.keyToValue.clear(),this.valueToKey.clear()}},zf=class{constructor(e){this.generateIdentifier=e,this.kv=new Fb}register(e,t){this.kv.getByValue(e)||(t||(t=this.generateIdentifier(e)),this.kv.set(t,e))}clear(){this.kv.clear()}getIdentifier(e){return this.kv.getByValue(e)}getValue(e){return this.kv.getByKey(e)}},Hb=class extends zf{constructor(){super(e=>e.name),this.classToAllowedProps=new Map}register(e,t){typeof t=="object"?(t.allowProps&&this.classToAllowedProps.set(e,t.allowProps),super.register(e,t.identifier)):super.register(e,t)}getAllowedProps(e){return this.classToAllowedProps.get(e)}};z();z();function jb(e){if("values"in Object)return Object.values(e);const t=[];for(const n in e)e.hasOwnProperty(n)&&t.push(e[n]);return t}function Ub(e,t){const n=jb(e);if("find"in n)return n.find(t);const r=n;for(let o=0;ot(r,n))}function Mo(e,t){return e.indexOf(t)!==-1}function xu(e,t){for(let n=0;nt.isApplicable(e))}findByName(e){return this.transfomers[e]}};z();z();var Wb=e=>Object.prototype.toString.call(e).slice(8,-1),$f=e=>typeof e>"u",Gb=e=>e===null,eo=e=>typeof e!="object"||e===null||e===Object.prototype?!1:Object.getPrototypeOf(e)===null?!0:Object.getPrototypeOf(e)===Object.prototype,ks=e=>eo(e)&&Object.keys(e).length===0,bn=e=>Array.isArray(e),qb=e=>typeof e=="string",Yb=e=>typeof e=="number"&&!isNaN(e),Xb=e=>typeof e=="boolean",Zb=e=>e instanceof RegExp,to=e=>e instanceof Map,no=e=>e instanceof Set,Bf=e=>Wb(e)==="Symbol",Jb=e=>e instanceof Date&&!isNaN(e.valueOf()),Qb=e=>e instanceof Error,Iu=e=>typeof e=="number"&&isNaN(e),e1=e=>Xb(e)||Gb(e)||$f(e)||Yb(e)||qb(e)||Bf(e),t1=e=>typeof e=="bigint",n1=e=>e===1/0||e===-1/0,r1=e=>ArrayBuffer.isView(e)&&!(e instanceof DataView),o1=e=>e instanceof URL;z();var Ff=e=>e.replace(/\./g,"\\."),Zi=e=>e.map(String).map(Ff).join("."),jr=e=>{const t=[];let n="";for(let o=0;onull,()=>{}),Vt(t1,"bigint",e=>e.toString(),e=>typeof BigInt<"u"?BigInt(e):(console.error("Please add a BigInt polyfill."),e)),Vt(Jb,"Date",e=>e.toISOString(),e=>new Date(e)),Vt(Qb,"Error",(e,t)=>{const n={name:e.name,message:e.message};return t.allowedErrorProps.forEach(r=>{n[r]=e[r]}),n},(e,t)=>{const n=new Error(e.message);return n.name=e.name,n.stack=e.stack,t.allowedErrorProps.forEach(r=>{n[r]=e[r]}),n}),Vt(Zb,"regexp",e=>""+e,e=>{const t=e.slice(1,e.lastIndexOf("/")),n=e.slice(e.lastIndexOf("/")+1);return new RegExp(t,n)}),Vt(no,"set",e=>[...e.values()],e=>new Set(e)),Vt(to,"map",e=>[...e.entries()],e=>new Map(e)),Vt(e=>Iu(e)||n1(e),"number",e=>Iu(e)?"NaN":e>0?"Infinity":"-Infinity",Number),Vt(e=>e===0&&1/e===-1/0,"number",()=>"-0",Number),Vt(o1,"URL",e=>e.toString(),e=>new URL(e))];function bi(e,t,n,r){return{isApplicable:e,annotation:t,transform:n,untransform:r}}var jf=bi((e,t)=>Bf(e)?!!t.symbolRegistry.getIdentifier(e):!1,(e,t)=>["symbol",t.symbolRegistry.getIdentifier(e)],e=>e.description,(e,t,n)=>{const r=n.symbolRegistry.getValue(t[1]);if(!r)throw new Error("Trying to deserialize unknown symbol");return r}),i1=[Int8Array,Uint8Array,Int16Array,Uint16Array,Int32Array,Uint32Array,Float32Array,Float64Array,Uint8ClampedArray].reduce((e,t)=>(e[t.name]=t,e),{}),Uf=bi(r1,e=>["typed-array",e.constructor.name],e=>[...e],(e,t)=>{const n=i1[t[1]];if(!n)throw new Error("Trying to deserialize unknown typed array");return new n(e)});function Kf(e,t){return e!=null&&e.constructor?!!t.classRegistry.getIdentifier(e.constructor):!1}var Wf=bi(Kf,(e,t)=>["class",t.classRegistry.getIdentifier(e.constructor)],(e,t)=>{const n=t.classRegistry.getAllowedProps(e.constructor);if(!n)return{...e};const r={};return n.forEach(o=>{r[o]=e[o]}),r},(e,t,n)=>{const r=n.classRegistry.getValue(t[1]);if(!r)throw new Error(`Trying to deserialize unknown class '${t[1]}' - check https://github.com/blitz-js/superjson/issues/116#issuecomment-773996564`);return Object.assign(Object.create(r.prototype),e)}),Gf=bi((e,t)=>!!t.customTransformerRegistry.findApplicable(e),(e,t)=>["custom",t.customTransformerRegistry.findApplicable(e).name],(e,t)=>t.customTransformerRegistry.findApplicable(e).serialize(e),(e,t,n)=>{const r=n.customTransformerRegistry.findByName(t[1]);if(!r)throw new Error("Trying to deserialize unknown custom value");return r.deserialize(e)}),s1=[Wf,jf,Gf,Uf],Lu=(e,t)=>{const n=xu(s1,o=>o.isApplicable(e,t));if(n)return{value:n.transform(e,t),type:n.annotation(e,t)};const r=xu(Hf,o=>o.isApplicable(e,t));if(r)return{value:r.transform(e,t),type:r.annotation}},qf={};Hf.forEach(e=>{qf[e.annotation]=e});var l1=(e,t,n)=>{if(bn(t))switch(t[0]){case"symbol":return jf.untransform(e,t,n);case"class":return Wf.untransform(e,t,n);case"custom":return Gf.untransform(e,t,n);case"typed-array":return Uf.untransform(e,t,n);default:throw new Error("Unknown transformation: "+t)}else{const r=qf[t];if(!r)throw new Error("Unknown transformation: "+t);return r.untransform(e,n)}};z();var Qn=(e,t)=>{if(t>e.size)throw new Error("index out of bounds");const n=e.keys();for(;t>0;)n.next(),t--;return n.next().value};function Yf(e){if(Mo(e,"__proto__"))throw new Error("__proto__ is not allowed as a property");if(Mo(e,"prototype"))throw new Error("prototype is not allowed as a property");if(Mo(e,"constructor"))throw new Error("constructor is not allowed as a property")}var a1=(e,t)=>{Yf(t);for(let n=0;n{if(Yf(t),t.length===0)return n(e);let r=e;for(let i=0;iPs(i,t,[...n,...jr(s)]));return}const[r,o]=e;o&&_r(o,(i,s)=>{Ps(i,t,[...n,...jr(s)])}),t(r,n)}function u1(e,t,n){return Ps(t,(r,o)=>{e=Os(e,o,i=>l1(i,r,n))}),e}function c1(e,t){function n(r,o){const i=a1(e,jr(o));r.map(jr).forEach(s=>{e=Os(e,s,()=>i)})}if(bn(t)){const[r,o]=t;r.forEach(i=>{e=Os(e,jr(i),()=>e)}),o&&_r(o,n)}else _r(t,n);return e}var d1=(e,t)=>eo(e)||bn(e)||to(e)||no(e)||Kf(e,t);function f1(e,t,n){const r=n.get(e);r?r.push(t):n.set(e,[t])}function p1(e,t){const n={};let r;return e.forEach(o=>{if(o.length<=1)return;t||(o=o.map(l=>l.map(String)).sort((l,a)=>l.length-a.length));const[i,...s]=o;i.length===0?r=s.map(Zi):n[Zi(i)]=s.map(Zi)}),r?ks(n)?[r]:[r,n]:ks(n)?void 0:n}var Xf=(e,t,n,r,o=[],i=[],s=new Map)=>{var l;const a=e1(e);if(!a){f1(e,o,t);const _=s.get(e);if(_)return r?{transformedValue:null}:_}if(!d1(e,n)){const _=Lu(e,n),g=_?{transformedValue:_.value,annotations:[_.type]}:{transformedValue:e};return a||s.set(e,g),g}if(Mo(i,e))return{transformedValue:null};const u=Lu(e,n),d=(l=u==null?void 0:u.value)!=null?l:e,c=bn(d)?[]:{},f={};_r(d,(_,g)=>{if(g==="__proto__"||g==="constructor"||g==="prototype")throw new Error(`Detected property ${g}. This is a prototype pollution risk, please remove it from your object.`);const v=Xf(_,t,n,r,[...o,g],[...i,e],s);c[g]=v.transformedValue,bn(v.annotations)?f[g]=v.annotations:eo(v.annotations)&&_r(v.annotations,(y,w)=>{f[Ff(g)+"."+w]=y})});const p=ks(f)?{transformedValue:c,annotations:u?[u.type]:void 0}:{transformedValue:c,annotations:u?[u.type,f]:f};return a||s.set(e,p),p};z();z();function Zf(e){return Object.prototype.toString.call(e).slice(8,-1)}function Ru(e){return Zf(e)==="Array"}function h1(e){if(Zf(e)!=="Object")return!1;const t=Object.getPrototypeOf(e);return!!t&&t.constructor===Object&&t===Object.prototype}function m1(e,t,n,r,o){const i={}.propertyIsEnumerable.call(r,t)?"enumerable":"nonenumerable";i==="enumerable"&&(e[t]=n),o&&i==="nonenumerable"&&Object.defineProperty(e,t,{value:n,enumerable:!1,writable:!0,configurable:!0})}function Cs(e,t={}){if(Ru(e))return e.map(o=>Cs(o,t));if(!h1(e))return e;const n=Object.getOwnPropertyNames(e),r=Object.getOwnPropertySymbols(e);return[...n,...r].reduce((o,i)=>{if(Ru(t.props)&&!t.props.includes(i))return o;const s=e[i],l=Cs(s,t);return m1(o,i,l,e,t.nonenumerable),o},{})}var Pe=class{constructor({dedupe:e=!1}={}){this.classRegistry=new Hb,this.symbolRegistry=new zf(t=>{var n;return(n=t.description)!=null?n:""}),this.customTransformerRegistry=new Kb,this.allowedErrorProps=[],this.dedupe=e}serialize(e){const t=new Map,n=Xf(e,t,this,this.dedupe),r={json:n.transformedValue};n.annotations&&(r.meta={...r.meta,values:n.annotations});const o=p1(t,this.dedupe);return o&&(r.meta={...r.meta,referentialEqualities:o}),r}deserialize(e){const{json:t,meta:n}=e;let r=Cs(t);return n!=null&&n.values&&(r=u1(r,n.values,this)),n!=null&&n.referentialEqualities&&(r=c1(r,n.referentialEqualities)),r}stringify(e){return JSON.stringify(this.serialize(e))}parse(e){return this.deserialize(JSON.parse(e))}registerClass(e,t){this.classRegistry.register(e,t)}registerSymbol(e,t){this.symbolRegistry.register(e,t)}registerCustom(e,t){this.customTransformerRegistry.register({name:t,...e})}allowErrorProps(...e){this.allowedErrorProps.push(...e)}};Pe.defaultInstance=new Pe;Pe.serialize=Pe.defaultInstance.serialize.bind(Pe.defaultInstance);Pe.deserialize=Pe.defaultInstance.deserialize.bind(Pe.defaultInstance);Pe.stringify=Pe.defaultInstance.stringify.bind(Pe.defaultInstance);Pe.parse=Pe.defaultInstance.parse.bind(Pe.defaultInstance);Pe.registerClass=Pe.defaultInstance.registerClass.bind(Pe.defaultInstance);Pe.registerSymbol=Pe.defaultInstance.registerSymbol.bind(Pe.defaultInstance);Pe.registerCustom=Pe.defaultInstance.registerCustom.bind(Pe.defaultInstance);Pe.allowErrorProps=Pe.defaultInstance.allowErrorProps.bind(Pe.defaultInstance);z();z();z();z();z();z();z();z();z();z();z();z();z();z();z();z();z();z();z();z();z();z();z();var Du,Vu;(Vu=(Du=Q).__VUE_DEVTOOLS_KIT_MESSAGE_CHANNELS__)!=null||(Du.__VUE_DEVTOOLS_KIT_MESSAGE_CHANNELS__=[]);var Nu,Mu;(Mu=(Nu=Q).__VUE_DEVTOOLS_KIT_RPC_CLIENT__)!=null||(Nu.__VUE_DEVTOOLS_KIT_RPC_CLIENT__=null);var zu,$u;($u=(zu=Q).__VUE_DEVTOOLS_KIT_RPC_SERVER__)!=null||(zu.__VUE_DEVTOOLS_KIT_RPC_SERVER__=null);var Bu,Fu;(Fu=(Bu=Q).__VUE_DEVTOOLS_KIT_VITE_RPC_CLIENT__)!=null||(Bu.__VUE_DEVTOOLS_KIT_VITE_RPC_CLIENT__=null);var Hu,ju;(ju=(Hu=Q).__VUE_DEVTOOLS_KIT_VITE_RPC_SERVER__)!=null||(Hu.__VUE_DEVTOOLS_KIT_VITE_RPC_SERVER__=null);var Uu,Ku;(Ku=(Uu=Q).__VUE_DEVTOOLS_KIT_BROADCAST_RPC_SERVER__)!=null||(Uu.__VUE_DEVTOOLS_KIT_BROADCAST_RPC_SERVER__=null);z();z();z();z();z();z();z();const _1=JSON.parse(`{"logo":"/images/hero.png","repo":"vuepress/ecosystem","docsDir":"docs","locales":{"/":{"navbar":[{"text":"Themes","prefix":"/themes/","icon":"palette","children":["guidelines","default/",{"text":"Hope Theme","icon":"https://theme-hope-assets.vuejs.press/logo.svg","link":"https://theme-hope.vuejs.press"},{"text":"Plume Theme","icon":"https://theme-plume.vuejs.press/favicon.ico","link":"https://theme-plume.vuejs.press"},{"text":"Reco Theme","icon":"https://theme-reco.vuejs.press/favicon.ico","link":"https://theme-reco.vuejs.press/en"}]},{"text":"Plugins","icon":"unplug","prefix":"/plugins/","children":["features/","markdown/","search/","blog/","pwa/","analytics/","seo/","development/","tools/"]},{"text":"Tools","icon":"hammer","prefix":"/tools/","children":["helper/"]}],"sidebar":{"/plugins/":[{"text":"Common Features","link":"features/"},{"text":"Markdown","link":"markdown/"},{"text":"Content Search","link":"search/"},{"text":"Blogging","link":"blog/"},{"text":"Analytics","link":"analytics/"},{"text":"SEO","link":"seo/"},{"text":"PWA","link":"pwa/"},{"text":"Theme Development","link":"development/"},{"text":"Tools","link":"tools/"}],"/plugins/analytics/":["baidu-analytics","google-analytics","umami-analytics"],"/plugins/blog/":[{"text":"Blog","icon":"la:blog","prefix":"blog/","link":"blog/","children":["guide","config"]},{"text":"Comment","icon":"message-circle-more","prefix":"comment/","link":"comment/","children":["guide","giscus/","waline/","artalk/","twikoo/"]},{"text":"Feed","icon":"rss","prefix":"feed/","link":"feed/","children":["guide","config","frontmatter","channel","getter"]}],"/plugins/development/":["active-header-links","git","palette","reading-time","rtl",{"text":"Sass Palette","icon":"palette","prefix":"sass-palette/","link":"sass-palette/","children":["guide","config"]},"theme-data","toc"],"/plugins/features/":["back-to-top","catalog","copy-code","copyright","icon","medium-zoom","notice","nprogress","photo-swipe","watermark"],"/plugins/markdown/":["append-date","markdown-container","markdown-ext","markdown-image","markdown-include","markdown-hint","markdown-math","markdown-stylize","markdown-tab","links-check","prismjs",{"text":"revealjs","icon":"presentation","prefix":"revealjs/","link":"revealjs/","children":["","demo","themes"]},"shiki"],"/plugins/pwa/":[{"text":"PWA","icon":"layout-grid","prefix":"pwa/","link":"pwa/","children":["guide","config"]},"/plugins/pwa/remove-pwa"],"/plugins/tools/":["cache","google-tag-manager","redirect","register-components"],"/plugins/search/":["guidelines","docsearch","search","slimsearch"],"/plugins/seo/":[{"text":"SEO","icon":"scan-search","prefix":"seo/","link":"seo/","children":["guide","config"]},{"text":"Sitemap","icon":"network","prefix":"sitemap/","link":"sitemap/","children":["guide","config","frontmatter"]}],"/themes/":["guidelines",{"text":"Default Theme","icon":"palette","prefix":"default/","link":"default/","children":["config","plugin","locale","frontmatter","components","markdown","styles","extending"]},{"text":"Hope Theme","icon":"https://theme-hope-assets.vuejs.press/logo.svg","link":"https://theme-hope.vuejs.press"},{"text":"Plume Theme","icon":"https://theme-plume.vuejs.press/favicon.ico","link":"https://theme-plume.vuejs.press"},{"text":"Reco Theme","icon":"https://theme-reco.vuejs.press/favicon.ico","link":"https://theme-reco.vuejs.press/en"}],"/tools/":[{"text":"@vuepress/helper","icon":"hammer","prefix":"helper/","link":"helper/","children":[{"text":"Node","icon":"nonicons:node-16","prefix":"node/","children":["bundler","locales","page"]},"client","shared","style"]}]},"editLinkText":"Edit this page on GitHub","selectLanguageName":"English"},"/zh/":{"navbar":[{"text":"主题","prefix":"/zh/themes/","icon":"palette","children":["guidelines","default/",{"text":"Hope 主题","icon":"https://theme-hope-assets.vuejs.press/logo.svg","link":"https://theme-hope.vuejs.press/zh/"},{"text":"Plume 主题","icon":"https://theme-plume.vuejs.press/favicon.ico","link":"https://theme-plume.vuejs.press"},{"text":"Reco 主题","icon":"https://theme-reco.vuejs.press/favicon.ico","link":"https://theme-reco.vuejs.press"}]},{"text":"插件","icon":"unplug","prefix":"/zh/plugins/","children":["features/","markdown/","search/","blog/","pwa/","analytics/","seo/","development/","tools/"]},{"text":"工具","icon":"hammer","prefix":"/zh/tools/","children":["helper/"]}],"selectLanguageName":"简体中文","selectLanguageText":"选择语言","selectLanguageAriaLabel":"选择语言","sidebar":{"/zh/plugins/":[{"text":"常用功能","link":"features/"},{"text":"Markdown","link":"markdown/"},{"text":"搜索","link":"search/"},{"text":"博客","link":"blog/"},{"text":"分析统计","link":"analytics/"},{"text":"搜索引擎优化","link":"seo/"},{"text":"渐进式应用","link":"pwa/"},{"text":"主题开发","link":"development/"},{"text":"工具","link":"tools/"}],"/zh/plugins/analytics/":["baidu-analytics","google-analytics","umami-analytics"],"/zh/plugins/blog/":[{"text":"博客","icon":"la:blog","prefix":"blog/","link":"blog/","children":["guide","config"]},{"text":"评论","icon":"message-circle-more","prefix":"comment/","link":"comment/","children":["guide","giscus/","waline/","artalk/","twikoo/"]},{"text":"Feed","icon":"rss","prefix":"feed/","link":"feed/","children":["guide","config","frontmatter","channel","getter"]}],"/zh/plugins/development/":["active-header-links","git","palette","reading-time","rtl",{"text":"Sass Palette","icon":"palette","prefix":"sass-palette/","link":"sass-palette/","children":["guide","config"]},"theme-data","toc"],"/zh/plugins/features/":["back-to-top","catalog","copy-code","copyright","icon","medium-zoom","notice","nprogress","photo-swipe","watermark"],"/zh/plugins/markdown/":["append-date","markdown-container","markdown-ext","markdown-image","markdown-include","markdown-hint","markdown-math","markdown-stylize","markdown-tab","links-check","prismjs",{"text":"revealjs","icon":"presentation","prefix":"revealjs/","link":"revealjs/","children":["","demo","themes"]},"shiki"],"/zh/plugins/pwa/":[{"text":"PWA","icon":"layout-grid","prefix":"pwa/","link":"pwa/","children":["guide","config"]},"/plugins/pwa/remove-pwa"],"/zh/plugins/tools/":["cache","google-tag-manager","redirect","register-components"],"/zh/plugins/search/":["guidelines","docsearch","search","slimsearch"],"/zh/plugins/seo/":[{"text":"搜索引擎增强","icon":"scan-search","prefix":"seo/","link":"seo/","children":["guide","config"]},{"text":"站点地图","icon":"network","prefix":"sitemap/","link":"sitemap/","children":["guide","config","frontmatter"]}],"/zh/themes/":["guidelines",{"text":"默认主题","icon":"palette","prefix":"default/","link":"default/","children":["config","plugin","locale","frontmatter","components","markdown","styles","extending"]},{"text":"Hope 主题","icon":"https://theme-hope-assets.vuejs.press/logo.svg","link":"https://theme-hope.vuejs.press/zh/"},{"text":"Plume 主题","icon":"https://theme-plume.vuejs.press/favicon.ico","link":"https://theme-plume.vuejs.press"},{"text":"Reco 主题","icon":"https://theme-reco.vuejs.press/favicon.ico","link":"https://theme-reco.vuejs.press"}],"/zh/tools/":[{"text":"@vuepress/helper","icon":"hammer","prefix":"helper/","link":"helper/","children":[{"text":"Node","icon":"nonicons:node-16","prefix":"node/","children":["bundler","locales","page"]},"client","shared","style"]}]},"editLinkText":"在 GitHub 上编辑此页","lastUpdatedText":"上次更新","contributorsText":"贡献者","tip":"提示","warning":"注意","danger":"警告","notFound":["这里什么都没有","我们怎么到这来了?","这是一个 404 页面","看起来我们进入了错误的链接"],"backToHome":"返回首页","openInNewWindow":"在新窗口打开","toggleColorMode":"切换颜色模式","toggleSidebar":"切换侧边栏"}},"colorMode":"auto","colorModeSwitch":true,"navbar":[],"selectLanguageText":"Languages","selectLanguageAriaLabel":"Select language","sidebar":"heading","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"}`),g1=ue(_1),Jf=()=>g1,Qf=Symbol(""),v1=()=>{const e=Ue(Qf);if(!e)throw new Error("useThemeLocaleData() is called without provider.");return e},E1=(e,t)=>{const{locales:n,...r}=e;return{...r,...n==null?void 0:n[t]}},b1="org.vuejs.vuepress.plugin-theme-data",ep="VuePress Theme Data",y1=ep,xo="org.vuejs.vuepress",T1="VuePress",Wu="client-data",Gu="Client Data",w1=Pt({enhance({app:e}){const t=Jf(),n=e._context.provides[Js],r=N(()=>E1(t.value,n.routeLocale.value));e.provide(Qf,r),Object.defineProperties(e.config.globalProperties,{$theme:{get(){return t.value}},$themeLocale:{get(){return r.value}}}),Rb({app:e,id:b1,label:ep,packageName:"@vuepress/plugin-theme-data",homepage:"https://vuepress.vuejs.org",logo:"https://vuepress.vuejs.org/images/hero.png",componentStateTypes:[y1]},o=>{o.on.inspectComponent(i=>{i.instanceData.state.push({type:"VuePress",key:"themeData",editable:!1,value:t.value},{type:"VuePress",key:"themeLocaleData",editable:!1,value:r.value})}),o.addInspector({id:xo,label:T1,icon:"article"}),o.on.getInspectorTree(i=>{var s;i.inspectorId===xo&&((s=i.rootNodes.find(l=>l.id===Wu))==null||s.children.push({id:"themeData",label:"themeData"},{id:"themeLocaleData",label:"themeLocaleData"}))}),o.on.getInspectorState(i=>{i.inspectorId===xo&&(i.nodeId===Wu&&i.state[Gu].push({key:"themeData",value:t.value},{key:"themeLocaleData",value:r.value}),["themeData","themeLocaleData"].includes(i.nodeId)&&(i.state={[Gu]:[{key:i.nodeId,value:i.nodeId==="themeData"?t.value:r.value}]}))}),Be([t,r],()=>{o.notifyComponentUpdate(),o.sendInspectorState(xo)})})}}),A1=Object.freeze(Object.defineProperty({__proto__:null,default:w1},Symbol.toStringTag,{value:"Module"})),S1=()=>Jf(),Xe=()=>v1(),tp=Symbol(""),k1=e=>{const t=(n=e.value)=>{const r=window.document.documentElement;r.dataset.theme=n?"dark":"light"};Ke(()=>{pr(e,t)}),Wn(()=>{t()})},Tl=()=>{const e=Ue(tp);if(!e)throw new Error("useDarkMode() is called without provider.");return e},O1=()=>{const e=Xe(),t=Vv(),n=mo("vuepress-color-scheme",e.value.colorMode),r=N({get(){return e.value.colorModeSwitch?n.value==="auto"?t.value:n.value==="dark":e.value.colorMode==="dark"},set(o){o===t.value?n.value="auto":n.value=o?"dark":"light"}});Nn(tp,r),k1(r)};let Ji=null,Pr=null;const P1={wait:()=>Ji,pending:()=>{Ji=new Promise(e=>{Pr=e})},resolve:()=>{Pr==null||Pr(),Ji=null,Pr=null}},np=()=>P1,gr=(e,t)=>{const{notFound:n,meta:r,path:o}=Jr(e,t);return n?{text:o,link:o}:{text:r.title||o,...r.icon?{icon:r.icon}:{},link:o}},lr=(e="",t="")=>cl(t)||uo(t)?t:`${xd(e)}${t}`,zo=ue([]),C1=()=>{const e=Ft(),t=Xe(),n=bt(),r=N(()=>n.value.sidebarDepth??t.value.sidebarDepth??2);e.beforeEach((i,s)=>{i.path!==s.path&&(zo.value=[])});const o=()=>{if(r.value<=0){zo.value=[];return}zo.value=T0({levels:[2,r.value+1],ignore:[".vp-badge"]})};Be(r,o),Ke(o)},x1=()=>zo,I1=e=>({text:e.title,link:e.link,children:wl(e.children)}),wl=e=>e?e.map(t=>I1(t)):[],rp=(e,t)=>[{text:e.title,children:wl(t)}],op=(e,t,n,r="")=>{const o=(i,s)=>{var a;const l=at(i)?gr(lr(s,i)):at(i.link)?{...i,link:gf(i.link)?gr(lr(s,i.link)).link:i.link}:i;if("children"in l)return{...l,children:l.children.map(u=>o(u,lr(s,l.prefix)))};if(l.link===n){const u=((a=t[0])==null?void 0:a.level)===1?t[0].children:t;return{...l,children:wl(u)}}return l};return e.map(i=>o(i,r))},L1=(e,t,n,r)=>{const o=ul(e).sort((i,s)=>s.length-i.length);for(const i of o)if(ei(decodeURI(r),i)){const s=e[i];return s?s==="heading"?rp(t,n):op(s,n,r,i):[]}return console.warn(`${decodeURI(r)} is missing sidebar config.`),[]},ip=Symbol("sidebarItems"),Al=()=>{const e=Ue(ip);if(!e)throw new Error("useSidebarItems() is called without provider.");return e},R1=(e,t,n,r,o)=>e===!1?[]:e==="heading"?rp(t,o):Array.isArray(e)?op(e,o,n,r):Mn(e)?L1(e,t,o,n):[],D1=()=>{const e=Xe(),t=bt(),n=Tn(),r=rn(),o=Ht(),i=x1(),s=N(()=>t.value.home?!1:t.value.sidebar??e.value.sidebar??"heading"),l=N(()=>R1(s.value,n.value,r.path,o.value,i.value));Nn(ip,l)},V1=fe({__name:"Badge",props:{type:{default:"tip"},text:{default:""},vertical:{default:void 0}},setup(e,{expose:t}){t();const n={};return Object.defineProperty(n,"__isScriptSetup",{enumerable:!1,value:!0}),n}}),Re=(e,t)=>{const n=e.__vccOpts||e;for(const[r,o]of t)n[r]=o;return n};function N1(e,t,n,r,o,i){return q(),se("span",{class:st(["vp-badge",n.type]),style:jn({verticalAlign:n.vertical})},[De(e.$slots,"default",{},()=>[Zt($e(n.text),1)])],6)}const M1=Re(V1,[["render",N1],["__file","Badge.vue"]]),z1=fe({__name:"VPHomeFeatures",setup(e,{expose:t}){t();const n=bt(),r=N(()=>n.value.features??[]),o={frontmatter:n,features:r};return Object.defineProperty(o,"__isScriptSetup",{enumerable:!1,value:!0}),o}}),$1={key:0,class:"vp-features"};function B1(e,t,n,r,o,i){return r.features.length?(q(),se("div",$1,[(q(!0),se(ye,null,gn(r.features,s=>(q(),se("div",{key:s.title,class:"vp-feature"},[de("h2",null,$e(s.title),1),de("p",null,$e(s.details),1)]))),128))])):Ve("",!0)}const F1=Re(z1,[["render",B1],["__file","VPHomeFeatures.vue"]]),H1=fe({__name:"VPHomeFooter",setup(e,{expose:t}){t();const n=bt(),r=N(()=>n.value.footer),o=N(()=>n.value.footerHtml),i={frontmatter:n,footer:r,footerHtml:o};return Object.defineProperty(i,"__isScriptSetup",{enumerable:!1,value:!0}),i}}),j1=["innerHTML"],U1=["textContent"];function K1(e,t,n,r,o,i){return r.footer?(q(),se(ye,{key:0},[r.footerHtml?(q(),se("div",{key:0,class:"vp-footer","vp-footer":"",innerHTML:r.footer},null,8,j1)):(q(),se("div",{key:1,class:"vp-footer","vp-footer":"",textContent:$e(r.footer)},null,8,U1))],64)):Ve("",!0)}const W1=Re(H1,[["render",K1],["__file","VPHomeFooter.vue"]]),G1=fe({__name:"VPAutoLink",props:{config:{}},setup(e,{expose:t}){t();const n={get AutoLink(){return uv}};return Object.defineProperty(n,"__isScriptSetup",{enumerable:!1,value:!0}),n}});function q1(e,t,n,r,o,i){const s=Fn("VPIcon");return q(),Se(r.AutoLink,{config:n.config},tm({before:Ne(()=>[De(e.$slots,"before",wi(Ro(n.config)),()=>[n.config.icon?(q(),Se(s,{key:0,class:"auto-link-icon",icon:n.config.icon},null,8,["icon"])):Ve("",!0)])]),after:Ne(()=>[De(e.$slots,"after",wi(Ro(n.config)))]),_:2},[e.$slots.default?{name:"default",fn:Ne(()=>[De(e.$slots,"default",wi(Ro(n.config)))]),key:"0"}:void 0]),1032,["config"])}const Gn=Re(G1,[["render",q1],["__file","VPAutoLink.vue"]]),Y1=Object.freeze(Object.defineProperty({__proto__:null,default:Gn},Symbol.toStringTag,{value:"Module"})),X1=fe({__name:"VPHomeHero",setup(e,{expose:t}){t();const n=bt(),r=tl(),o=Tl(),i=N(()=>n.value.heroText===null?null:n.value.heroText||r.value.title||"Hello"),s=N(()=>n.value.tagline===null?null:n.value.tagline||r.value.description||"Welcome to your VuePress site"),l=N(()=>o.value&&n.value.heroImageDark!==void 0?n.value.heroImageDark:n.value.heroImage),a=N(()=>n.value.heroAlt||i.value||"hero"),u=N(()=>n.value.heroHeight??280),d=N(()=>Array.isArray(n.value.actions)?n.value.actions.map(({type:p="primary",..._})=>({type:p,..._})):[]),f={frontmatter:n,siteLocale:r,isDarkMode:o,heroText:i,tagline:s,heroImage:l,heroAlt:a,heroHeight:u,actions:d,HomeHeroImage:()=>{if(!l.value)return null;const p=H("img",{class:"vp-hero-image",src:mi(l.value),alt:a.value,height:u.value});return n.value.heroImageDark===void 0?p:H(nl,()=>p)},VPAutoLink:Gn};return Object.defineProperty(f,"__isScriptSetup",{enumerable:!1,value:!0}),f}}),Z1={class:"vp-hero"},J1={key:0,id:"main-title"},Q1={key:1,class:"vp-hero-description"},ey={key:2,class:"vp-hero-actions"};function ty(e,t,n,r,o,i){return q(),se("header",Z1,[ae(r.HomeHeroImage),r.heroText?(q(),se("h1",J1,$e(r.heroText),1)):Ve("",!0),r.tagline?(q(),se("p",Q1,$e(r.tagline),1)):Ve("",!0),r.actions.length?(q(),se("p",ey,[(q(!0),se(ye,null,gn(r.actions,s=>(q(),Se(r.VPAutoLink,{key:s.text,class:st(["vp-hero-action-button",[s.type]]),config:s},null,8,["class","config"]))),128))])):Ve("",!0)])}const ny=Re(X1,[["render",ty],["__file","VPHomeHero.vue"]]),ry=fe({__name:"VPHome",setup(e,{expose:t}){t();const n={VPHomeFeatures:F1,VPHomeFooter:W1,VPHomeHero:ny,get Content(){return hi}};return Object.defineProperty(n,"__isScriptSetup",{enumerable:!1,value:!0}),n}}),oy={class:"vp-home"},iy={"vp-content":""};function sy(e,t,n,r,o,i){return q(),se("main",oy,[ae(r.VPHomeHero),ae(r.VPHomeFeatures),de("div",iy,[ae(r.Content)]),ae(r.VPHomeFooter)])}const ly=Re(ry,[["render",sy],["__file","VPHome.vue"]]),ay=fe({__name:"VPNavbarBrand",setup(e,{expose:t}){t();const n=Ht(),r=tl(),o=Xe(),i=Tl(),s=N(()=>o.value.home||n.value),l=N(()=>r.value.title),a=N(()=>i.value&&o.value.logoDark!==void 0?o.value.logoDark:o.value.logo),u=N(()=>o.value.logoAlt??l.value),d=N(()=>l.value.toLocaleUpperCase().trim()===u.value.toLocaleUpperCase().trim()),f={routeLocale:n,siteLocale:r,themeLocale:o,isDarkMode:i,navbarBrandLink:s,navbarBrandTitle:l,navbarBrandLogo:a,navbarBrandLogoAlt:u,navBarLogoAltMatchesTitle:d,NavbarBrandLogo:()=>{if(!a.value)return null;const p=H("img",{class:"vp-site-logo",src:mi(a.value),alt:u.value});return o.value.logoDark===void 0?p:H(nl,()=>p)},get RouteLink(){return hn}};return Object.defineProperty(f,"__isScriptSetup",{enumerable:!1,value:!0}),f}}),uy=["aria-hidden"];function cy(e,t,n,r,o,i){return q(),Se(r.RouteLink,{to:r.navbarBrandLink},{default:Ne(()=>[ae(r.NavbarBrandLogo),r.navbarBrandTitle?(q(),se("span",{key:0,class:st(["vp-site-name",{"vp-hide-mobile":r.navbarBrandLogo}]),"aria-hidden":r.navBarLogoAltMatchesTitle},$e(r.navbarBrandTitle),11,uy)):Ve("",!0)]),_:1},8,["to"])}const dy=Re(ay,[["render",cy],["__file","VPNavbarBrand.vue"]]),fy=fe({__name:"VPDropdownTransition",setup(e,{expose:t}){t();const o={setHeight:i=>{i.style.height=`${i.scrollHeight}px`},unsetHeight:i=>{i.style.height=""}};return Object.defineProperty(o,"__isScriptSetup",{enumerable:!1,value:!0}),o}});function py(e,t,n,r,o,i){return q(),Se(qs,{name:"vp-dropdown",onEnter:r.setHeight,onAfterEnter:r.unsetHeight,onBeforeLeave:r.setHeight},{default:Ne(()=>[De(e.$slots,"default")]),_:3})}const sp=Re(fy,[["render",py],["__file","VPDropdownTransition.vue"]]),hy=fe({__name:"VPNavbarDropdown",props:{config:{}},setup(e,{expose:t}){t();const n=e,{config:r}=kc(n),o=rn(),[i,s]=lf(!1),l=N(()=>r.value.ariaLabel||r.value.text),a=(c,f)=>f[f.length-1]===c,u=c=>{c.detail===0?s():s(!1)};Be(()=>o.path,()=>{s(!1)});const d={props:n,config:r,route:o,open:i,toggleOpen:s,dropdownAriaLabel:l,isLastItemOfArray:a,handleDropdown:u,VPAutoLink:Gn,VPDropdownTransition:sp};return Object.defineProperty(d,"__isScriptSetup",{enumerable:!1,value:!0}),d}}),my=["aria-label"],_y={class:"title"},gy=["aria-label"],vy={class:"title"},Ey={class:"vp-navbar-dropdown"},by={class:"vp-navbar-dropdown-subtitle"},yy={key:1},Ty={class:"vp-navbar-dropdown-subitem-wrapper"};function wy(e,t,n,r,o,i){const s=Fn("VPIcon");return q(),se("div",{class:st(["vp-navbar-dropdown-wrapper",{open:r.open}])},[de("button",{class:"vp-navbar-dropdown-title",type:"button","aria-label":r.dropdownAriaLabel,onClick:r.handleDropdown},[r.config.icon?(q(),Se(s,{key:0,icon:r.config.icon},null,8,["icon"])):Ve("",!0),de("span",_y,$e(r.config.text),1),t[1]||(t[1]=de("span",{class:"arrow down"},null,-1))],8,my),de("button",{class:"vp-navbar-dropdown-title-mobile",type:"button","aria-label":r.dropdownAriaLabel,onClick:t[0]||(t[0]=()=>r.toggleOpen())},[r.config.icon?(q(),Se(s,{key:0,icon:r.config.icon},null,8,["icon"])):Ve("",!0),de("span",vy,$e(r.config.text),1),de("span",{class:st(["arrow",r.open?"down":"right"])},null,2)],8,gy),ae(r.VPDropdownTransition,null,{default:Ne(()=>[qo(de("ul",Ey,[(q(!0),se(ye,null,gn(r.config.children,l=>(q(),se("li",{key:l.text,class:"vp-navbar-dropdown-item"},["children"in l?(q(),se(ye,{key:0},[de("h4",by,[l.link?(q(),Se(r.VPAutoLink,{key:0,config:l,onFocusout:()=>{r.isLastItemOfArray(l,r.config.children)&&l.children.length===0&&(r.open=!1)}},null,8,["config","onFocusout"])):(q(),se("span",yy,[r.config.icon?(q(),Se(s,{key:0,icon:r.config.icon},null,8,["icon"])):Ve("",!0),Zt($e(l.text),1)]))]),de("ul",Ty,[(q(!0),se(ye,null,gn(l.children,a=>(q(),se("li",{key:a.link,class:"vp-navbar-dropdown-subitem"},[ae(r.VPAutoLink,{config:a,onFocusout:()=>{r.isLastItemOfArray(a,l.children)&&r.isLastItemOfArray(l,r.config.children)&&r.toggleOpen(!1)}},null,8,["config","onFocusout"])]))),128))])],64)):(q(),Se(r.VPAutoLink,{key:1,config:l,onFocusout:()=>{r.isLastItemOfArray(l,r.config.children)&&r.toggleOpen(!1)}},null,8,["config","onFocusout"]))]))),128))],512),[[Jo,r.open]])]),_:1})],2)}const lp=Re(hy,[["render",wy],["__file","VPNavbarDropdown.vue"]]),Ay=Object.freeze(Object.defineProperty({__proto__:null,default:lp},Symbol.toStringTag,{value:"Module"})),ap=(e,t="")=>at(e)?gr(lr(t,e)):"children"in e?{...e,children:e.children.map(n=>ap(n,lr(t,e.prefix)))}:{...e,link:gf(e.link)?gr(lr(t,e.link)).link:e.link},Sy=()=>{const e=Xe();return N(()=>(e.value.navbar||[]).map(t=>ap(t)))},ky=()=>{const e=Xe(),t=Tn(),n=bt();return N(()=>{var o;return n.value.contributors??e.value.contributors??!0?((o=t.value.git)==null?void 0:o.contributors)??null:null})},up=e=>!co(e)||e.includes("github.com")?"GitHub":e.includes("bitbucket.org")?"Bitbucket":e.includes("gitlab.com")?"GitLab":e.includes("gitee.com")?"Gitee":null,Oy={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"},Py=({docsRepo:e,editLinkPattern:t})=>{if(t)return t;const n=up(e);return n!==null?Oy[n]:null},Cy=({docsRepo:e,docsBranch:t,docsDir:n,filePathRelative:r,editLinkPattern:o})=>{if(!r)return null;const i=Py({docsRepo:e,editLinkPattern:o});return i?i.replace(/:repo/,co(e)?e:`https://github.com/${e}`).replace(/:branch/,t).replace(/:path/,Ys(`${Id(n)}/${r}`)):null},xy=()=>{const e=Xe(),t=Tn(),n=bt();return N(()=>{if(!(n.value.editLink??e.value.editLink??!0))return null;const{repo:o,docsRepo:i=o,docsBranch:s="main",docsDir:l="",editLinkText:a}=e.value;if(!i)return null;const u=Cy({docsRepo:i,docsBranch:s,docsDir:l,filePathRelative:t.value.filePathRelative,editLinkPattern:n.value.editLinkPattern??e.value.editLinkPattern});return u?{text:a??"Edit this page",link:u}:null})},Iy=()=>{const e=Xe(),t=Tn(),n=bt();return N(()=>{var i;return!(n.value.lastUpdated??e.value.lastUpdated??!0)||!((i=t.value.git)!=null&&i.updatedTime)?null:new Date(t.value.git.updatedTime).toLocaleString()})},Ly=()=>{const e=Ft(),t=rn();return n=>{n&&(cl(n)?t.path!==n&&e.push(n):uo(n)?window.open(n):e.push(encodeURI(n)))}},Ry=()=>{const e=Xe(),t=N(()=>e.value.repo),n=N(()=>t.value?up(t.value):null),r=N(()=>t.value&&!co(t.value)?`https://github.com/${t.value}`:t.value),o=N(()=>r.value?e.value.repoLabel?e.value.repoLabel:n.value===null?"Source":n.value:null);return N(()=>!r.value||!o.value?[]:[{text:o.value,link:r.value}])},Dy=()=>{const e=rn(),t=w0(),n=Ht(),r=el(),o=tl(),i=S1(),s=Xe();return N(()=>{const l=Object.keys(r.value.locales);if(l.length<2)return[];const a=e.path,u=e.fullPath;return[{text:`${s.value.selectLanguageText}`,ariaLabel:`${s.value.selectLanguageAriaLabel??s.value.selectLanguageText}`,children:l.map(c=>{var y,w;const f=((y=r.value.locales)==null?void 0:y[c])??{},p=((w=i.value.locales)==null?void 0:w[c])??{},_=`${f.lang}`,g=p.selectLanguageName??_;if(_===o.value.lang)return{text:g,activeMatch:".",link:e.fullPath};const v=a.replace(n.value,c);return{text:g,link:t.value.some(h=>h===v)?u.replace(a,v):p.home??c}})}]})},qu=(e,t)=>e===!1?!1:at(e)?gr(e,t):Mn(e)?{...e,link:gr(e.link,t).link}:null,xs=(e,t,n)=>{const r=e.findIndex(i=>i.link===t);if(r!==-1){const i=e[r+n];return i?i.link?i:"prefix"in i&&!Jr(i.prefix).notFound?{...i,link:i.prefix}:null:null}for(const i of e)if("children"in i){const s=xs(i.children,t,n);if(s)return s}const o=e.findIndex(i=>"prefix"in i&&i.prefix===t);if(o!==-1){const i=e[o+n];return i?i.link?i:"prefix"in i&&!Jr(i.prefix).notFound?{...i,link:i.prefix}:null:null}return null},Vy=()=>{const e=bt(),t=Xe(),n=Al(),r=rn(),o=N(()=>{const s=qu(e.value.prev,r.path);return s===!1?null:s??(t.value.prev===!1?null:xs(n.value,r.path,-1))}),i=N(()=>{const s=qu(e.value.next,r.path);return s===!1?null:s??(t.value.next===!1?null:xs(n.value,r.path,1))});return{prevLink:o,nextLink:i}},Ny="719px",My={mobile:Ny};var ro;(function(e){e.Mobile="mobile"})(ro||(ro={}));const zy={[ro.Mobile]:Number.parseInt(My.mobile.replace("px",""),10)},cp=(e,t)=>{const n=zy[e];Number.isInteger(n)&&(He("orientationchange",()=>{t(n)},!1),He("resize",()=>{t(n)},!1),Ke(()=>{t(n)}))},Yu=e=>decodeURI(e).replace(/#.*$/,"").replace(/(index)?\.(md|html)$/,""),$y=(e,t)=>{if(t.hash===e)return!0;const n=Yu(t.path),r=Yu(e);return n===r},dp=(e,t)=>e.link&&$y(e.link,t)?!0:"children"in e?e.children.some(n=>dp(n,t)):!1,By=()=>{const e=Ry();return N(()=>[{...e.value[0],icon:"github"}])},Fy=()=>{const e=Dy();return N(()=>[{...e.value[0],icon:"languages"}])},Hy=fe({__name:"VPNavbarItems",setup(e,{expose:t}){t();const n=Sy(),r=Fy(),o=By(),i=ue(!1),s=N(()=>Xe().value.navbarLabel??"site navigation"),l=N(()=>[...n.value,...r.value,...o.value]);cp(ro.Mobile,u=>{i.value=window.innerWidth(q(),se("div",{key:s.text,class:"vp-navbar-item"},["children"in s?(q(),Se(r.VPNavbarDropdown,{key:0,class:st({mobile:r.isMobile}),config:s},null,8,["class","config"])):(q(),Se(r.VPAutoLink,{key:1,config:s},null,8,["config"]))]))),128))],8,jy)):Ve("",!0)}const fp=Re(Hy,[["render",Uy],["__file","VPNavbarItems.vue"]]),Ky={},Wy={class:"dark-icon",viewBox:"0 0 32 32"};function Gy(e,t){return q(),se("svg",Wy,t[0]||(t[0]=[de("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)]))}const qy=Re(Ky,[["render",Gy],["__file","VPDarkIcon.vue"]]),Yy={},Xy={class:"light-icon",viewBox:"0 0 32 32"};function Zy(e,t){return q(),se("svg",Xy,t[0]||(t[0]=[Cm('',9)]))}const Jy=Re(Yy,[["render",Zy],["__file","VPLightIcon.vue"]]),Qy=fe({__name:"VPToggleColorModeButton",setup(e,{expose:t}){t();const n=Xe(),r=Tl(),i={themeLocale:n,isDarkMode:r,toggleColorMode:()=>{r.value=!r.value},VPDarkIcon:qy,VPLightIcon:Jy};return Object.defineProperty(i,"__isScriptSetup",{enumerable:!1,value:!0}),i}}),eT=["title"];function tT(e,t,n,r,o,i){return q(),se("button",{type:"button",class:"vp-toggle-color-mode-button",title:r.themeLocale.toggleColorMode,onClick:r.toggleColorMode},[qo(ae(r.VPLightIcon,null,null,512),[[Jo,!r.isDarkMode]]),qo(ae(r.VPDarkIcon,null,null,512),[[Jo,r.isDarkMode]])],8,eT)}const nT=Re(Qy,[["render",tT],["__file","VPToggleColorModeButton.vue"]]),rT=fe({__name:"VPToggleSidebarButton",emits:["toggle"],setup(e,{expose:t}){t();const r={themeLocale:Xe()};return Object.defineProperty(r,"__isScriptSetup",{enumerable:!1,value:!0}),r}}),oT=["title"];function iT(e,t,n,r,o,i){return q(),se("div",{class:"vp-toggle-sidebar-button",title:r.themeLocale.toggleSidebar,"aria-expanded":"false",role:"button",tabindex:"0",onClick:t[0]||(t[0]=s=>e.$emit("toggle"))},t[1]||(t[1]=[de("div",{class:"icon","aria-hidden":"true"},[de("span"),de("span"),de("span")],-1)]),8,oT)}const sT=Re(rT,[["render",iT],["__file","VPToggleSidebarButton.vue"]]),lT=fe({__name:"VPNavbar",emits:["toggleSidebar"],setup(e,{expose:t}){t();const n=gi("SearchBox")?Fn("SearchBox"):()=>null,r=Xe(),o=Dl("navbar"),i=Dl("navbar-brand"),s=ue(0),l=N(()=>s.value?{maxWidth:`${s.value}px`}:{}),a=(d,c)=>{var _;const f=(_=d==null?void 0:d.ownerDocument.defaultView)==null?void 0:_.getComputedStyle(d,null)[c],p=Number.parseInt(f,10);return Number.isNaN(p)?0:p};cp(ro.Mobile,d=>{var f;const c=a(o.value,"paddingLeft")+a(o.value,"paddingRight");window.innerWidthe.$emit("toggleSidebar"))}),de("span",uT,[ae(r.VPNavbarBrand)],512),de("div",{class:"vp-navbar-items-wrapper",style:jn(r.linksWrapperStyle)},[De(e.$slots,"before"),ae(r.VPNavbarItems,{class:"vp-hide-mobile"}),De(e.$slots,"after"),r.themeLocale.colorModeSwitch?(q(),Se(r.VPToggleColorModeButton,{key:0})):Ve("",!0),ae(r.SearchBox)],4)],512)}const dT=Re(lT,[["render",cT],["__file","VPNavbar.vue"]]),fT={},pT={class:"edit-icon",viewBox:"0 0 1024 1024"};function hT(e,t){return q(),se("svg",pT,t[0]||(t[0]=[de("g",{fill:"currentColor"},[de("path",{d:"M430.818 653.65a60.46 60.46 0 0 1-50.96-93.281l71.69-114.012 7.773-10.365L816.038 80.138A60.46 60.46 0 0 1 859.225 62a60.46 60.46 0 0 1 43.186 18.138l43.186 43.186a60.46 60.46 0 0 1 0 86.373L588.879 565.55l-8.637 8.637-117.466 68.234a60.46 60.46 0 0 1-31.958 11.229z"}),de("path",{d:"M728.802 962H252.891A190.883 190.883 0 0 1 62.008 771.98V296.934a190.883 190.883 0 0 1 190.883-192.61h267.754a60.46 60.46 0 0 1 0 120.92H252.891a69.962 69.962 0 0 0-69.098 69.099V771.98a69.962 69.962 0 0 0 69.098 69.098h475.911A69.962 69.962 0 0 0 797.9 771.98V503.363a60.46 60.46 0 1 1 120.922 0V771.98A190.883 190.883 0 0 1 728.802 962z"})],-1)]))}const mT=Re(fT,[["render",hT],["__file","VPEditIcon.vue"]]),_T=fe({__name:"VPPageMeta",setup(e,{expose:t}){t();const n=Xe(),r=xy(),o=Iy(),i=ky(),s={themeLocale:n,editLink:r,lastUpdated:o,contributors:i,VPAutoLink:Gn,VPEditIcon:mT};return Object.defineProperty(s,"__isScriptSetup",{enumerable:!1,value:!0}),s}}),gT={class:"vp-page-meta"},vT={key:0,class:"vp-meta-item edit-link"},ET={class:"vp-meta-item git-info"},bT={key:0,class:"vp-meta-item last-updated"},yT={class:"meta-item-label"},TT={class:"meta-item-info"},wT={key:1,class:"vp-meta-item contributors"},AT={class:"meta-item-label"},ST={class:"meta-item-info"},kT=["title"];function OT(e,t,n,r,o,i){const s=Fn("ClientOnly");return q(),se("footer",gT,[r.editLink?(q(),se("div",vT,[ae(r.VPAutoLink,{class:"label",config:r.editLink},{before:Ne(()=>[ae(r.VPEditIcon)]),_:1},8,["config"])])):Ve("",!0),de("div",ET,[r.lastUpdated?(q(),se("div",bT,[de("span",yT,$e(r.themeLocale.lastUpdatedText)+": ",1),ae(s,null,{default:Ne(()=>[de("span",TT,$e(r.lastUpdated),1)]),_:1})])):Ve("",!0),r.contributors&&r.contributors.length?(q(),se("div",wT,[de("span",AT,$e(r.themeLocale.contributorsText)+": ",1),de("span",ST,[(q(!0),se(ye,null,gn(r.contributors,(l,a)=>(q(),se(ye,{key:a},[de("span",{class:"contributor",title:`email: ${l.email}`},$e(l.name),9,kT),a!==r.contributors.length-1?(q(),se(ye,{key:0},[Zt(", ")],64)):Ve("",!0)],64))),128))])])):Ve("",!0)])])}const PT=Re(_T,[["render",OT],["__file","VPPageMeta.vue"]]),CT=fe({__name:"VPPageNav",setup(e,{expose:t}){t();const n=Xe(),r=Ly(),{prevLink:o,nextLink:i}=Vy(),s=N(()=>n.value.pageNavbarLabel??"page navigation");He("keydown",a=>{a.altKey&&(a.key==="ArrowRight"?i.value&&(r(i.value.link),a.preventDefault()):a.key==="ArrowLeft"&&o.value&&(r(o.value.link),a.preventDefault()))});const l={themeLocale:n,navigate:r,prevLink:o,nextLink:i,navbarLabel:s,VPAutoLink:Gn};return Object.defineProperty(l,"__isScriptSetup",{enumerable:!1,value:!0}),l}}),xT=["aria-label"],IT={class:"hint"},LT={class:"link"},RT={class:"hint"},DT={class:"link"};function VT(e,t,n,r,o,i){return r.prevLink||r.nextLink?(q(),se("nav",{key:0,class:"vp-page-nav","aria-label":r.navbarLabel},[r.prevLink?(q(),Se(r.VPAutoLink,{key:0,class:"prev",config:r.prevLink},{default:Ne(()=>[de("div",IT,[t[0]||(t[0]=de("span",{class:"arrow left"},null,-1)),Zt(" "+$e(r.themeLocale.prev??"Prev"),1)]),de("div",LT,[de("span",null,$e(r.prevLink.text),1)])]),_:1},8,["config"])):Ve("",!0),r.nextLink?(q(),Se(r.VPAutoLink,{key:1,class:"next",config:r.nextLink},{default:Ne(()=>[de("div",RT,[Zt($e(r.themeLocale.next??"Next")+" ",1),t[1]||(t[1]=de("span",{class:"arrow right"},null,-1))]),de("div",DT,[de("span",null,$e(r.nextLink.text),1)])]),_:1},8,["config"])):Ve("",!0)],8,xT)):Ve("",!0)}const NT=Re(CT,[["render",VT],["__file","VPPageNav.vue"]]),MT=fe({__name:"VPPage",setup(e,{expose:t}){t(),C1();const n={VPPageMeta:PT,VPPageNav:NT,get Content(){return hi}};return Object.defineProperty(n,"__isScriptSetup",{enumerable:!1,value:!0}),n}}),zT={class:"vp-page"},$T={"vp-content":""};function BT(e,t,n,r,o,i){return q(),se("main",zT,[De(e.$slots,"top"),de("div",$T,[De(e.$slots,"content-top"),ae(r.Content),De(e.$slots,"content-bottom")]),ae(r.VPPageMeta),ae(r.VPPageNav),De(e.$slots,"bottom")])}const FT=Re(MT,[["render",BT],["__file","VPPage.vue"]]),HT=fe({__name:"VPSidebarItem",props:{item:{},depth:{default:0}},setup(e,{expose:t}){t();const n=e,{item:r,depth:o}=kc(n),i=rn(),s=Ft(),l=N(()=>"collapsible"in r.value&&r.value.collapsible),a=N(()=>dp(r.value,i)),u=N(()=>({"vp-sidebar-item":!0,"vp-sidebar-heading":o.value===0,active:a.value,collapsible:l.value})),d=N(()=>l.value?a.value:!0),[c,f]=lf(d.value),p=v=>{l.value&&(v.preventDefault(),f())},_=s.afterEach(()=>{Kn(()=>{c.value=d.value})});ui(()=>{_()});const g={props:n,item:r,depth:o,route:i,router:s,collapsible:l,isActive:a,itemClass:u,isOpenDefault:d,isOpen:c,toggleIsOpen:f,onClick:p,unregisterRouterHook:_,VPAutoLink:Gn,VPDropdownTransition:sp};return Object.defineProperty(g,"__isScriptSetup",{enumerable:!1,value:!0}),g}}),jT={class:"vp-sidebar-children"};function UT(e,t,n,r,o,i){const s=Fn("VPIcon"),l=Fn("VPSidebarItem",!0);return q(),se("li",null,[r.item.link?(q(),Se(r.VPAutoLink,{key:0,class:st(r.itemClass),config:r.item},null,8,["class","config"])):(q(),se("p",{key:1,tabindex:"0",class:st(r.itemClass),onClick:r.onClick,onKeydown:__(r.onClick,["enter"])},[r.item.icon?(q(),Se(s,{key:0,icon:r.item.icon},null,8,["icon"])):Ve("",!0),Zt(" "+$e(r.item.text)+" ",1),r.collapsible?(q(),se("span",{key:1,class:st(["arrow",r.isOpen?"down":"right"])},null,2)):Ve("",!0)],34)),"children"in r.item&&r.item.children.length?(q(),Se(r.VPDropdownTransition,{key:2},{default:Ne(()=>[qo(de("ul",jT,[(q(!0),se(ye,null,gn(r.item.children,a=>(q(),Se(l,{key:`${r.depth}${a.text}${a.link}`,item:a,depth:r.depth+1},null,8,["item","depth"]))),128))],512),[[Jo,r.isOpen]])]),_:1})):Ve("",!0)])}const pp=Re(HT,[["render",UT],["__file","VPSidebarItem.vue"]]),KT=Object.freeze(Object.defineProperty({__proto__:null,default:pp},Symbol.toStringTag,{value:"Module"})),WT=fe({__name:"VPSidebarItems",setup(e,{expose:t}){t();const n=rn(),r=Al();Ke(()=>{Be(()=>n.hash,i=>{const s=document.querySelector(".vp-sidebar");if(!s)return;const l=document.querySelector(`.vp-sidebar a.vp-sidebar-item[href="${n.path}${i}"]`);if(!l)return;const{top:a,height:u}=s.getBoundingClientRect(),{top:d,height:c}=l.getBoundingClientRect();da+u&&l.scrollIntoView(!1)})});const o={route:n,sidebarItems:r,VPSidebarItem:pp};return Object.defineProperty(o,"__isScriptSetup",{enumerable:!1,value:!0}),o}}),GT={key:0,class:"vp-sidebar-items"};function qT(e,t,n,r,o,i){return r.sidebarItems.length?(q(),se("ul",GT,[(q(!0),se(ye,null,gn(r.sidebarItems,s=>(q(),Se(r.VPSidebarItem,{key:`${s.text}${s.link}`,item:s},null,8,["item"]))),128))])):Ve("",!0)}const YT=Re(WT,[["render",qT],["__file","VPSidebarItems.vue"]]),XT=fe({__name:"VPSidebar",setup(e,{expose:t}){t();const n={VPNavbarItems:fp,VPSidebarItems:YT};return Object.defineProperty(n,"__isScriptSetup",{enumerable:!1,value:!0}),n}}),ZT={class:"vp-sidebar","vp-sidebar":""};function JT(e,t,n,r,o,i){return q(),se("aside",ZT,[ae(r.VPNavbarItems),De(e.$slots,"top"),ae(r.VPSidebarItems),De(e.$slots,"bottom")])}const QT=Re(XT,[["render",JT],["__file","VPSidebar.vue"]]),ew=fe({__name:"Layout",setup(e,{expose:t}){t();const n=Tn(),r=bt(),o=Xe(),i=N(()=>r.value.navbar!==!1&&o.value.navbar!==!1),s=Al(),l=ue(!1),a=h=>{l.value=typeof h=="boolean"?h:!l.value},u={x:0,y:0},d=h=>{u.x=h.changedTouches[0].clientX,u.y=h.changedTouches[0].clientY},c=h=>{const T=h.changedTouches[0].clientX-u.x,R=h.changedTouches[0].clientY-u.y;Math.abs(T)>Math.abs(R)&&Math.abs(T)>40&&(T>0&&u.x<=80?a(!0):a(!1))},f=N(()=>r.value.externalLinkIcon??o.value.externalLinkIcon??!0),p=N(()=>[{"no-navbar":!i.value,"no-sidebar":!s.value.length,"sidebar-open":l.value,"external-link-icon":f.value},r.value.pageClass]);let _;Ke(()=>{_=Ft().afterEach(()=>{a(!1)})}),Wn(()=>{_()});const g=np(),v=g.resolve,y=g.pending,w={page:n,frontmatter:r,themeLocale:o,shouldShowNavbar:i,sidebarItems:s,isSidebarOpen:l,toggleSidebar:a,touchStart:u,onTouchStart:d,onTouchEnd:c,enableExternalLinkIcon:f,containerClass:p,get unregisterRouterHook(){return _},set unregisterRouterHook(h){_=h},scrollPromise:g,onBeforeEnter:v,onBeforeLeave:y,VPHome:ly,VPNavbar:dT,VPPage:FT,VPSidebar:QT};return Object.defineProperty(w,"__isScriptSetup",{enumerable:!1,value:!0}),w}});function tw(e,t,n,r,o,i){return q(),se("div",{class:st(["vp-theme-container",r.containerClass]),"vp-container":"",onTouchstart:r.onTouchStart,onTouchend:r.onTouchEnd},[De(e.$slots,"navbar",{},()=>[r.shouldShowNavbar?(q(),Se(r.VPNavbar,{key:0,onToggleSidebar:r.toggleSidebar},{before:Ne(()=>[De(e.$slots,"navbar-before")]),after:Ne(()=>[De(e.$slots,"navbar-after")]),_:3})):Ve("",!0)]),de("div",{class:"vp-sidebar-mask",onClick:t[0]||(t[0]=s=>r.toggleSidebar(!1))}),De(e.$slots,"sidebar",{},()=>[ae(r.VPSidebar,null,{top:Ne(()=>[De(e.$slots,"sidebar-top")]),bottom:Ne(()=>[De(e.$slots,"sidebar-bottom")]),_:3})]),De(e.$slots,"page",{},()=>[r.frontmatter.home?(q(),Se(r.VPHome,{key:0})):(q(),Se(qs,{key:1,name:"fade-slide-y",mode:"out-in",onBeforeEnter:r.onBeforeEnter,onBeforeLeave:r.onBeforeLeave},{default:Ne(()=>[(q(),Se(r.VPPage,{key:r.page.path},{top:Ne(()=>[De(e.$slots,"page-top")]),"content-top":Ne(()=>[De(e.$slots,"page-content-top")]),"content-bottom":Ne(()=>[De(e.$slots,"page-content-bottom")]),bottom:Ne(()=>[De(e.$slots,"page-bottom")]),_:3}))]),_:3},8,["onBeforeEnter","onBeforeLeave"]))])],34)}const hp=Re(ew,[["render",tw],["__file","Layout.vue"]]),nw=fe({__name:"NotFound",setup(e,{expose:t}){t();const n=Ht(),r=Xe(),o=r.value.notFound??["Not Found"],i=()=>o[Math.floor(Math.random()*o.length)],s=r.value.home??n.value,l=r.value.backToHome??"Back to home",a={routeLocale:n,themeLocale:r,messages:o,getMsg:i,homeLink:s,homeText:l,get RouteLink(){return hn}};return Object.defineProperty(a,"__isScriptSetup",{enumerable:!1,value:!0}),a}}),rw={class:"vp-theme-container","vp-container":""},ow={class:"page"},iw={"vp-content":""};function sw(e,t,n,r,o,i){return q(),se("div",rw,[de("main",ow,[de("div",iw,[t[0]||(t[0]=de("h1",null,"404",-1)),de("blockquote",null,$e(r.getMsg()),1),ae(r.RouteLink,{to:r.homeLink},{default:Ne(()=>[Zt($e(r.homeText),1)]),_:1},8,["to"])])])])}const lw=Re(nw,[["render",sw],["__scopeId","data-v-491e75a2"],["__file","NotFound.vue"]]),aw=Pt({enhance({app:e,router:t}){gi("Badge")||e.component("Badge",M1);const n=t.options.scrollBehavior;t.options.scrollBehavior=async(...r)=>(await np().wait(),n(...r))},setup(){O1(),D1()},layouts:{Layout:hp,NotFound:lw}}),uw=Object.freeze(Object.defineProperty({__proto__:null,default:aw},Symbol.toStringTag,{value:"Module"}));let cw=e=>at(e.title)?{title:e.title}:null;const mp=Symbol(""),dw=()=>Ue(mp),fw=e=>{e.provide(mp,cw)};var pw={"/":{title:"Catalog",empty:"No catalog"},"/zh/":{title:"目录",empty:"暂无目录"}},hw=fe({name:"Catalog",props:{base:{type:String,default:""},level:{type:Number,default:3},index:Boolean,hideHeading:Boolean},setup(e){const t=dw(),n=ll(pw),r=Tn(),o=tf(),i=el(),s=ct(al(o.value).map(([a,{meta:u}])=>{const d=t(u);if(!d)return null;const c=a.split("/").length;return{level:k0(a,"/")?c-2:c-1,base:a.replace(/\/[^/]+\/?$/,"/"),path:a,...d}}).filter(a=>Mn(a)&&at(a.title))),l=N(()=>{const a=e.base?R_(xd(e.base)):r.value.path.replace(/\/[^/]+$/,"/"),u=a.split("/").length-2,d=[];return s.value.filter(({level:c,path:f})=>{if(!ei(f,a)||f===a)return!1;if(a==="/"){const p=ul(i.value.locales).filter(_=>_!=="/");if(f==="/404.html"||p.some(_=>ei(f,_)))return!1}return c-u<=e.level}).sort(({title:c,level:f,order:p},{title:_,level:g,order:v})=>f-g||(Wi(p)?Wi(v)?p>0?v>0?p-v:-1:v<0?p-v:1:p:Wi(v)?v:c.localeCompare(_))).forEach(c=>{var _;const{base:f,level:p}=c;switch(p-u){case 1:{d.push(c);break}case 2:{const g=d.find(v=>v.path===f);g&&(g.children??(g.children=[])).push(c);break}default:{const g=d.find(v=>v.path===f.replace(/\/[^/]+\/$/,"/"));if(g){const v=(_=g.children)==null?void 0:_.find(y=>y.path===f);v&&(v.children??(v.children=[])).push(c)}}}}),d});return()=>{const a=l.value.some(u=>u.children);return H("div",{class:["vp-catalog",{index:e.index}]},[e.hideHeading?null:H("h2",{class:"vp-catalog-main-title"},n.value.title),l.value.length?H(e.index?"ol":"ul",{class:["vp-catalog-list",{deep:a}]},l.value.map(({children:u=[],title:d,path:c,content:f})=>{const p=H(hn,{class:"vp-catalog-title",to:c},()=>f?H(f):d);return H("li",{class:"vp-catalog-item"},a?[H("h3",{id:d,class:["vp-catalog-child-title",{"has-children":u.length}]},[H("a",{href:`#${d}`,class:"vp-catalog-header-anchor","aria-hidden":!0},"#"),p]),u.length?H(e.index?"ol":"ul",{class:"vp-child-catalogs"},u.map(({children:_=[],content:g,path:v,title:y})=>H("li",{class:"vp-child-catalog"},[H("div",{class:["vp-catalog-sub-title",{"has-children":_.length}]},[H("a",{href:`#${y}`,class:"vp-catalog-header-anchor"},"#"),H(hn,{class:"vp-catalog-title",to:v},()=>g?H(g):y)]),_.length?H(e.index?"ol":"div",{class:e.index?"vp-sub-catalogs":"vp-sub-catalogs-wrapper"},_.map(({content:w,path:h,title:T})=>e.index?H("li",{class:"vp-sub-catalog"},H(hn,{to:h},()=>w?H(w):T)):H(hn,{class:"vp-sub-catalog-link",to:h},()=>w?H(w):T))):null]))):null]:H("div",{class:"vp-catalog-child-title"},p))})):H("p",{class:"vp-empty-catalog"},n.value.empty)])}}}),mw=Pt({enhance:({app:e})=>{fw(e),gi("Catalog",e)||e.component("Catalog",hw)}});const _w=Object.freeze(Object.defineProperty({__proto__:null,default:mw},Symbol.toStringTag,{value:"Module"}));var gw={provider:"Giscus"};const $o=gw,Bo=ue($o),_p=Symbol(""),vw=e=>{Me(e)?pr(e,t=>{Bo.value={...$o,...t}}):Ld(e)?pr(N(e),t=>{Bo.value={...$o,...t}}):Bo.value={...$o,...e}},gp=()=>Ue(_p),Ew=vw,bw=gp,yw=e=>{e.provide(_p,Un(Bo))},Xu=["ar","ca","da","de","en","eo","es","fa","fr","he","id","it","ja","ko","nl","pl","pt","ro","ru","th","tr","uk","uz","vi","zh-CN","zh-TW"];var Tw=fe({name:"GiscusComment",props:{identifier:{type:String,required:!0},darkmode:Boolean},setup(e){const t=bw(),n=Qs(),r=N(()=>!!(t.value.repo&&t.value.repoId&&t.value.category&&t.value.categoryId)),o=ue(!1),i=N(()=>{if(Xu.includes(n.value))return n.value;const l=n.value.split("-")[0];return Xu.includes(l)?l:"en"}),s=N(()=>({repo:t.value.repo,repoId:t.value.repoId,category:t.value.category,categoryId:t.value.categoryId,lang:i.value,theme:e.darkmode?t.value.darkTheme||"dark":t.value.lightTheme||"light",mapping:t.value.mapping||"pathname",term:e.identifier,inputPosition:t.value.inputPosition||"top",reactionsEnabled:t.value.reactionsEnabled===!1?"0":"1",strict:t.value.strict===!1?"0":"1",loading:t.value.lazyLoading===!1?"eager":"lazy",emitMetadata:"0"}));return Ke(async()=>{await b(()=>import("./giscus-B3IT9xos.js"),[]),o.value=!0}),()=>r.value?H("div",{id:"comment",class:["giscus-wrapper",{"input-top":t.value.inputPosition!=="bottom"}]},o.value?H("giscus-widget",s.value):H(il)):null}}),ww=fe({name:"CommentService",props:{darkmode:Boolean},setup(e){const t=gp(),n=Tn(),r=bt(),o=N(()=>r.value.comment??t.value.comment!==!1);return()=>H(Tw,{class:"vp-comment","vp-comment":"",identifier:r.value.commentID??n.value.path,darkmode:e.darkmode,style:{display:o.value?"block":"none"}})}}),Aw=Pt({enhance:({app:e})=>{yw(e),e.component("CommentService",ww)}});const Sw=Object.freeze(Object.defineProperty({__proto__:null,default:Aw},Symbol.toStringTag,{value:"Module"}));function kw(e,t,n){var r,o,i;n===void 0&&(n={});var s=(r=n.isImmediate)!=null&&r,l=(o=n.callback)!=null&&o,a=n.maxWait,u=Date.now(),d=[];function c(){if(a!==void 0){var p=Date.now()-u;if(p+t>=a)return a-p}return t}var f=function(){var p=[].slice.call(arguments),_=this;return new Promise(function(g,v){var y=s&&i===void 0;if(i!==void 0&&clearTimeout(i),i=setTimeout(function(){if(i=void 0,u=Date.now(),!s){var h=e.apply(_,p);l&&l(h),d.forEach(function(T){return(0,T.resolve)(h)}),d=[]}},c()),y){var w=e.apply(_,p);return l&&l(w),g(w)}d.push({resolve:g,reject:v})})};return f.cancel=function(p){i!==void 0&&clearTimeout(i),d.forEach(function(_){return(0,_.reject)(p)}),d=[]},f}var Ow={locales:{"/":{},"/zh/":{placeholder:"搜索文档",translations:{button:{buttonText:"搜索文档",buttonAriaLabel:"搜索文档"},modal:{searchBox:{resetButtonTitle:"清除查询条件",resetButtonAriaLabel:"清除查询条件",cancelButtonText:"取消",cancelButtonAriaLabel:"取消"},startScreen:{recentSearchesTitle:"搜索历史",noRecentSearchesText:"没有搜索历史",saveRecentSearchButtonTitle:"保存至搜索历史",removeRecentSearchButtonTitle:"从搜索历史中移除",favoriteSearchesTitle:"收藏",removeFavoriteSearchButtonTitle:"从收藏中移除"},errorScreen:{titleText:"无法获取结果",helpText:"你可能需要检查你的网络连接"},footer:{selectText:"选择",navigateText:"切换",closeText:"关闭",searchByText:"搜索提供者"},noResultsScreen:{noResultsText:"无法找到相关结果",suggestedQueryText:"你可以尝试查询",reportMissingResultsText:"你认为该查询应该有结果?",reportMissingResultsLinkText:"点击反馈"}}}}}};const Pw=e=>{const t=He("keydown",n=>{const r=n.key==="k"&&(n.ctrlKey||n.metaKey);n.key!=="/"&&!r||(n.preventDefault(),e(),t())})},Cw=e=>e.button===1||e.altKey||e.ctrlKey||e.metaKey||e.shiftKey,xw=()=>{const e=Ft();return{transformItems:t=>t.map(n=>({...n,url:`/ecosystem/${Ys(O_(n.url,"/ecosystem/"))}`})),hitComponent:({hit:t,children:n})=>({type:"a",ref:void 0,constructor:void 0,key:void 0,props:{href:t.url,onClick:r=>{Cw(r)||(r.preventDefault(),e.push(t.url.replace("/ecosystem/","/")))},children:n},__v:null}),navigator:{navigate:({itemUrl:t})=>{e.push(t.replace("/ecosystem/","/"))}},transformSearchClient:t=>{const n=kw(t.search,500);return{...t,search:async r=>n(r)}}}},Fo=Ow,Ho=ue(Fo),vp=Symbol(""),Iw=e=>{Me(e)?pr(()=>e.value,t=>{Ho.value=Hr({},Fo,t)}):Ld(e)?pr(N(e),t=>{Ho.value=Hr({},Fo,t)}):Ho.value=Hr({},Fo,e)},Lw=()=>{const e=Ue(vp),t=Ht();return N(()=>{var n;return{...e.value,...(n=e.value.locales)==null?void 0:n[t.value]}})},Rw=e=>{e.provide(vp,Un(Ho))},Dw=(e,t=[])=>[`lang:${e}`,...Array.isArray(t)?t:[t]],Vw=({buttonText:e="Search",buttonAriaLabel:t=e}={})=>``,Ep=()=>{if(document.querySelector(".DocSearch-Modal"))return;const e=new Event("keydown");e.key="k",e.metaKey=!0,window.dispatchEvent(e),setTimeout(Ep,16)},Nw=e=>{const t="algolia-preconnect";("requestIdleCallback"in window?window.requestIdleCallback:setTimeout)(()=>{if(document.head.querySelector(`#${t}`))return;const n=document.createElement("link");n.id=t,n.rel="preconnect",n.href=`https://${e}-dsn.algolia.net`,n.crossOrigin="",document.head.appendChild(n)})},Mw=fe({name:"DocSearch",props:{containerId:{type:String,default:"docsearch-container"},options:{type:Object,default:()=>({})}},setup(e){const t=Lw(),n=xw(),r=Qs(),o=Ht(),i=ue(!1),s=ue(!1),l=N(()=>{const{locales:d={},...c}=e.options;return{...t.value,...c,...d[o.value]}}),a=async()=>{const{default:d}=await b(async()=>{const{default:f}=await import("./index-DGYl2PJE.js");return{default:f}},[]),{searchParameters:c}=l.value;d({...n,...l.value,container:`#${e.containerId}`,searchParameters:{...c,facetFilters:Dw(r.value,c==null?void 0:c.facetFilters)}}),i.value=!0},u=()=>{s.value||i.value||(s.value=!0,a(),Ep(),Be(o,a))};return Pw(u),Ke(()=>{Nw(l.value.appId)}),()=>{var d;return[H("div",{id:e.containerId,style:{display:i.value?"block":"none"}}),i.value?null:H("div",{onClick:u,innerHTML:Vw((d=l.value.translations)==null?void 0:d.button)})]}}}),zw={enhance({app:e}){Rw(e),e.component("SearchBox",Mw)}},$w=Object.freeze(Object.defineProperty({__proto__:null,default:zw},Symbol.toStringTag,{value:"Module"})),Bw=fe({name:"VPIcon",props:{type:{type:String,default:"unknown"},prefix:{type:String,default:""},icon:{type:String,default:""},color:{type:String,default:""},size:{type:[String,Number],default:""},verticalAlign:{type:String,default:""}},setup(e){const t=N(()=>{const r={};return e.color&&(r.color=e.color),e.size&&(r.fontSize=Number.isNaN(Number(e.size))?e.size:`${e.size}px`),e.verticalAlign&&(r.verticalAlign=e.verticalAlign),ul(r).length?r:null}),n=r=>r.includes("fa-")||/^fa.$/.test(r)?r:`fa-${r}`;return()=>{const{type:r,icon:o,prefix:i}=e;if(!o)return null;if(co(o))return H("img",{class:"vp-icon",src:o,alt:"","aria-hidden":"","no-view":"",style:t.value});if(cl(o))return H("img",{class:"vp-icon",src:mi(o),alt:"","aria-hidden":"","no-view":"",style:t.value});if(r==="iconify")return H("iconify-icon",{key:o,class:"vp-icon",icon:o.includes(":")?o:`${i}${o}`,inline:"",style:t.value});if(r==="fontawesome"){const[s,l]=o.includes(":")?o.split(":",2):["fas",o];return H("i",{key:o,class:["vp-icon",s.length===1?`fa${s}`:n(s),...l.split(" ").map(n)],style:t.value})}return H("i",{key:o,class:["vp-icon",o.includes(" ")?o:`${i}${o}`],style:t.value})}}}),Fw={enhance:({app:e})=>{gi("VPIcon")||e.component("VPIcon",t=>H(Bw,{type:"iconify",prefix:"lucide:",...t}))},setup:()=>{jv("https://cdn.jsdelivr.net/npm/iconify-icon@2")}},Hw=Object.freeze(Object.defineProperty({__proto__:null,default:Fw},Symbol.toStringTag,{value:"Module"})),Zu=Object.freeze(Object.defineProperty({__proto__:null},Symbol.toStringTag,{value:"Module"})),jw=Object.freeze(Object.defineProperty({__proto__:null},Symbol.toStringTag,{value:"Module"})),Uw=Object.freeze(Object.defineProperty({__proto__:null},Symbol.toStringTag,{value:"Module"})),bp="VUEPRESS_REDIRECT_STATUS",Ju=Fv(bp,{}),Qu=Wv(bp,{}),Kw=e=>{const t=Hv(),n=Ht(),r=al(e.config);return N(()=>{if(r.some(([o])=>n.value===o)){for(const o of t.value)for(const[i,s]of r)if(s.includes(o))return i===n.value?null:{lang:o,localePath:i}}return null})};var Ww=fe({name:"RedirectModal",props:{config:{type:Object,required:!0},locales:{type:Object,required:!0}},setup(e){const t=Ft(),n=iv(),r=Ht(),o=Kw(e.config),i=ue(),s=Kv(i),l=ue(!1),a=N(()=>{if(!o.value)return null;const{lang:d,localePath:c}=o.value,f=[e.locales[c],e.locales[r.value]];return{hint:f.map(({hint:p})=>p.replace("$1",d)),switch:f.map(({switch:p})=>p.replace("$1",d)).join(" / "),cancel:f.map(({cancel:p})=>p).join(" / "),remember:f.map(({remember:p})=>p).join(" / ")}}),u=()=>{Qu.value[r.value]=!0,l.value&&(Ju.value[r.value]=!0),s.value=!1};return Be(n,()=>{s.value=!1}),Ke(async()=>{i.value=document.body,await Kn(),o.value&&!Qu.value[r.value]&&!Ju.value[r.value]&&(s.value=!0)}),ui(()=>{s.value=!1}),()=>H(c_,{name:"redirect-modal-fade"},()=>{var d,c,f,p;return s.value?H("div",{key:"mask",class:"redirect-modal-mask"},H("div",{key:"popup",class:"redirect-modal-wrapper"},[H("div",{class:"redirect-modal-content"},(d=a.value)==null?void 0:d.hint.map(_=>H("p",_))),H("div",{class:"redirect-modal-hint"},[H("input",{id:"remember-redirect",type:"checkbox",value:l.value,onChange:()=>{l.value=!l.value}}),H("label",{for:"remember-redirect"},(c=a.value)==null?void 0:c.remember)]),H("button",{type:"button",class:"redirect-modal-action primary",onClick:()=>{u(),t.replace(n.value.replace(r.value,o.value.localePath))}},(f=a.value)==null?void 0:f.switch),H("button",{type:"button",class:"redirect-modal-action",onClick:()=>{u()}},(p=a.value)==null?void 0:p.cancel)])):null})}}),Gw={config:{"/":["en-US"],"/zh/":["zh-CN"]},autoLocale:!1,defaultLocale:"/zh/",localeFallback:!0,defaultBehavior:"defaultLocale"},qw={"/":{name:"English",hint:"Your primary language is $1, do you want to switch to it?",switch:"Switch to $1",cancel:"Cancel",remember:"Remember my choice"},"/zh/":{name:"简体中文",hint:"您的首选语言是 $1,是否切换到该语言?",switch:"切换到 $1",cancel:"取消",remember:"记住我的选择"}};const yp=Gw;var Yw=Pt({setup(){},rootComponents:[()=>H(Ww,{config:yp,locales:qw})]});const Xw=Object.freeze(Object.defineProperty({__proto__:null,config:yp,default:Yw},Symbol.toStringTag,{value:"Module"})),Zw=()=>[b(()=>import("./reveal.esm-B3O8JSaZ.js"),[]),b(()=>import("./markdown.esm-DotuGrRK.js"),[]),b(()=>import("./highlight.esm-BAQT6v9A.js"),[]),b(()=>import("./math.esm-Cgto3ffo.js"),[]),b(()=>import("./search.esm-BPaPUgSs.js"),[]),b(()=>import("./notes.esm-DugvsDbc.js"),[]),b(()=>import("./zoom.esm-BPsgJ1Hi.js"),[])],Jw=ue({}),Tp=Symbol(""),Qw=()=>Ue(Tp),eA=e=>{e.provide(Tp,Jw)},tA=fe({name:"RevealJs",props:{id:{type:String,required:!0},code:{type:String,required:!0},theme:{type:String,default:"auto"}},setup(e){const t=Qw(),n=bt(),r=ef(),o=ue(""),i=ue(!0),s=ct();let l=null;const a=async u=>{const d=[sl(800),...Zw()],[,{default:c},...f]=await Promise.all(d),p=r.value.name==="SlidePage",_=new c(u,{backgroundTransition:"slide",transition:"slide",slideNumber:!0,...t.value,hash:p,mouseWheel:p,...n.value.revealJs,embedded:!p,keyboardCondition:p?null:"focused",markdown:{separator:`^\r?\\n---\r? +$`,verticalSeparator:`^\r? +--\r? +$`},plugins:[f.map(({default:g})=>g),t.value.plugins??[]].flat()});return await _.initialize(),_};return Ke(async()=>{const u=s.value;u&&(o.value=E0(e.code),u.setAttribute("id",e.id),u.setAttribute("data-theme",e.theme),l=await a(u),i.value=!1)}),Wn(()=>{l==null||l.destroy()}),()=>H("div",{class:"vp-reveal"},[H("div",{ref:s,class:["reveal","reveal-viewport"]},H("div",{class:"slides",innerHTML:`
+ 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/analytics/baidu-analytics.html b/plugins/analytics/baidu-analytics.html new file mode 100644 index 0000000000..b63c452b69 --- /dev/null +++ b/plugins/analytics/baidu-analytics.html @@ -0,0 +1,51 @@ + + + + + + + + + baidu-analytics | VuePress Ecosystem + + + + + +
+ + + diff --git a/plugins/analytics/google-analytics.html b/plugins/analytics/google-analytics.html new file mode 100644 index 0000000000..a74a5f18a0 --- /dev/null +++ b/plugins/analytics/google-analytics.html @@ -0,0 +1,64 @@ + + + + + + + + + google-analytics | VuePress Ecosystem + + + + + +
+ + + diff --git a/plugins/analytics/index.html b/plugins/analytics/index.html new file mode 100644 index 0000000000..5eaa801d12 --- /dev/null +++ b/plugins/analytics/index.html @@ -0,0 +1,43 @@ + + + + + + + + + Analytics Plugins | VuePress Ecosystem + + + + + + + + + diff --git a/plugins/analytics/umami-analytics.html b/plugins/analytics/umami-analytics.html new file mode 100644 index 0000000000..b8eac7255d --- /dev/null +++ b/plugins/analytics/umami-analytics.html @@ -0,0 +1,51 @@ + + + + + + + + + umami-analytics | VuePress Ecosystem + + + + + +
+ + + diff --git a/plugins/blog/blog/config.html b/plugins/blog/blog/config.html new file mode 100644 index 0000000000..a42bd9180d --- /dev/null +++ b/plugins/blog/blog/config.html @@ -0,0 +1,180 @@ + + + + + + + + + Config | VuePress Ecosystem + + + + + +
+ + + diff --git a/plugins/blog/blog/guide.html b/plugins/blog/blog/guide.html new file mode 100644 index 0000000000..aa32dff9b1 --- /dev/null +++ b/plugins/blog/blog/guide.html @@ -0,0 +1,247 @@ + + + + + + + + + Guide | VuePress Ecosystem + + + + + +
+ + + diff --git a/plugins/blog/blog/index.html b/plugins/blog/blog/index.html new file mode 100644 index 0000000000..205ddf9bc0 --- /dev/null +++ b/plugins/blog/blog/index.html @@ -0,0 +1,51 @@ + + + + + + + + + blog | VuePress Ecosystem + + + + + + + + + diff --git a/plugins/blog/comment/artalk/config.html b/plugins/blog/comment/artalk/config.html new file mode 100644 index 0000000000..f2f74da72f --- /dev/null +++ b/plugins/blog/comment/artalk/config.html @@ -0,0 +1,59 @@ + + + + + + + + + Artalk Options | VuePress Ecosystem + + + + + +
+ + + diff --git a/plugins/blog/comment/artalk/index.html b/plugins/blog/comment/artalk/index.html new file mode 100644 index 0000000000..1eb3377f99 --- /dev/null +++ b/plugins/blog/comment/artalk/index.html @@ -0,0 +1,43 @@ + + + + + + + + + Artalk | VuePress Ecosystem + + + + + +
+ + + diff --git a/plugins/blog/comment/giscus/config.html b/plugins/blog/comment/giscus/config.html new file mode 100644 index 0000000000..e3891b87a7 --- /dev/null +++ b/plugins/blog/comment/giscus/config.html @@ -0,0 +1,79 @@ + + + + + + + + + Giscus Options | VuePress Ecosystem + + + + + +
+ + + diff --git a/plugins/blog/comment/giscus/index.html b/plugins/blog/comment/giscus/index.html new file mode 100644 index 0000000000..a7b59b467e --- /dev/null +++ b/plugins/blog/comment/giscus/index.html @@ -0,0 +1,43 @@ + + + + + + + + + Giscus | VuePress Ecosystem + + + + + +
+ + + diff --git a/plugins/blog/comment/guide.html b/plugins/blog/comment/guide.html new file mode 100644 index 0000000000..923a03915e --- /dev/null +++ b/plugins/blog/comment/guide.html @@ -0,0 +1,65 @@ + + + + + + + + + Guide | VuePress Ecosystem + + + + + +
+ + + diff --git a/plugins/blog/comment/index.html b/plugins/blog/comment/index.html new file mode 100644 index 0000000000..153b323d9c --- /dev/null +++ b/plugins/blog/comment/index.html @@ -0,0 +1,51 @@ + + + + + + + + + comment | VuePress Ecosystem + + + + + + + + + diff --git a/plugins/blog/comment/twikoo/config.html b/plugins/blog/comment/twikoo/config.html new file mode 100644 index 0000000000..7b8459357f --- /dev/null +++ b/plugins/blog/comment/twikoo/config.html @@ -0,0 +1,59 @@ + + + + + + + + + Twikoo Options | VuePress Ecosystem + + + + + +
+ + + diff --git a/plugins/blog/comment/twikoo/index.html b/plugins/blog/comment/twikoo/index.html new file mode 100644 index 0000000000..f2400a8525 --- /dev/null +++ b/plugins/blog/comment/twikoo/index.html @@ -0,0 +1,43 @@ + + + + + + + + + Twikoo | VuePress Ecosystem + + + + + +
+ + + diff --git a/plugins/blog/comment/waline/config.html b/plugins/blog/comment/waline/config.html new file mode 100644 index 0000000000..016e0fbec9 --- /dev/null +++ b/plugins/blog/comment/waline/config.html @@ -0,0 +1,134 @@ + + + + + + + + + Waline Config | VuePress Ecosystem + + + + + +
+ + + diff --git a/plugins/blog/comment/waline/index.html b/plugins/blog/comment/waline/index.html new file mode 100644 index 0000000000..3fb85e2864 --- /dev/null +++ b/plugins/blog/comment/waline/index.html @@ -0,0 +1,43 @@ + + + + + + + + + Waline | VuePress Ecosystem + + + + + +
+ + + diff --git a/plugins/blog/feed/channel.html b/plugins/blog/feed/channel.html new file mode 100644 index 0000000000..b59da06381 --- /dev/null +++ b/plugins/blog/feed/channel.html @@ -0,0 +1,56 @@ + + + + + + + + + Channel Config | VuePress Ecosystem + + + + + +
+ + + diff --git a/plugins/blog/feed/config.html b/plugins/blog/feed/config.html new file mode 100644 index 0000000000..31a1d87a58 --- /dev/null +++ b/plugins/blog/feed/config.html @@ -0,0 +1,53 @@ + + + + + + + + + Plugin Config | VuePress Ecosystem + + + + + +
+ + + diff --git a/plugins/blog/feed/frontmatter.html b/plugins/blog/feed/frontmatter.html new file mode 100644 index 0000000000..b044fd9640 --- /dev/null +++ b/plugins/blog/feed/frontmatter.html @@ -0,0 +1,91 @@ + + + + + + + + + Frontmatter Config | VuePress Ecosystem + + + + + +
+ + + diff --git a/plugins/blog/feed/getter.html b/plugins/blog/feed/getter.html new file mode 100644 index 0000000000..dd3dd4fde3 --- /dev/null +++ b/plugins/blog/feed/getter.html @@ -0,0 +1,129 @@ + + + + + + + + + Feed Getter | VuePress Ecosystem + + + + + +
+ + + diff --git a/plugins/blog/feed/guide.html b/plugins/blog/feed/guide.html new file mode 100644 index 0000000000..751306db0e --- /dev/null +++ b/plugins/blog/feed/guide.html @@ -0,0 +1,43 @@ + + + + + + + + + Guide | VuePress Ecosystem + + + + + +
+ + + diff --git a/plugins/blog/feed/index.html b/plugins/blog/feed/index.html new file mode 100644 index 0000000000..98201fb6cf --- /dev/null +++ b/plugins/blog/feed/index.html @@ -0,0 +1,51 @@ + + + + + + + + + feed | VuePress Ecosystem + + + + + + + + + diff --git a/plugins/blog/index.html b/plugins/blog/index.html new file mode 100644 index 0000000000..f67f319e73 --- /dev/null +++ b/plugins/blog/index.html @@ -0,0 +1,43 @@ + + + + + + + + + Blog Plugins | VuePress Ecosystem + + + + + + + + + diff --git a/plugins/development/active-header-links.html b/plugins/development/active-header-links.html new file mode 100644 index 0000000000..155edc2a87 --- /dev/null +++ b/plugins/development/active-header-links.html @@ -0,0 +1,51 @@ + + + + + + + + + active-header-links | VuePress Ecosystem + + + + + +
+ + + diff --git a/plugins/development/git.html b/plugins/development/git.html new file mode 100644 index 0000000000..dc8c6c9fce --- /dev/null +++ b/plugins/development/git.html @@ -0,0 +1,203 @@ + + + + + + + + + git | VuePress Ecosystem + + + + + +
+ + + diff --git a/plugins/development/index.html b/plugins/development/index.html new file mode 100644 index 0000000000..e43a14c74c --- /dev/null +++ b/plugins/development/index.html @@ -0,0 +1,43 @@ + + + + + + + + + Development Plugins | VuePress Ecosystem + + + + + + + + + diff --git a/plugins/development/palette.html b/plugins/development/palette.html new file mode 100644 index 0000000000..6160cb4211 --- /dev/null +++ b/plugins/development/palette.html @@ -0,0 +1,74 @@ + + + + + + + + + palette | VuePress Ecosystem + + + + + +
+ + + diff --git a/plugins/development/reading-time.html b/plugins/development/reading-time.html new file mode 100644 index 0000000000..47868e3c8e --- /dev/null +++ b/plugins/development/reading-time.html @@ -0,0 +1,127 @@ + + + + + + + + + reading-time | VuePress Ecosystem + + + + + +
+ + + diff --git a/plugins/development/rtl.html b/plugins/development/rtl.html new file mode 100644 index 0000000000..3b123f174c --- /dev/null +++ b/plugins/development/rtl.html @@ -0,0 +1,56 @@ + + + + + + + + + rtl | VuePress Ecosystem + + + + + +
+ + + diff --git a/plugins/development/sass-palette/config.html b/plugins/development/sass-palette/config.html new file mode 100644 index 0000000000..966d951487 --- /dev/null +++ b/plugins/development/sass-palette/config.html @@ -0,0 +1,43 @@ + + + + + + + + + Config | VuePress Ecosystem + + + + + +
+ + + diff --git a/plugins/development/sass-palette/guide.html b/plugins/development/sass-palette/guide.html new file mode 100644 index 0000000000..180af3ef35 --- /dev/null +++ b/plugins/development/sass-palette/guide.html @@ -0,0 +1,97 @@ + + + + + + + + + Guide | VuePress Ecosystem + + + + + +
+ + + diff --git a/plugins/development/sass-palette/index.html b/plugins/development/sass-palette/index.html new file mode 100644 index 0000000000..256ba78d6d --- /dev/null +++ b/plugins/development/sass-palette/index.html @@ -0,0 +1,53 @@ + + + + + + + + + sass-palette | VuePress Ecosystem + + + + + +
+ + + diff --git a/plugins/development/theme-data.html b/plugins/development/theme-data.html new file mode 100644 index 0000000000..bbec7ede80 --- /dev/null +++ b/plugins/development/theme-data.html @@ -0,0 +1,88 @@ + + + + + + + + + theme-data | VuePress Ecosystem + + + + + +
+ + + diff --git a/plugins/development/toc.html b/plugins/development/toc.html new file mode 100644 index 0000000000..e0f587a755 --- /dev/null +++ b/plugins/development/toc.html @@ -0,0 +1,108 @@ + + + + + + + + + toc | VuePress Ecosystem + + + + + +
+ + + diff --git a/plugins/features/back-to-top.html b/plugins/features/back-to-top.html new file mode 100644 index 0000000000..de9e3a27a3 --- /dev/null +++ b/plugins/features/back-to-top.html @@ -0,0 +1,55 @@ + + + + + + + + + back-to-top | VuePress Ecosystem + + + + + +
+ + + diff --git a/plugins/features/catalog.html b/plugins/features/catalog.html new file mode 100644 index 0000000000..8e1cb4c7b7 --- /dev/null +++ b/plugins/features/catalog.html @@ -0,0 +1,123 @@ + + + + + + + + + catalog | VuePress Ecosystem + + + + + +
+ + + diff --git a/plugins/features/copy-code.html b/plugins/features/copy-code.html new file mode 100644 index 0000000000..a1ca5e9435 --- /dev/null +++ b/plugins/features/copy-code.html @@ -0,0 +1,112 @@ + + + + + + + + + copy-code | VuePress Ecosystem + + + + + +
+ + + diff --git a/plugins/features/copyright.html b/plugins/features/copyright.html new file mode 100644 index 0000000000..6075bd7765 --- /dev/null +++ b/plugins/features/copyright.html @@ -0,0 +1,104 @@ + + + + + + + + + copyright | VuePress Ecosystem + + + + + +
+ + + diff --git a/plugins/features/icon.html b/plugins/features/icon.html new file mode 100644 index 0000000000..7432f0e59d --- /dev/null +++ b/plugins/features/icon.html @@ -0,0 +1,86 @@ + + + + + + + + + icon | VuePress Ecosystem + + + + + +
+ + + diff --git a/plugins/features/index.html b/plugins/features/index.html new file mode 100644 index 0000000000..0e883d2406 --- /dev/null +++ b/plugins/features/index.html @@ -0,0 +1,43 @@ + + + + + + + + + Feature Plugins | VuePress Ecosystem + + + + + + + + + diff --git a/plugins/features/medium-zoom.html b/plugins/features/medium-zoom.html new file mode 100644 index 0000000000..3f83dd45d0 --- /dev/null +++ b/plugins/features/medium-zoom.html @@ -0,0 +1,69 @@ + + + + + + + + + medium-zoom | VuePress Ecosystem + + + + + +
+ + + diff --git a/plugins/features/notice.html b/plugins/features/notice.html new file mode 100644 index 0000000000..f4a5eac931 --- /dev/null +++ b/plugins/features/notice.html @@ -0,0 +1,145 @@ + + + + + + + + + notice | VuePress Ecosystem + + + + + +
+ + + diff --git a/plugins/features/nprogress.html b/plugins/features/nprogress.html new file mode 100644 index 0000000000..f73eb31cbf --- /dev/null +++ b/plugins/features/nprogress.html @@ -0,0 +1,50 @@ + + + + + + + + + nprogress | VuePress Ecosystem + + + + + +
+ + + diff --git a/plugins/features/photo-swipe.html b/plugins/features/photo-swipe.html new file mode 100644 index 0000000000..2618677649 --- /dev/null +++ b/plugins/features/photo-swipe.html @@ -0,0 +1,161 @@ + + + + + + + + + photo-swipe | VuePress Ecosystem + + + + + +
+ + + diff --git a/plugins/features/watermark.html b/plugins/features/watermark.html new file mode 100644 index 0000000000..0a732eb0cc --- /dev/null +++ b/plugins/features/watermark.html @@ -0,0 +1,76 @@ + + + + + + + + + watermark | VuePress Ecosystem + + + + + +
+ + + diff --git a/plugins/index.html b/plugins/index.html new file mode 100644 index 0000000000..0d4653d6ca --- /dev/null +++ b/plugins/index.html @@ -0,0 +1,43 @@ + + + + + + + + + Plugins | VuePress Ecosystem + + + + + + + + + diff --git a/plugins/markdown/append-date.html b/plugins/markdown/append-date.html new file mode 100644 index 0000000000..1d55906942 --- /dev/null +++ b/plugins/markdown/append-date.html @@ -0,0 +1,47 @@ + + + + + + + + + append-date | VuePress Ecosystem + + + + + +
+ + + diff --git a/plugins/markdown/index.html b/plugins/markdown/index.html new file mode 100644 index 0000000000..44c47fd4e8 --- /dev/null +++ b/plugins/markdown/index.html @@ -0,0 +1,43 @@ + + + + + + + + + Markdown Plugins | VuePress Ecosystem + + + + + + + + + diff --git a/plugins/markdown/links-check.html b/plugins/markdown/links-check.html new file mode 100644 index 0000000000..45e15f5de7 --- /dev/null +++ b/plugins/markdown/links-check.html @@ -0,0 +1,66 @@ + + + + + + + + + links-check | VuePress Ecosystem + + + + + +
+ + + diff --git a/plugins/markdown/markdown-container.html b/plugins/markdown/markdown-container.html new file mode 100644 index 0000000000..b0e461acd9 --- /dev/null +++ b/plugins/markdown/markdown-container.html @@ -0,0 +1,74 @@ + + + + + + + + + markdown-container | VuePress Ecosystem + + + + + +
+ + + diff --git a/plugins/markdown/markdown-ext.html b/plugins/markdown/markdown-ext.html new file mode 100644 index 0000000000..85a210713c --- /dev/null +++ b/plugins/markdown/markdown-ext.html @@ -0,0 +1,98 @@ + + + + + + + + + markdown-ext | VuePress Ecosystem + + + + + +
+ + + diff --git a/plugins/markdown/markdown-hint.html b/plugins/markdown/markdown-hint.html new file mode 100644 index 0000000000..09fe602ee4 --- /dev/null +++ b/plugins/markdown/markdown-hint.html @@ -0,0 +1,119 @@ + + + + + + + + + markdown-hint | VuePress Ecosystem + + + + + +
+ + + diff --git a/plugins/markdown/markdown-image.html b/plugins/markdown/markdown-image.html new file mode 100644 index 0000000000..b78bf65db3 --- /dev/null +++ b/plugins/markdown/markdown-image.html @@ -0,0 +1,82 @@ + + + + + + + + + markdown-image | VuePress Ecosystem + + + + + +
+ + + diff --git a/plugins/markdown/markdown-include.html b/plugins/markdown/markdown-include.html new file mode 100644 index 0000000000..a88a9b1fee --- /dev/null +++ b/plugins/markdown/markdown-include.html @@ -0,0 +1,236 @@ + + + + + + + + + markdown-include | VuePress Ecosystem + + + + + +
+ + + diff --git a/plugins/markdown/markdown-math.html b/plugins/markdown/markdown-math.html new file mode 100644 index 0000000000..4b9379d11f --- /dev/null +++ b/plugins/markdown/markdown-math.html @@ -0,0 +1,62 @@ + + + + + + + + + markdown-math | VuePress Ecosystem + + + + + +
+ + + diff --git a/plugins/markdown/markdown-stylize.html b/plugins/markdown/markdown-stylize.html new file mode 100644 index 0000000000..3910f9347f --- /dev/null +++ b/plugins/markdown/markdown-stylize.html @@ -0,0 +1,111 @@ + + + + + + + + + markdown-stylize | VuePress Ecosystem + + + + + +
+ + + diff --git a/plugins/markdown/markdown-tab.html b/plugins/markdown/markdown-tab.html new file mode 100644 index 0000000000..9f4af7cbe2 --- /dev/null +++ b/plugins/markdown/markdown-tab.html @@ -0,0 +1,226 @@ + + + + + + + + + markdown-tab | VuePress Ecosystem + + + + + +
+ + + diff --git a/plugins/markdown/prismjs.html b/plugins/markdown/prismjs.html new file mode 100644 index 0000000000..046745356b --- /dev/null +++ b/plugins/markdown/prismjs.html @@ -0,0 +1,342 @@ + + + + + + + + + prismjs | VuePress Ecosystem + + + + + +
+ + + diff --git a/plugins/markdown/revealjs/demo.html b/plugins/markdown/revealjs/demo.html new file mode 100644 index 0000000000..13f08013a2 --- /dev/null +++ b/plugins/markdown/revealjs/demo.html @@ -0,0 +1,43 @@ + + + + + + + + + Slide Demo | VuePress Ecosystem + + + + + +
+ + + diff --git a/plugins/markdown/revealjs/index.html b/plugins/markdown/revealjs/index.html new file mode 100644 index 0000000000..863ae38cf3 --- /dev/null +++ b/plugins/markdown/revealjs/index.html @@ -0,0 +1,111 @@ + + + + + + + + + revealjs | VuePress Ecosystem + + + + + +
+ + + diff --git a/plugins/markdown/revealjs/themes.html b/plugins/markdown/revealjs/themes.html new file mode 100644 index 0000000000..c8f030ea68 --- /dev/null +++ b/plugins/markdown/revealjs/themes.html @@ -0,0 +1,43 @@ + + + + + + + + + Reveal.js Themes | VuePress Ecosystem + + + + + + + + + diff --git a/plugins/markdown/shiki.html b/plugins/markdown/shiki.html new file mode 100644 index 0000000000..c5b316f449 --- /dev/null +++ b/plugins/markdown/shiki.html @@ -0,0 +1,343 @@ + + + + + + + + + shiki | VuePress Ecosystem + + + + + +
+ + + diff --git a/plugins/pwa/index.html b/plugins/pwa/index.html new file mode 100644 index 0000000000..0b46a04ecb --- /dev/null +++ b/plugins/pwa/index.html @@ -0,0 +1,43 @@ + + + + + + + + + PWA Plugins | VuePress Ecosystem + + + + + + + + + diff --git a/plugins/pwa/pwa/config.html b/plugins/pwa/pwa/config.html new file mode 100644 index 0000000000..3608b0a112 --- /dev/null +++ b/plugins/pwa/pwa/config.html @@ -0,0 +1,174 @@ + + + + + + + + + Config | VuePress Ecosystem + + + + + +
+ + + diff --git a/plugins/pwa/pwa/guide.html b/plugins/pwa/pwa/guide.html new file mode 100644 index 0000000000..d24da7e858 --- /dev/null +++ b/plugins/pwa/pwa/guide.html @@ -0,0 +1,63 @@ + + + + + + + + + Guide | VuePress Ecosystem + + + + + +
+ + + diff --git a/plugins/pwa/pwa/index.html b/plugins/pwa/pwa/index.html new file mode 100644 index 0000000000..8b41aa8bb1 --- /dev/null +++ b/plugins/pwa/pwa/index.html @@ -0,0 +1,51 @@ + + + + + + + + + pwa | VuePress Ecosystem + + + + + + + + + diff --git a/plugins/pwa/remove-pwa.html b/plugins/pwa/remove-pwa.html new file mode 100644 index 0000000000..ad1c2d01ce --- /dev/null +++ b/plugins/pwa/remove-pwa.html @@ -0,0 +1,51 @@ + + + + + + + + + remove-pwa | VuePress Ecosystem + + + + + +
+ + + diff --git a/plugins/search/docsearch.html b/plugins/search/docsearch.html new file mode 100644 index 0000000000..3250777583 --- /dev/null +++ b/plugins/search/docsearch.html @@ -0,0 +1,251 @@ + + + + + + + + + docsearch | VuePress Ecosystem + + + + + +
+ + + diff --git a/plugins/search/guidelines.html b/plugins/search/guidelines.html new file mode 100644 index 0000000000..3879a35409 --- /dev/null +++ b/plugins/search/guidelines.html @@ -0,0 +1,43 @@ + + + + + + + + + Search Plugin Guidelines | VuePress Ecosystem + + + + + +
+ + + diff --git a/plugins/search/index.html b/plugins/search/index.html new file mode 100644 index 0000000000..7840f2bd73 --- /dev/null +++ b/plugins/search/index.html @@ -0,0 +1,43 @@ + + + + + + + + + Search Plugins | VuePress Ecosystem + + + + + + + + + diff --git a/plugins/search/search.html b/plugins/search/search.html new file mode 100644 index 0000000000..c7c3107b03 --- /dev/null +++ b/plugins/search/search.html @@ -0,0 +1,113 @@ + + + + + + + + + search | VuePress Ecosystem + + + + + +
+ + + diff --git a/plugins/search/slimsearch.html b/plugins/search/slimsearch.html new file mode 100644 index 0000000000..f2b861beeb --- /dev/null +++ b/plugins/search/slimsearch.html @@ -0,0 +1,337 @@ + + + + + + + + + slimsearch | VuePress Ecosystem + + + + + +
+ + + diff --git a/plugins/seo/index.html b/plugins/seo/index.html new file mode 100644 index 0000000000..1dbbb1102f --- /dev/null +++ b/plugins/seo/index.html @@ -0,0 +1,43 @@ + + + + + + + + + SEO Plugins | VuePress Ecosystem + + + + + + + + + diff --git a/plugins/seo/seo/config.html b/plugins/seo/seo/config.html new file mode 100644 index 0000000000..68959a56fc --- /dev/null +++ b/plugins/seo/seo/config.html @@ -0,0 +1,83 @@ + + + + + + + + + Config | VuePress Ecosystem + + + + + +
+ + + diff --git a/plugins/seo/seo/guide.html b/plugins/seo/seo/guide.html new file mode 100644 index 0000000000..4ff58a5bc9 --- /dev/null +++ b/plugins/seo/seo/guide.html @@ -0,0 +1,74 @@ + + + + + + + + + Guide | VuePress Ecosystem + + + + + +
+ + + diff --git a/plugins/seo/seo/index.html b/plugins/seo/seo/index.html new file mode 100644 index 0000000000..841ada3d4a --- /dev/null +++ b/plugins/seo/seo/index.html @@ -0,0 +1,51 @@ + + + + + + + + + seo | VuePress Ecosystem + + + + + + + + + diff --git a/plugins/seo/sitemap/config.html b/plugins/seo/sitemap/config.html new file mode 100644 index 0000000000..341ce48a5c --- /dev/null +++ b/plugins/seo/sitemap/config.html @@ -0,0 +1,43 @@ + + + + + + + + + Config | VuePress Ecosystem + + + + + +
+ + + diff --git a/plugins/seo/sitemap/frontmatter.html b/plugins/seo/sitemap/frontmatter.html new file mode 100644 index 0000000000..ebbc604319 --- /dev/null +++ b/plugins/seo/sitemap/frontmatter.html @@ -0,0 +1,43 @@ + + + + + + + + + Frontmatter | VuePress Ecosystem + + + + + +
+ + + diff --git a/plugins/seo/sitemap/guide.html b/plugins/seo/sitemap/guide.html new file mode 100644 index 0000000000..a5f7db412b --- /dev/null +++ b/plugins/seo/sitemap/guide.html @@ -0,0 +1,49 @@ + + + + + + + + + Guide | VuePress Ecosystem + + + + + +
+ + + diff --git a/plugins/seo/sitemap/index.html b/plugins/seo/sitemap/index.html new file mode 100644 index 0000000000..f06f7fb741 --- /dev/null +++ b/plugins/seo/sitemap/index.html @@ -0,0 +1,51 @@ + + + + + + + + + sitemap | VuePress Ecosystem + + + + + + + + + diff --git a/plugins/tools/cache.html b/plugins/tools/cache.html new file mode 100644 index 0000000000..94cdb21ae8 --- /dev/null +++ b/plugins/tools/cache.html @@ -0,0 +1,54 @@ + + + + + + + + + cache | VuePress Ecosystem + + + + + +
+ + + diff --git a/plugins/tools/google-tag-manager.html b/plugins/tools/google-tag-manager.html new file mode 100644 index 0000000000..d5bbd0ea4b --- /dev/null +++ b/plugins/tools/google-tag-manager.html @@ -0,0 +1,66 @@ + + + + + + + + + google-tag-manager | VuePress Ecosystem + + + + + +
+ + + diff --git a/plugins/tools/index.html b/plugins/tools/index.html new file mode 100644 index 0000000000..9825ed0ed2 --- /dev/null +++ b/plugins/tools/index.html @@ -0,0 +1,43 @@ + + + + + + + + + Tool Plugins | VuePress Ecosystem + + + + + + + + + diff --git a/plugins/tools/redirect.html b/plugins/tools/redirect.html new file mode 100644 index 0000000000..cb2e4538aa --- /dev/null +++ b/plugins/tools/redirect.html @@ -0,0 +1,149 @@ + + + + + + + + + redirect | VuePress Ecosystem + + + + + +
+ + + diff --git a/plugins/tools/register-components.html b/plugins/tools/register-components.html new file mode 100644 index 0000000000..0e18026dbd --- /dev/null +++ b/plugins/tools/register-components.html @@ -0,0 +1,85 @@ + + + + + + + + + register-components | VuePress Ecosystem + + + + + +
+ + + diff --git a/robots.txt b/robots.txt new file mode 100644 index 0000000000..eb4925b55a --- /dev/null +++ b/robots.txt @@ -0,0 +1,5 @@ + +User-agent:* +Disallow: + +Sitemap: https://ecosystem.vuejs.press/ecosystem/sitemap.xml diff --git a/rss.xml b/rss.xml new file mode 100644 index 0000000000..5011ab18b0 --- /dev/null +++ b/rss.xml @@ -0,0 +1,5209 @@ + + + + + VuePress Ecosystem + https://ecosystem.vuejs.press/ecosystem/ + VuePress official themes and plugins + en-US + Fri, 10 Jan 2025 18:17:55 GMT + Fri, 10 Jan 2025 18:17:55 GMT + @vuepress/plugin-feed + https://validator.w3.org/feed/docs/rss2.html + + Plugins + https://ecosystem.vuejs.press/ecosystem/plugins/ + https://ecosystem.vuejs.press/ecosystem/plugins/ + Plugins + Plugins + + + + Themes + https://ecosystem.vuejs.press/ecosystem/themes/ + https://ecosystem.vuejs.press/ecosystem/themes/ + Themes + Themes + + + + Theme Guidelines + https://ecosystem.vuejs.press/ecosystem/themes/guidelines.html + https://ecosystem.vuejs.press/ecosystem/themes/guidelines.html + Theme Guidelines + Theme Guidelines To avoid theme developers and users setting unneeded options, we have a set of guidelines that should be followed when creating a theme. DOM Structure A theme m... + To avoid theme developers and users setting unneeded options, we have a set of guidelines that should be followed when creating a theme.

+

DOM Structure

+

A theme must implement the following DOM structure:

+
    +
  • Container: An element which contains the entire theme. This element should have an attribute vp-container.
  • +
  • Content: An element which holds markdown render results. This element should have an attribute vp-content.
  • +
+

A theme may have the following optional elements:

+
    +
  • Navbar: Navbar of the site. This element should have an attribute vp-navbar.
  • +
  • Sidebar: Sidebar of the site. This element should have an attribute vp-sidebar.
  • +
  • Outline: Headings or outline of the main content. This element should have an attribute vp-outline.
  • +
  • Comment: Comment service (comment box and comment list). This element should have an attribute vp-comment.
  • +
  • Footer: Footer of the site. This element should have an attribute vp-footer.
  • +
+

A theme must:

+
    +
  • Set data-theme to dark on html in darkmode.
  • +
  • Set data-theme to light on html in lightmode.
  • +
+

If it only have one color scheme, it still needs to set data-theme to light or dark to indicate the default color scheme.

+

Components

+

To support search plugins, a theme shall check whether <SearchBox /> is globally registered and render it in it's own navbar or sidebar if it is available.

+

Color Variables

+

A theme must implement the following color variables:

+

Text

+
    +
  • --vp-c-text: Default text color.
  • +
  • --vp-c-text-mute: Colors for muted texts, such as "inactive menu" or "info texts".
  • +
  • --vp-c-text-subtle: Color for subtle text, such as as "placeholders" or "caret icon".
  • +
+

Background

+
    +
  • --vp-c-bg: The bg color used for main screen.
  • +
  • --vp-c-bg-alt: The alternative bg color used in places such as "sidebar", or "code block".
  • +
  • --vp-c-bg-elv: The elevated bg color. This is used at parts where it "floats", such as "dialog".
  • +
+

Shadow

+
    +
  • --vp-c-shadow: Shadow color
  • +
+

Accent

+

Accent color and brand colors which used for interactive components.

+
    +
  • +

    --vp-c-accent: The most solid color used mainly for colored text. It must satisfy the contrast ratio against when used on top of --vp-c-accent-soft.

    +
  • +
  • +

    --vp-c-accent-hover: Color used for hover state.

    +
  • +
  • +

    --vp-c-accent-bg: Color used for solid background. It must satisfy the contrast ratio with --vp-c-accent-text on top of it.

    +
  • +
  • +

    --vp-c-accent-text: Color used for text with --vp-c-accent-bg background. It must satisfy the contrast ratio with --vp-c-accent-bg.

    +
  • +
  • +

    --vp-c-accent-soft: The color used for subtle background such as custom container or badges. It must satisfy the contrast ratio when putting --vp-c-accent colors on top of it.

    +

    The soft color must be semi transparent alpha channel. This is crucial because it allows adding multiple "soft" colors on top of each other to create a accent, such as when having inline code block inside custom containers.

    +
  • +
+

Borders

+
    +
  • --vp-c-border: Border color for interactive components. For example this should be used for a button outline.
  • +
  • --vp-c-border-hard: Darker border colors, which is used for "hard" borders closed to text, such as table and kbd.
  • +
  • --vp-c-divider: Color for separators, used to divide sections within the same components, such as having separator on "h2" heading.
  • +
+

Controls

+
    +
  • --vp-c-control: Background color for interactive controls, such as buttons or checkboxes.
  • +
  • --vp-c-control-hover: Background color for hover state of interactive controls.
  • +
  • --vp-c-control-disabled: Color for disabled state of interactive controls.
  • +
+

Transition timing

+
    +
  • --vp-t-color: Color transition timing.
  • +
  • --vp-t-transform: Transform transition timing.
  • +
+

Demo

+]]>
+
+ + Tool Packages + https://ecosystem.vuejs.press/ecosystem/tools/ + https://ecosystem.vuejs.press/ecosystem/tools/ + Tool Packages + Tool Packages + + + + Analytics Plugins + https://ecosystem.vuejs.press/ecosystem/plugins/analytics/ + https://ecosystem.vuejs.press/ecosystem/plugins/analytics/ + Analytics Plugins + Analytics Plugins + + + + baidu-analytics + https://ecosystem.vuejs.press/ecosystem/plugins/analytics/baidu-analytics.html + https://ecosystem.vuejs.press/ecosystem/plugins/analytics/baidu-analytics.html + baidu-analytics + baidu-analytics + + + + google-analytics + https://ecosystem.vuejs.press/ecosystem/plugins/analytics/google-analytics.html + https://ecosystem.vuejs.press/ecosystem/plugins/analytics/google-analytics.html + google-analytics + google-analytics + + + + umami-analytics + https://ecosystem.vuejs.press/ecosystem/plugins/analytics/umami-analytics.html + https://ecosystem.vuejs.press/ecosystem/plugins/analytics/umami-analytics.html + umami-analytics + umami-analytics + + + + Blog Plugins + https://ecosystem.vuejs.press/ecosystem/plugins/blog/ + https://ecosystem.vuejs.press/ecosystem/plugins/blog/ + Blog Plugins + Blog Plugins + + + + Development Plugins + https://ecosystem.vuejs.press/ecosystem/plugins/development/ + https://ecosystem.vuejs.press/ecosystem/plugins/development/ + Development Plugins + Development Plugins + + + + active-header-links + https://ecosystem.vuejs.press/ecosystem/plugins/development/active-header-links.html + https://ecosystem.vuejs.press/ecosystem/plugins/development/active-header-links.html + active-header-links + active-header-links + + + + git + https://ecosystem.vuejs.press/ecosystem/plugins/development/git.html + https://ecosystem.vuejs.press/ecosystem/plugins/development/git.html + git + git + + + + palette + https://ecosystem.vuejs.press/ecosystem/plugins/development/palette.html + https://ecosystem.vuejs.press/ecosystem/plugins/development/palette.html + palette + palette + + + + reading-time + https://ecosystem.vuejs.press/ecosystem/plugins/development/reading-time.html + https://ecosystem.vuejs.press/ecosystem/plugins/development/reading-time.html + reading-time + reading-time + + + + rtl + https://ecosystem.vuejs.press/ecosystem/plugins/development/rtl.html + https://ecosystem.vuejs.press/ecosystem/plugins/development/rtl.html + rtl + rtl + + + + theme-data + https://ecosystem.vuejs.press/ecosystem/plugins/development/theme-data.html + https://ecosystem.vuejs.press/ecosystem/plugins/development/theme-data.html + theme-data + theme-data + + + + toc + https://ecosystem.vuejs.press/ecosystem/plugins/development/toc.html + https://ecosystem.vuejs.press/ecosystem/plugins/development/toc.html + toc + + + + Feature Plugins + https://ecosystem.vuejs.press/ecosystem/plugins/features/ + https://ecosystem.vuejs.press/ecosystem/plugins/features/ + Feature Plugins + Feature Plugins + + + + back-to-top + https://ecosystem.vuejs.press/ecosystem/plugins/features/back-to-top.html + https://ecosystem.vuejs.press/ecosystem/plugins/features/back-to-top.html + back-to-top + back-to-top + + + + catalog + https://ecosystem.vuejs.press/ecosystem/plugins/features/catalog.html + https://ecosystem.vuejs.press/ecosystem/plugins/features/catalog.html + catalog + catalog + + + + copy-code + https://ecosystem.vuejs.press/ecosystem/plugins/features/copy-code.html + https://ecosystem.vuejs.press/ecosystem/plugins/features/copy-code.html + copy-code + copy-code + + + + copyright + https://ecosystem.vuejs.press/ecosystem/plugins/features/copyright.html + https://ecosystem.vuejs.press/ecosystem/plugins/features/copyright.html + copyright + copyright + + + + icon + https://ecosystem.vuejs.press/ecosystem/plugins/features/icon.html + https://ecosystem.vuejs.press/ecosystem/plugins/features/icon.html + icon + icon + + + + medium-zoom + https://ecosystem.vuejs.press/ecosystem/plugins/features/medium-zoom.html + https://ecosystem.vuejs.press/ecosystem/plugins/features/medium-zoom.html + medium-zoom + medium-zoom + + + + notice + https://ecosystem.vuejs.press/ecosystem/plugins/features/notice.html + https://ecosystem.vuejs.press/ecosystem/plugins/features/notice.html + notice + notice + + + + nprogress + https://ecosystem.vuejs.press/ecosystem/plugins/features/nprogress.html + https://ecosystem.vuejs.press/ecosystem/plugins/features/nprogress.html + nprogress + nprogress + + + + photo-swipe + https://ecosystem.vuejs.press/ecosystem/plugins/features/photo-swipe.html + https://ecosystem.vuejs.press/ecosystem/plugins/features/photo-swipe.html + photo-swipe + photo-swipe + + + + watermark + https://ecosystem.vuejs.press/ecosystem/plugins/features/watermark.html + https://ecosystem.vuejs.press/ecosystem/plugins/features/watermark.html + watermark + watermark + + + + Markdown Plugins + https://ecosystem.vuejs.press/ecosystem/plugins/markdown/ + https://ecosystem.vuejs.press/ecosystem/plugins/markdown/ + Markdown Plugins + Markdown Plugins + + + + append-date + https://ecosystem.vuejs.press/ecosystem/plugins/markdown/append-date.html + https://ecosystem.vuejs.press/ecosystem/plugins/markdown/append-date.html + append-date + append-date + + + + links-check + https://ecosystem.vuejs.press/ecosystem/plugins/markdown/links-check.html + https://ecosystem.vuejs.press/ecosystem/plugins/markdown/links-check.html + links-check + links-check + + + + markdown-container + https://ecosystem.vuejs.press/ecosystem/plugins/markdown/markdown-container.html + https://ecosystem.vuejs.press/ecosystem/plugins/markdown/markdown-container.html + markdown-container + markdown-container + + + + markdown-ext + https://ecosystem.vuejs.press/ecosystem/plugins/markdown/markdown-ext.html + https://ecosystem.vuejs.press/ecosystem/plugins/markdown/markdown-ext.html + markdown-ext + markdown-ext + + + + markdown-hint + https://ecosystem.vuejs.press/ecosystem/plugins/markdown/markdown-hint.html + https://ecosystem.vuejs.press/ecosystem/plugins/markdown/markdown-hint.html + markdown-hint + markdown-hint + + + + markdown-image + https://ecosystem.vuejs.press/ecosystem/plugins/markdown/markdown-image.html + https://ecosystem.vuejs.press/ecosystem/plugins/markdown/markdown-image.html + markdown-image + markdown-image + + + + + markdown-include + https://ecosystem.vuejs.press/ecosystem/plugins/markdown/markdown-include.html + https://ecosystem.vuejs.press/ecosystem/plugins/markdown/markdown-include.html + markdown-include + markdown-include + + + + markdown-math + https://ecosystem.vuejs.press/ecosystem/plugins/markdown/markdown-math.html + https://ecosystem.vuejs.press/ecosystem/plugins/markdown/markdown-math.html + markdown-math + markdown-math + + + + markdown-stylize + https://ecosystem.vuejs.press/ecosystem/plugins/markdown/markdown-stylize.html + https://ecosystem.vuejs.press/ecosystem/plugins/markdown/markdown-stylize.html + markdown-stylize + markdown-stylize + + + + markdown-tab + https://ecosystem.vuejs.press/ecosystem/plugins/markdown/markdown-tab.html + https://ecosystem.vuejs.press/ecosystem/plugins/markdown/markdown-tab.html + markdown-tab + markdown-tab + + + + prismjs + https://ecosystem.vuejs.press/ecosystem/plugins/markdown/prismjs.html + https://ecosystem.vuejs.press/ecosystem/plugins/markdown/prismjs.html + prismjs + prismjs + + + + shiki + https://ecosystem.vuejs.press/ecosystem/plugins/markdown/shiki.html + https://ecosystem.vuejs.press/ecosystem/plugins/markdown/shiki.html + shiki + shiki + + + + PWA Plugins + https://ecosystem.vuejs.press/ecosystem/plugins/pwa/ + https://ecosystem.vuejs.press/ecosystem/plugins/pwa/ + PWA Plugins + PWA Plugins + + + + remove-pwa + https://ecosystem.vuejs.press/ecosystem/plugins/pwa/remove-pwa.html + https://ecosystem.vuejs.press/ecosystem/plugins/pwa/remove-pwa.html + remove-pwa + remove-pwa + + + + Search Plugins + https://ecosystem.vuejs.press/ecosystem/plugins/search/ + https://ecosystem.vuejs.press/ecosystem/plugins/search/ + Search Plugins + Search Plugins + + + + docsearch + https://ecosystem.vuejs.press/ecosystem/plugins/search/docsearch.html + https://ecosystem.vuejs.press/ecosystem/plugins/search/docsearch.html + docsearch + docsearch + + + + Search Plugin Guidelines + https://ecosystem.vuejs.press/ecosystem/plugins/search/guidelines.html + https://ecosystem.vuejs.press/ecosystem/plugins/search/guidelines.html + Search Plugin Guidelines + Search Plugin Guidelines To make VuePress theme support search plugins out of box, we have a set of guidelines that should be followed when creating a search plugin. Component N... + To make VuePress theme support search plugins out of box, we have a set of guidelines that should be followed when creating a search plugin.

+

Component Name

+
    +
  • +

    If a search plugin provides a search box that is suitable to display in navbar or sidebars, it shall be named SearchBox and registered globally.

    +
  • +
  • +

    If a search plugin provides a complex search result component with input and result list that is suitable to display in a single page, it shall be named SearchPanel and registered globally.

    +

    The search plugin shall automatically generate a /search.html page with the SearchPanel component in every locales, but it must not override any existing page.

    +
  • +
+]]>
+
+ + search + https://ecosystem.vuejs.press/ecosystem/plugins/search/search.html + https://ecosystem.vuejs.press/ecosystem/plugins/search/search.html + search + search + + + + slimsearch + https://ecosystem.vuejs.press/ecosystem/plugins/search/slimsearch.html + https://ecosystem.vuejs.press/ecosystem/plugins/search/slimsearch.html + slimsearch + slimsearch + + + + SEO Plugins + https://ecosystem.vuejs.press/ecosystem/plugins/seo/ + https://ecosystem.vuejs.press/ecosystem/plugins/seo/ + SEO Plugins + SEO Plugins + + + + Tool Plugins + https://ecosystem.vuejs.press/ecosystem/plugins/tools/ + https://ecosystem.vuejs.press/ecosystem/plugins/tools/ + Tool Plugins + Tool Plugins + + + + cache + https://ecosystem.vuejs.press/ecosystem/plugins/tools/cache.html + https://ecosystem.vuejs.press/ecosystem/plugins/tools/cache.html + cache + cache + + + + google-tag-manager + https://ecosystem.vuejs.press/ecosystem/plugins/tools/google-tag-manager.html + https://ecosystem.vuejs.press/ecosystem/plugins/tools/google-tag-manager.html + google-tag-manager + google-tag-manager + + + + redirect + https://ecosystem.vuejs.press/ecosystem/plugins/tools/redirect.html + https://ecosystem.vuejs.press/ecosystem/plugins/tools/redirect.html + redirect + redirect + + + + register-components + https://ecosystem.vuejs.press/ecosystem/plugins/tools/register-components.html + https://ecosystem.vuejs.press/ecosystem/plugins/tools/register-components.html + register-components + register-components + + + + theme-default + https://ecosystem.vuejs.press/ecosystem/themes/default/ + https://ecosystem.vuejs.press/ecosystem/themes/default/ + theme-default + theme-default + + + + Built-in Components + https://ecosystem.vuejs.press/ecosystem/themes/default/components.html + https://ecosystem.vuejs.press/ecosystem/themes/default/components.html + Built-in Components + Built-in Components + + + + Config + https://ecosystem.vuejs.press/ecosystem/themes/default/config.html + https://ecosystem.vuejs.press/ecosystem/themes/default/config.html + Config + Config Basic Config hostname Type: string Details: Hostname to be deployed, e.g.: https://example.com locales Type: { [path: string]: Partial&lt;DefaultThemeLocaleData&gt; } Default: ... + 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

+ +

externalLinkIcon

+
    +
  • +

    Type: boolean

    +
  • +
  • +

    Default: true

    +
  • +
  • +

    Details:

    +

    Show external link icon on external links or not.

    +
  • +
+

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
    • +
    +
  • +
+

navbar

+
    +
  • +

    Type: false | NavbarOptions

    +
  • +
  • +

    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 NavbarLink object, a NavbarGroup object, or a string:

    +
      +
    • A NavbarLink 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, could have an optional prefix field. The children field should be a navbar array too, and prefix will be prepended before every link inside it.
    • +
    • A string should be the path to the target page file. It will be converted to a NavbarLink object, using the page title as text, and the page route path as link.
    • +
    +
  • +
  • +

    Example 1:

    +
  • +
+
export default {
+  theme: defaultTheme({
+    navbar: [
+      // NavbarLink
+      {
+        text: 'Foo',
+        link: '/foo/',
+      },
+      // NavbarGroup
+      {
+        text: 'Group',
+        prefix: '/group/',
+        children: ['foo.md', '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: 'SubGroup1',
+            prefix: 'sub1/',
+            children: [
+              'foo.md', // resolved as `/guide/group/sub1/bar.md`
+              'bar.md', // resolved as `/guide/group/sub1/bar.md`
+
+              // an external link
+              {
+                text: 'Example',
+                link: 'https://example.com',
+              },
+            ],
+          },
+          {
+            text: 'SubGroup2',
+            prefix: 'sub2/',
+            // for project links, .md or .html suffix is optional
+            children: [
+              'foo', // resolved as `/guide/group/sub2/foo.md`
+              'bar', // resolved as `/guide/group/sub2/bar.md`
+
+              // link not inside SubGroup2
+              '/baz/', // resolved as `/baz/README.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/',
+          },
+        ],
+      },
+    ],
+  }),
+}
+

logo

+
    +
  • +

    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

+
    +
  • +

    Type: null | string

    +
  • +
  • +

    Details:

    +

    Specify the url of logo image to be used in dark mode.

    +

    You can make use of this option if you want to use different logo config in dark mode.

    +

    Set to null to disable logo in dark mode. Omit this option to use logo in dark mode.

    +
  • +
  • +

    Also see:

    + +
  • +
+

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',
+  }),
+}
+

sidebar

+
    +
  • +

    Type: false | SidebarOptions

    +
  • +
  • +

    Default: 'heading'

    +
  • +
  • +

    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 'heading', 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, an optional collapsible field and an optional prefix field. The children field should be a sidebar array, where prefix will be prepended to every link inside it. 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',
+        prefix: '/foo/',
+        link: '/foo/',
+        children: [
+          // SidebarItem
+          {
+            text: 'github',
+            link: 'https://github.com',
+            children: [],
+          },
+          // string - page file path
+          'bar.md', // resolved to `/foo/bar.md`
+          '/ray.md', // resolved to `/ray.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',
+          // prefix will be prepended to relative paths
+          children: [
+            'introduction.md', // resolved to `/guide/introduction.md`
+            'getting-started.md', // resolved to `/guide/getting-started.md`
+          ],
+        },
+      ],
+      '/reference/': 'heading',
+    },
+  }),
+}
+
    +
  • Example 3:
  • +
+
export default {
+  theme: defaultTheme({
+    // collapsible sidebar
+    sidebar: {
+      '/reference/': [
+        {
+          text: 'VuePress Reference',
+          collapsible: true,
+          // for project links, .md or .html suffix is optional
+          children: ['cli', 'config'],
+        },
+        {
+          text: 'Bundlers Reference',
+          collapsible: true,
+          // prefix can be a relative path, which is equivalent to `prefix: /reference/bundler/`
+          prefix: 'bundler/',
+          children: ['vite', 'webpack'],
+        },
+      ],
+    },
+  }),
+}
+

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.
    • +
    • ...
    • +
    +

    You can override this global option via sidebarDepth frontmatter in your pages.

    +
  • +
+

editLink

+
    +
  • +

    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:

    +

    | Pattern | Description |
    +|

    +
  • +
+]]>
+
+ + Extending + https://ecosystem.vuejs.press/ecosystem/themes/default/extending.html + https://ecosystem.vuejs.press/ecosystem/themes/default/extending.html + Extending + 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 them... + 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
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 alias for every non-global components 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 { defineUserConfig } from 'vuepress'
+import { getDirname, path } from 'vuepress/utils'
+
+const __dirname = import.meta.dirname || getDirname(import.meta.url)
+
+export default defineUserConfig({
+  theme: defaultTheme(),
+  alias: {
+    '@theme/HomeFooter.vue': path.resolve(
+      __dirname,
+      './components/MyHomeFooter.vue',
+    ),
+  },
+})
+

Modifying Behavior

+

Most of the core behaviors of the default theme have been extracted into a composable API or util function, and also provide aliases with the @theme prefix.

+

For example, if you want to add some default values ​​to the theme data of the default theme, you can override the useThemeData function of @theme/useThemeData.

+

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 type { DefaultThemeOptions } from '@vuepress/theme-default'
+import { defaultTheme } from '@vuepress/theme-default'
+import type { Theme } from 'vuepress/core'
+import { getDirname, path } from 'vuepress/utils'
+
+const __dirname = import.meta.dirname || getDirname(import.meta.url)
+
+export const childTheme = (options: DefaultThemeOptions): Theme => ({
+  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',
+    ),
+  },
+})
+
]]>
+ +
+ + Frontmatter + https://ecosystem.vuejs.press/ecosystem/themes/default/frontmatter.html + https://ecosystem.vuejs.press/ecosystem/themes/default/frontmatter.html + Frontmatter + Frontmatter + + + + Locale Config + https://ecosystem.vuejs.press/ecosystem/themes/default/locale.html + https://ecosystem.vuejs.press/ecosystem/themes/default/locale.html + Locale Config + 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 tr... + 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: '简体中文',
+      },
+    },
+  }),
+}
+

navbarLabel

+
    +
  • +

    Type: null | string

    +
  • +
  • +

    Details:

    +

    aria-label value for main navigation in navbar.

    +
  • +
+

pageNavbarLabel

+
    +
  • +

    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.

    +
  • +
+

toggleColorMode

+
    +
  • +

    Type: string

    +
  • +
  • +

    Default: 'toggle color mode'

    +
  • +
  • +

    Details:

    +

    Title text for the color mode toggle button.

    +

    This is mainly for a11y purpose.

    +
  • +
  • +

    Also see:

    + +
  • +
+

toggleSidebar

+
    +
  • +

    Type: string

    +
  • +
  • +

    Default: 'toggle sidebar'

    +
  • +
  • +

    Details:

    +

    Title text for sidebar toggle button.

    +

    This is mainly for a11y purpose.

    +
  • +
+

prev

+
    +
  • +

    Type: string | false

    +
  • +
  • +

    Default: 'Prev'

    +
  • +
  • +

    Details:

    +

    Text for the previous page navigation button.

    +

    Set to false to disable the previous page navigation button.

    +
  • +
+

next

+
    +
  • +

    Type: string | false

    +
  • +
  • +

    Default: 'Next'

    +
  • +
  • +

    Details:

    +

    Text for the next page navigation button.

    +

    Set to false to disable the next page navigation button.

    +
  • +
+]]>
+
+ + Markdown + https://ecosystem.vuejs.press/ecosystem/themes/default/markdown.html + https://ecosystem.vuejs.press/ecosystem/themes/default/markdown.html + Markdown + Markdown + + + + Plugins Config + https://ecosystem.vuejs.press/ecosystem/themes/default/plugin.html + https://ecosystem.vuejs.press/ecosystem/themes/default/plugin.html + Plugins Config + 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... + 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.activeHeaderLinks

+ +

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.copyCode

+
    +
  • +

    Type: CopyCodePluginOptions | boolean

    +
  • +
  • +

    Default: true

    +
  • +
  • +

    Details:

    +

    Enable @vuepress/plugin-copy-code or not.

    +

    Object value is supported as plugin options.

    +
  • +
+

themePlugins.git

+ +

themePlugins.hint

+ +

themePlugins.linksCheck

+ +

themePlugins.mediumZoom

+ +

themePlugins.nprogress

+ +

themePlugins.prismjs

+ +

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.

    +
  • +
+

themePlugins.tab

+ +]]>
+
+ + Styles + https://ecosystem.vuejs.press/ecosystem/themes/default/styles.html + https://ecosystem.vuejs.press/ecosystem/themes/default/styles.html + Styles + Styles + + + + @vuepress/helper + https://ecosystem.vuejs.press/ecosystem/tools/helper/ + https://ecosystem.vuejs.press/ecosystem/tools/helper/ + @vuepress/helper + @vuepress/helper + + + + Client Related + https://ecosystem.vuejs.press/ecosystem/tools/helper/client.html + https://ecosystem.vuejs.press/ecosystem/tools/helper/client.html + Client Related + Client Related These functions are only available in @vuepress/helper/client. Composables APIs hasGlobalComponent Check whether a component is registered globally. Tips Local im... + These functions are only available in @vuepress/helper/client.

+

Composables APIs

+

hasGlobalComponent

+

Check whether a component is registered globally.

+
+

Tips

+
    +
  1. Local import of the component does not affect the result.
  2. +
  3. When calling outside setup scope, you need to pass the app instance as the second parameter.
  4. +
+
+
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 // '标题'
+
+

Utils

+

getHeaders

+

Get headers from current page.

+
export const getHeaders: (options: GetHeadersOptions) => MenuItem[]
+

Params:

+
export interface GetHeadersOptions {
+  /**
+   * The selector of the headers.
+   *
+   * It will be passed as an argument to `document.querySelectorAll(selector)`,
+   * so you should pass a `CSS Selector` string.
+   *
+   * @default '[vp-content] h1, [vp-content] h2, [vp-content] h3, [vp-content] h4, [vp-content] h5, [vp-content] h6'
+   */
+  selector?: string
+  /**
+   * Ignore specific elements within the header.
+   *
+   * The Array of `CSS Selector`
+   *
+   * @default []
+   */
+  ignore?: string[]
+  /**
+   * The levels of the headers
+   *
+   * - `false`: No headers.
+   * - `number`: only headings of that level will be displayed.
+   * - `[number, number]: headings level tuple, where the first number should be less than the second number, for example, `[2, 4]` which means all headings from `<h2>` to `<h4>` will be displayed.
+   * - `deep`: same as `[2, 6]`, which means all headings from `<h2>` to `<h6>` will be displayed.
+   *
+   * @default 2
+   */
+  levels?: HeaderLevels
+}
+

Result:

+
export interface Header {
+  /**
+   * The level of the header
+   *
+   * `1` to `6` for `<h1>` to `<h6>`
+   */
+  level: number
+  /**
+   * The title of the header
+   */
+  title: string
+  /**
+   * The slug of the header
+   *
+   * Typically the `id` attr of the header anchor
+   */
+  slug: string
+  /**
+   * Link of the header
+   *
+   * Typically using `#${slug}` as the anchor hash
+   */
+  link: string
+  /**
+   * The children of the header
+   */
+  children: Header[]
+}
+
+export type HeaderLevels = number | 'deep' | false | [number, number]
+
+export type MenuItem = Omit<Header, 'children' | 'slug'> & {
+  element: HTMLHeadElement
+  children?: MenuItem[]
+}
+
Examples +
onMounted(() => {
+  const headers = getHeaders({
+    selector: '[vp-content] :where(h1,h2,h3,h4,h5,h6)',
+    levels: [2, 3], // only h2 and h3
+    ignore: ['.badge'], // ignore the <Badge /> within the header
+  })
+  console.log(headers)
+})
+
+]]>
+
+ + Shared Methods + https://ecosystem.vuejs.press/ecosystem/tools/helper/shared.html + https://ecosystem.vuejs.press/ecosystem/tools/helper/shared.html + Shared Methods + Shared Methods The following functions are available on both Node.js and Client. These functions are available in @vuepress/helper, @vuepress/helper/client, and @vuepress/helper... + The following functions are available on both Node.js and Client.

+

These functions are available in @vuepress/helper, @vuepress/helper/client, and @vuepress/helper/shared.

+

Data Related

+

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
+
Details +
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.
  • +
+

String Related

+
    +
  • 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.

+

Object Related

+
    +
  • +

    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",
    +// }
    +
    +
  • +
+

Date Related

+
    +
  • +

    getDate(x): Convert input x to a date. It can support Date, timestamp, and date string. The support range of date string depends on the Date.parse support range 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,
    +// ]
    +
    +
  • +
+

Link Related

+
    +
  • 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.
  • +
  • isLinkRelative(x): Check if x is not absolute or external 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.
  • +
+]]>
+
+ + Styles + https://ecosystem.vuejs.press/ecosystem/tools/helper/style.html + https://ecosystem.vuejs.press/ecosystem/tools/helper/style.html + Styles + Styles The following styles are provided. Normalize @vuepress/helper/normalize.css is a CSS file that normalizes the default styles of the browser. It is recommended to import i... + The following styles are provided.

+

Normalize

+

@vuepress/helper/normalize.css is a CSS file that normalizes the default styles of the browser. It is recommended to import it in community themes.

+]]>
+
+ + blog + https://ecosystem.vuejs.press/ecosystem/plugins/blog/blog/ + https://ecosystem.vuejs.press/ecosystem/plugins/blog/blog/ + blog + blog + + + + Config + https://ecosystem.vuejs.press/ecosystem/plugins/blog/blog/config.html + https://ecosystem.vuejs.press/ecosystem/plugins/blog/blog/config.html + Config + Config Plugin Options getInfo Type: (page: Page) =&gt; Record&lt;string, unknown&gt; Required: No Reference: Details: Function getting article info. Article info will be injected in rout... + 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

+
    +
  • Type: string
  • +
  • Default: <!-- more -->
  • +
  • Reference: + +
  • +
  • Details: Separator used to split excerpt from page content.
  • +
+

excerptLength

+
    +
  • +

    Type: number

    +
  • +
  • +

    Default: 300

    +
  • +
  • +

    Reference:

    + +
  • +
  • +

    Details:

    +

    Length of excerpt when auto generating.

    +
    +

    Tips

    +

    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.

    +
    +

    Tips

    +

    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.

    +
    +

    Tips

    +

    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>[]
+}
+
]]>
+
+ + Guide + https://ecosystem.vuejs.press/ecosystem/plugins/blog/blog/guide.html + https://ecosystem.vuejs.press/ecosystem/plugins/blog/blog/guide.html + Guide + Guide 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&apos;t... + 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, unknown> = {
+          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/client'
+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" :key="path">
+        <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/client'
+
+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 v-if="categoryMap.currentItems" class="article-wrapper">
+      <div v-if="!categoryMap.currentItems.length">Nothing in here.</div>
+      <article
+        v-for="{ info, path } in categoryMap.currentItems"
+        :key="path"
+        class="article"
+        @click="$router.push(path)"
+      >
+        <header class="title">
+          {{ info.title }}
+        </header>
+        <hr />
+        <div class="article-info">
+          <span v-if="info.author" class="author"
+            >Author: {{ info.author }}</span
+          >
+          <span v-if="info.date" 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 ParentLayout from '@vuepress/theme-default/layouts/Layout.vue'
+import { RouteLink } from 'vuepress/client'
+
+import ArticleList from '../components/ArticleList.vue'
+
+const stars = useBlogType('star')
+</script>
+
+<template>
+  <div v-if="stars.items?.length" class="article-wrapper">
+    <article
+      v-for="{ info, path } in stars.items"
+      :key="path"
+      class="article"
+      @click="$router.push(path)"
+    >
+      <header class="title">
+        {{ info.title }}
+      </header>
+      <hr />
+      <div class="article-info">
+        <span v-if="info.author" class="author">Author: {{ info.author }}</span>
+        <span v-if="info.date" 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 v-else>Nothing in here.</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.

+
+]]>
+
+ + comment + https://ecosystem.vuejs.press/ecosystem/plugins/blog/comment/ + https://ecosystem.vuejs.press/ecosystem/plugins/blog/comment/ + comment + comment + + + + Guide + https://ecosystem.vuejs.press/ecosystem/plugins/blog/comment/guide.html + https://ecosystem.vuejs.press/ecosystem/plugins/blog/comment/guide.html + Guide + Guide Setting Options You can both set options with plugin options on Node side and set options in client config file on Browser side. With Plugin Options With Client Config Fil... + Setting Options +

You can both set options with plugin options on Node side and set options in client config file on Browser side.

+

With Plugin Options

+
import { commentPlugin } from '@vuepress/plugin-comment'
+
+// .vuepress/config.ts
+export default {
+  plugins: [
+    commentPlugin({
+      provider: 'Artalk', // Artalk | Giscus | Waline | Twikoo
+
+      // other options here
+      // ...
+    }),
+  ],
+}
+

With Client Config File

+
import {
+  defineArtalkConfig,
+  // defineGiscusConfig,
+  // defineTwikooConfig,
+  // defineWalineConfig,
+} from '@vuepress/plugin-comment/client'
+import { defineClientConfig } from 'vuepress/client'
+
+defineArtalkConfig({
+  // 选项
+})
+

But here are some limitations you should remember:

+
    +
  • +

    provider, locales and other resource related option must be set in plugin options.

    +

    To ensure tree-shaking works, we must optimize entries at node so that bundler can understand which resource should be included in the final bundle.

    +

    These options will be marked with

    +
  • +
  • +

    Options which can not be serialized to JSON must be set in client config.

    +

    Options that receives function values can not be set in plugin options, as plugins are running under Node.js environment, so we can not pass these values and their contexts to browser.

    +

    These options will be marked with

    +
  • +
+

Adding Comment

+

This plugin globally registers a component <CommentService />.

+
    +
  • If you are a user, you should use alias and layout slots to insert the component. We recommended you to insert the comment component (<CommentService />) after the <PageNav /> component, and the current page is a demo with default theme.
  • +
  • If you are a theme developer, you should insert this component in the layout of your theme.
  • +
+

By default, <CommentService /> component is enabled globally, and you can use comment option in both plugin options and page frontmatter to control it.

+
    +
  • You can disable it locally by setting comment: false in page frontmatter.
  • +
  • To keep it globally disabled, please set comment to false in the plugin options. Then you can set comment: true in page frontmatter to enable it locally.
  • +
+

You can set commentID option in page frontmatter to customize comment ID, which is used to identify comment storage item to use for a page. By default it will be the path of the page, which means if you are deploying the site to multiple places, page with same content across sites will share the same comment data.

+

Available Comment Services

+

Currently, you can choose from Giscus, Waline, Artalk and Twikoo.

+
+

Recommended comment services

+
    +
  • Facing programmers and developers: Giscus
  • +
  • Facing general public: Waline
  • +
+
+

Common Options

+

provider

+
    +
  • Type: "Artalk" | "Giscus" | "Twikoo" | "Waline" | "None"
  • +
  • Default: "None"
  • +
  • Details: Comment service provider.
  • +
+

comment

+
    +
  • Type: boolean
  • +
  • Default: true
  • +
  • Details: Whether to enable comment feature by default.
  • +
+]]>
+
+ + feed + https://ecosystem.vuejs.press/ecosystem/plugins/blog/feed/ + https://ecosystem.vuejs.press/ecosystem/plugins/blog/feed/ + feed + feed + + + + Channel Config + https://ecosystem.vuejs.press/ecosystem/plugins/blog/feed/channel.html + https://ecosystem.vuejs.press/ecosystem/plugins/blog/feed/channel.html + Channel Config + Channel Config The channel plugin option is used to config the feed channel. channel.title Type: string Default: SiteConfig.title Channel title channel.link Type: string Default... + The channel plugin option is used to config the feed channel.

+

channel.title

+
    +
  • Type: string
  • +
  • Default: SiteConfig.title
  • +
+

Channel title

+

channel.link

+
    +
  • 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

+

channel.copyright

+
    +
  • +

    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 Websub.

+
+
+]]>
+
+ + Plugin Config + https://ecosystem.vuejs.press/ecosystem/plugins/blog/feed/config.html + https://ecosystem.vuejs.press/ecosystem/plugins/blog/feed/config.html + Plugin Config + 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: boole... + 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 }) =>
    +  Boolean(frontmatter.feed ?? (filePathRelative && !frontmatter.home))
    +
  • +
+

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).

+
+

Tips

+

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.

+
+

Tips

+

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.

+]]>
+
+ + Frontmatter Config + https://ecosystem.vuejs.press/ecosystem/plugins/blog/feed/frontmatter.html + https://ecosystem.vuejs.press/ecosystem/plugins/blog/feed/frontmatter.html + Frontmatter Config + 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:... + 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.

+
+

copyright

+
    +
  • 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.

+
+]]>
+
+ + Feed Getter + https://ecosystem.vuejs.press/ecosystem/plugins/blog/feed/getter.html + https://ecosystem.vuejs.press/ecosystem/plugins/blog/feed/getter.html + Feed Getter + Feed Getter You can take full control of feed items generation by setting getter in the plugin options. getter.title Type: (page: Page, app: App) =&gt; string Item title getter get... + You can take full control of feed items generation by setting getter in the plugin options.

+

getter.title

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

Item title getter

+

getter.link

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

Item link getter

+

getter.description

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

Item description getter

+
+

Tips

+

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, app: App) => string
  • +
+

Item content getter

+

getter.author

+
    +
  • Type: (page: Page, app: App) => 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, app: App) => 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, app: App) => 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, app: App) => Date | undefined
  • +
+

Item release date getter

+

getter.lastUpdateDate

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

Item last update date getter

+

getter.image

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

Item Image Getter

+
+

Ensure it's returning a full URL

+
+

getter.contributor

+
    +
  • Type: (page: Page, app: App) => 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
+}
+
+

getter.copyright

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

Item copyright getter

+]]>
+
+ + Guide + https://ecosystem.vuejs.press/ecosystem/plugins/blog/feed/guide.html + https://ecosystem.vuejs.press/ecosystem/plugins/blog/feed/guide.html + Guide + 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 accor... + 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 atom and rss 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.

+]]>
+
+ + sass-palette + https://ecosystem.vuejs.press/ecosystem/plugins/development/sass-palette/ + https://ecosystem.vuejs.press/ecosystem/plugins/development/sass-palette/ + sass-palette + sass-palette + + + + Config + https://ecosystem.vuejs.press/ecosystem/plugins/development/sass-palette/config.html + https://ecosystem.vuejs.press/ecosystem/plugins/development/sass-palette/config.html + Config + Config Options id Type: string Required: Yes Identifier for palette, used to avoid duplicate registration. config Type: string Default: `.vuepress/styles/${id}-palette.scss` Use... + Options +

id

+
    +
  • Type: string
  • +
  • Required: Yes
  • +
+

Identifier for palette, used to avoid duplicate registration.

+

config

+
    +
  • Type: string
  • +
  • Default: `.vuepress/styles/${id}-palette.scss`
  • +
+

User config file path, relative to source dir.

+
+

Tips

+

This is the file where user set style variables.

+

The default filename is starting with ID above.

+
+

defaultConfig

+
    +
  • Type: string
  • +
  • Default: "@vuepress/plugin-sass-palette/styles/default/config.scss"
  • +
+

Default config file path, should be absolute path.

+
+

Tips

+

This is the file you should use to provide default values with !default.

+
+

palette

+
    +
  • Type: string
  • +
  • Default: `.vuepress/styles/${id}-palette.scss`
  • +
+

User palette file path, relative to source dir.

+
+

Tips

+

This is the file where user control injected CSS variables. All the variables will be converted in to kebab-case and injected.

+

The default filename is starting with ID above.

+
+

defaultPalette

+
    +
  • Type: string
  • +
  • Default: "@vuepress/plugin-sass-palette/styles/default/palette.scss"
  • +
+

Default palette file path, should be absolute path.

+
+

Tips

+

This is the file you should use to provide default CSS variables with !default. All the variable will be converted in to kebab-case and injected.

+
+

generator

+
    +
  • Type: string
  • +
  • Required: No
  • +
+

Custom generator, used to generate derivative values with the above config.

+

E.g.: You may want to provide a $theme-color-light based on $theme-color.

+

style

+
    +
  • Type: string
  • +
  • Required: No
  • +
+

User style file path, relative to source dir.

+

Alias

+

Available alias are:

+
    +
  • config: @sass-palette/${id}-config (based on id)
  • +
  • palette: @sass-palette/${id}-palette (based on id)
  • +
  • style: @sass-palette/${id}-style (only available when style option is set)
  • +
  • helper: @sass-palette/helper
  • +
+]]>
+
+ + Guide + https://ecosystem.vuejs.press/ecosystem/plugins/development/sass-palette/guide.html + https://ecosystem.vuejs.press/ecosystem/plugins/development/sass-palette/guide.html + Guide + Guide Comparing to , this plugin allows you to: Derive related styles based on user configuration Provide style customization similar to theme in plugins Group applications acro... + Comparing to @vuepress/plugin-palette, this plugin allows you to:

+
    +
  • Derive related styles based on user configuration
  • +
  • Provide style customization similar to theme in plugins
  • +
  • Group applications across multiple plugins or themes via id option
  • +
+

Before using the plugin, you need to understand the id option, as well as three styling concepts: configuration, palette and generator.

+

ID Option

+

To get started, you should understand that this plugin is designed to take across plugins and theme (unlike the official one only for theme).

+

We are providing id option to do that, and using this plugin (by calling useSassPalette) with same ID won't have any side effects. Also, all the alias and module names have an ID prefix.

+

This will allow you to:

+
    +
  • +

    Share same style system across your plugins (or themes) using same ID without any side effects.

    +

    All aliases and module names have an ID prefix, which means you can use a set of style variables within your plugins (or theme) to unify your styles without being affected by other plugins (or themes).

    +

    Users can configure all color variables, breakpoints, and other style configurations in the same file and have them automatically applied on themes and plugins with the same ID.

    +
    +

    Example

    +

    vuepress-theme-hope and other related plugins use the same ID hope to call the plugin, so the styles configured by the user in the theme will automatically take effect in all plugins.

    +
    +
  • +
  • +

    With different ID, plugins and theme won't affect others. We recommend you to set the id variable with your plugin name.

    +

    With the default settings, users will set your plugin style under .vuepress/styles directory with Sass files starting with your ID prefix. And you can access the variables you need with ${id}-config and ${id}-palette.

    +
    +

    Example

    +

    vuepress-theme-hope is using ID "hope", and just imagine a vuepress-plugin-abc is using "abc". They can get their own variables with module name hope-config hope-palette and abc-config abc-palette.

    +
    +
  • +
  • +

    Calling the plugin with the same ID has no side effects.

    +
    +

    example

    +

    vuepress-theme-hope and other related plugins use the same ID hope to call the plugin.

    +
    +
  • +
+

Config

+

Config file is used for Sass variable only. It holds Sass variables which can be used via ${id}-config in other files later.

+

You can specify a file (probably in .vuepress/styles/ directory) as user config file. So you can get the module containing every variable later in Sass files. Also, you are able to provide a default config files where you can place fallback values for variables with !default.

+
An example +

Imagine you are invoking the plugin with the following options in vuepress-plugin-abc:

+
useSassPalette(app, {
+  id: 'abc',
+  defaultConfig: 'vuepress-plugin-abc/styles/config.scss',
+})
+

User config file:

+
$navbar-height: 3.5rem;
+

Default config file:

+
$navbar-height: 2rem !default;
+$sidebar-width: 18rem !default;
+

You can get the following variables in the plugin Sass files:

+
// <style lang="scss"> block in vue sfc or scss file directly imported in scripts
+@debug abc-config.$navbar-height; // 3.5rem
+@debug abc-config.$sidebar-width; // 18rem
+
+

Limitations

+

We are using additionalData options to let ${id}-config module available in your styles, but this has some limitations.

+

additionalData only works on Scss entry, so ${id}-config is available only in :

+
    +
  • <style lang="scss"> block in component files
  • +
  • Scss files imported by script files directly (e.g.: import "./a-scss-file.scss" in client app enhance file).
  • +
+

If the Scss file is not imported directly, but is imported through @use or @import api, the module won't be available. So that in this case, you should manually import the module with @use "@sass-palette/${id}-config";.

+

Palette

+

Palette files are used for CSS variable injection, where each variable will be injected in to root with kebab-name of variable name.

+

You can specify a file (probably in .vuepress/styles/ directory) as user palette file, and the default filename is ${id}-palette.scss. Also, you are able to provide a default palette files where you can place fallback values for variables with !default.

+
An example +

Imagine you are invoking the plugin with the following options in vuepress-plugin-abc:

+
useSassPalette(app, {
+  id: 'abc',
+  defaultPalette: 'vuepress-plugin-abc/styles/palette.scss',
+})
+

If users are setting:

+
$color-a: red;
+

And you are providing a default palette file with:

+
$color-a: blue !default;
+$color-b: green !default;
+

Then the below CSS variables will be available under root selector:

+
:root {
+  --color-a: red;
+  --color-b: green;
+}
+
+

Like config file, palette file provides a module named ${$id}-palette (also including generator values), and it is also limited by additionalData option, so you should import the module manually if you want to use it in other Sass files.

+

Color Settings

+

Since the default theme is providing darkmode, so you probably want different colors under lightmode and darkmode.

+

To achieve that, you should set color variables with a map containing light and dark keys. Later, the plugin will generate different colors for you.

+
An example +
// User palette
+$text-color: (
+  light: #222,
+  dark: #999,
+);
+
+// Default palette
+$text-color: (
+  light: #2c3e50,
+  dark: #9e9e9e,
+) !default;
+$bg-color: (
+  light: #fff,
+  dark: #1e1e1e,
+) !default;
+

Then you will get:

+
:root {
+  --text-color: #222;
+  --bg-color: #fff;
+}
+
+[data-theme='dark'] {
+  --text-color: #999;
+  --bg-color: #1e1e1e;
+}
+
+

Allowed Variable Types

+

Only colors (or color map for light/dark mode), length and strings are allowed in palette. Any other type will be dropped.

+
+

Why only allow color and length besides strings

+

In common situations, you probably only want to make calculations with color and length. So it's quite safe to drop other type support, because any other value you want can be converted to string.

+
Example +

If you want a --move-transition with width 0.3s ease, you can use strings:

+
// this will be regarded as a list with (length, time, function) by Sass
+// and will trigger a warning and be dropped by plugin
+$moveTransition: width 0.3s ease;
+
+// this will get what you want
+// :root {
+//   --move-transition: width 0.3s ease
+// }
+$moveTransition: 'width 0.3s ease';
+
+
+

Helper

+

We are exposing internal functions which @vuepress/plugin-sass-palette uses, as a helper module.

+

You can use this helper with @sass-palette/helper alias and call its function to achieve similar features yourself.

+

Generator

+

A generator file is facing developers to generate derived values based on palette file variables.

+

You can access variables from palette file directly in this file and generate new values based on them.

+

Variables in generator file will be also injected as CSS variables like palette, and they will be available in palette module.

+
Example +

You may want a $theme-color-light based on $theme-color. So you can write a generator like this:

+
@use 'sass:color';
+@use '@sass-palette/helper';
+
+$theme-color-light: (
+  light: color.scale(helper.get-color($theme-color), $lightness: 10%),
+  dark: color.scale(helper.get-dark-color($theme-color), $lightness: 10%),
+) !default;
+

You can also generate values based on variables provided by config files by importing it:

+
// generator with id "abc"
+@use 'sass:color';
+@use '@sass-palette/abc-config';
+@use '@sass-palette/helper';
+
+$code-c-bg: abc-config.$highlighter == 'shiki'? #fff: #f8f8f8;
+
+

User Styles

+

If you are a theme developer, you may probably want to provide your users a way to custom your theme or the site.

+

In this case you should set style option as the user style file when using this plugin.

+

Later, you should manually include user style file by importing @sass-palette/${id}-style after your theme styles.

+
+

Tips

+

@sass-palette/${id}-style is an alias to user style file, and you can import it in JS/TS/SASS.

+
+]]>
+
+ + revealjs + https://ecosystem.vuejs.press/ecosystem/plugins/markdown/revealjs/ + https://ecosystem.vuejs.press/ecosystem/plugins/markdown/revealjs/ + revealjs + revealjs + + + + Slide Demo + https://ecosystem.vuejs.press/ecosystem/plugins/markdown/revealjs/demo.html + https://ecosystem.vuejs.press/ecosystem/plugins/markdown/revealjs/demo.html + Slide Demo + + + + Reveal.js Themes + https://ecosystem.vuejs.press/ecosystem/plugins/markdown/revealjs/themes.html + https://ecosystem.vuejs.press/ecosystem/plugins/markdown/revealjs/themes.html + Reveal.js Themes + Reveal.js Themes auto Based on theme mode. black white league beige sky night serif simple solarized blood moon + auto +
+

Based on theme mode.

+
+]]>
+
+ + pwa + https://ecosystem.vuejs.press/ecosystem/plugins/pwa/pwa/ + https://ecosystem.vuejs.press/ecosystem/plugins/pwa/pwa/ + pwa + pwa + + + + Config + https://ecosystem.vuejs.press/ecosystem/plugins/pwa/pwa/config.html + https://ecosystem.vuejs.press/ecosystem/plugins/pwa/pwa/config.html + Config + Config Options showInstall Type: boolean Default: false Details: Whether display install button when Service Worker is first registered successfully. manifest Type: AppManifest ... + 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.

    +
    +

    Tips

    +

    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: "disable" | "available" | "hint" | "force"

    +
  • +
  • +

    Default: "available"

    +
  • +
  • +

    Details:
    +Control logic when new content is found.

    +
      +
    • +

      "disable": 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.

      +
      +

      Tips

      +

      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

      +
      +

      Caution

      +

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

      +
      +
    • +
    +
    +

    Tips

    +

    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.maskIcon

+
    +
  • Type: string
  • +
  • Details: Safari mask icon
  • +
+

apple.statusBarColor

+
    +
  • Type: 'black-translucent' | 'black' | 'default
  • +
  • Details: Status bar color for Safari
  • +
+

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]: Partial<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)
  • +
  • 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

+
    +
  • +

    Details:

    +

    Returns the event emitter of this plugin.

    +

    You can add listener function to events that provided by register-service-worker.

    +
  • +
  • +

    Example:

    +
  • +
+
import { usePwaEvent } from '@vuepress/plugin-pwa/client'
+
+export default {
+  setup(): void {
+    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(): void {
+    onMounted(() => {
+      forceUpdate()
+    })
+  },
+}
+

registerSW

+
    +
  • +

    Details:

    +

    Register service worker manually.

    +
  • +
  • +

    Parameters:

    +
  • +
+

| Parameter | Type | Description |
+|

+]]>
+
+ + Guide + https://ecosystem.vuejs.press/ecosystem/plugins/pwa/pwa/guide.html + https://ecosystem.vuejs.press/ecosystem/plugins/pwa/pwa/guide.html + Guide + Guide Intro Make your VuePress site a Progressive Web Application (PWA)[1]. This plugin uses workbox-build to generate service worker file, and uses register-service-worker to r... + Intro +

Make your VuePress site a Progressive Web Application (PWA)[1].

+

This plugin uses workbox-build to generate service worker file, and uses register-service-worker 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.

+
+

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.

+ +

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 type="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 type="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_apps for details. ↩︎

    +
  2. +
  3. Service Worker Introduction

    +
      +
    1. +

      The Service Worker will get and cache all the files registered in it during the registration process.

      +
    2. +
    3. +

      After the registration complete, the Service Worker is activated, and starts to proxy and control all your requests.

      +
    4. +
    5. +

      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.

      +
    6. +
    7. +

      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.

      +
    8. +
    + ↩︎
  4. +
  5. 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.

    +
    +

    Tips

    +

    For Manifest standards and specifications, please see MDN Web app manifests and W3C Manifest.

    +
    + ↩︎
  6. +
  7. 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. ↩︎ ↩︎

    +
  8. +
  9. SSG: Static Site Generation, ↩︎

    +
  10. +
  11. SEO: Search Engine Optimization. ↩︎

    +
  12. +
  13. SPA: Single Page Application, most of them only have the homepage, and use history mode to handle routing instead of actually navigating between pages. ↩︎

    +
  14. +
+
+]]>
+
+ + seo + https://ecosystem.vuejs.press/ecosystem/plugins/seo/seo/ + https://ecosystem.vuejs.press/ecosystem/plugins/seo/seo/ + seo + seo + + + + Config + https://ecosystem.vuejs.press/ecosystem/plugins/seo/seo/config.html + https://ecosystem.vuejs.press/ecosystem/plugins/seo/seo/config.html + Config + Config hostname Type: string Required: Yes Details: Deploy hostname. author Type: Author Required: No Details: Default author. autoDescription Type: boolean Default: true Detail... + 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 = AuthorInfo | AuthorInfo[] | AuthorName | AuthorName[]
    +
  • +
  • +

    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 */
    +  ogpInfo: 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>.

    +
  • +
+]]>
+
+ + Guide + https://ecosystem.vuejs.press/ecosystem/plugins/seo/seo/guide.html + https://ecosystem.vuejs.press/ecosystem/plugins/seo/seo/guide.html + Guide + Guide This plugin will make your site fully support Open Content Protocol OGP and JSON-LD 1.1 to enhance the SEO of the site. Out of Box The plugin works out of the box. Without... + This plugin will make your site fully support Open Content Protocol OGP and JSON-LD 1.1 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 Name | Value |
+| :

+]]>
+
+ + sitemap + https://ecosystem.vuejs.press/ecosystem/plugins/seo/sitemap/ + https://ecosystem.vuejs.press/ecosystem/plugins/seo/sitemap/ + sitemap + sitemap + + + + Config + https://ecosystem.vuejs.press/ecosystem/plugins/seo/sitemap/config.html + https://ecosystem.vuejs.press/ecosystem/plugins/seo/sitemap/config.html + Config + 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: E... + 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.

    +
  • +
+
+

Tips

+

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.

    +
  • +
+]]>
+
+ + Frontmatter + https://ecosystem.vuejs.press/ecosystem/plugins/seo/sitemap/frontmatter.html + https://ecosystem.vuejs.press/ecosystem/plugins/seo/sitemap/frontmatter.html + Frontmatter + Frontmatter sitemap Type: SitemapFrontmatterOptions | false Details: false means exclude the page from sitemap. sitemap.changefreq Type: &quot;always&quot; | &quot;hourly&quot; | &quot;daily&quot; | &quot;weekly&quot;... + 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.

    +
  • +
+]]>
+
+ + Guide + https://ecosystem.vuejs.press/ecosystem/plugins/seo/sitemap/guide.html + https://ecosystem.vuejs.press/ecosystem/plugins/seo/sitemap/guide.html + Guide + 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. I... + 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.

+

Control Sitemap Link

+

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: /
+
+]]>
+
+ + Bundler Related + https://ecosystem.vuejs.press/ecosystem/tools/helper/node/bundler.html + https://ecosystem.vuejs.press/ecosystem/tools/helper/node/bundler.html + Bundler Related + Bundler Related Bundler functions for appending or modifying bundler options in theme and plugins. These functions are only available via @vuepress/helper. Tips All functions sh... + Bundler functions for appending or modifying bundler options in theme and plugins.

+

These functions are only available via @vuepress/helper.

+
+

Tips

+

All functions should be called in extendsBundlerOptions lifecycle hook.

+

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: RegExp | string[] | string,
+) => 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<Buffer | string>
+
+  /**
+   * 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,
+    path,
+  }: CustomServerOptions,
+) => void
+
Example +
import { useCustomDevServer } from '@vuepress/helper'
+
+// handle `/api/` path
+useCustomDevServer(bundlerOptions, app, {
+  path: '/api/',
+  response: async () => getData(),
+  errMsg: 'Unexpected api error',
+})
+
+

Vite Related

+
    +
  • +

    addViteOptimizeDepsInclude

    +

    Add modules to Vite optimizeDeps.include list

    +
    +

    Tips

    +

    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 {
    +  addViteOptimizeDepsExclude,
    +  addViteOptimizeDepsInclude,
    +  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',
    +  },
    +})
    +
    +
  • +
+

Webpack Related

+
    +
  • +

    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
    +})
    +
    +
  • +
+]]>
+
+ + Locales Related + https://ecosystem.vuejs.press/ecosystem/tools/helper/node/locales.html + https://ecosystem.vuejs.press/ecosystem/tools/helper/node/locales.html + Locales Related + Locales Related These functions are only available in @vuepress/helper. getFullLocaleConfig A helper function to get full locale config from built-in locale info and user config... + These functions are only available in @vuepress/helper.

+

getFullLocaleConfig

+

A helper function to get full locale config from built-in locale info and user configuration.

+
export interface GetLocaleConfigOption<T extends LocaleData> {
+  app: App
+  default: DefaultLocaleInfo<T>
+  config?: LocaleConfig<T> | undefined
+  name?: string
+}
+
+export const getFullLocaleConfig: <T extends LocaleData>(
+  options: GetLocaleConfigOption<T>,
+) => ExactLocaleConfig<T>
+
    +
  • +

    The app parameter is the VuePress Node app instance.

    +
  • +
  • +

    The default parameter is the default locale info, where this should be an array of locale info settings.

    +

    Each locale info setting should be an tuple with two elements:

    +
      +
    • The first element are an array of lang code that the locale info setting belongs to.
    • +
    • The second element are the locale info setting.
    • +
    +

    An example of default parameter:

    +
    const defaultLocaleInfo = [
    +  [
    +    ['en'],
    +    { title: 'VuePress', description: 'Vue-powered Static Site Generator' },
    +  ],
    +  [
    +    ['zh', 'zh-CN'],
    +    { title: 'VuePress', description: 'Vue 驱动的静态网站生成器' },
    +  ],
    +  [['zh-TW'], { title: 'VuePress', description: 'Vue 驅動的靜態網站生成器' }],
    +]
    +
  • +
  • +

    The config parameter is the user locale config, which is optional.

    +

    It should be an object with localePath as key and partial locale info setting as value.

    +

    An example of config parameter:

    +
    const userLocaleConfig = {
    +  '/zh/': { description: '由 Vue 驱动的静态网站生成器' },
    +  '/zh-TW/': { description: '由 Vue 驅動的靜態網站生成器' },
    +}
    +
  • +
  • +

    The name parameter is the plugin name, which is optional, only used for logging.

    +
  • +
+

The function will automatically merge the default locale info and user locale config, and return the final locale config, where the user locale config will override the default locale info.

+

The default locale info will be chosen based on the current language of each locale in site config, and when a locale's lang code is not found in the default locale info, it will fallback to the first one of the following that exists:

+
    +
  • locale info of en-US
  • +
  • locale info of en
  • +
  • locale info of first element in the default locale info
  • +
+]]>
+
+ + Page Related + https://ecosystem.vuejs.press/ecosystem/tools/helper/node/page.html + https://ecosystem.vuejs.press/ecosystem/tools/helper/node/page.html + Page Related + Page Related Common information generator for pages. getPageExcerpt Get the excerpt of the page. getPageText Get plain text of the page. + Common information generator for 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
+
]]>
+
+ + Artalk + https://ecosystem.vuejs.press/ecosystem/plugins/blog/comment/artalk/ + https://ecosystem.vuejs.press/ecosystem/plugins/blog/comment/artalk/ + Artalk + Artalk Artalk is a neat self-hosted commenting system that you can easily deploy on your server and put into your front-end page. Come to your blog, or anywhere, place the Artal... + Artalk is a neat self-hosted commenting system that you can easily deploy on your server and put into your front-end page.

+

Come to your blog, or anywhere, place the Artalk comment box, so that the page has rich social functions.

+ +

Install

+
npm i -D artalk
+

Deploy Artalk Server

+

See the Artalk documentation.

+

Configuration

+

Please set provider: "Artalk" and pass your server link to server in the plugin options.

+

For other configuration items, see Artalk Config.

+
+

Tips

+

The plugin retains the el option and inserts Artalk itself on the page. At the same time, the plugin will automatically set the pageTitle, pageKey and site options for you according to the VuePress information.

+
+

Darkmode

+

To let Artalk apply the correct theme, you need to pass a boolean value to <CommentService /> through darkmode prop, representing whether the dark mode is currently enabled.

+]]>
+
+ + Artalk Options + https://ecosystem.vuejs.press/ecosystem/plugins/blog/comment/artalk/config.html + https://ecosystem.vuejs.press/ecosystem/plugins/blog/comment/artalk/config.html + Artalk Options + Artalk Options Config See Artalk Configuration for details. The el pageTitle, pageKey and site options are reserved for plugins, they will be inferred from VuePress config. Two ... + Config +

See Artalk Configuration for details.

+
    +
  • +

    The el pageTitle, pageKey and site options are reserved for plugins, they will be inferred from VuePress config.

    +
  • +
  • +

    Two function options imgUploader and avatarURLBuilder can only be set on client side.

    +
  • +
+

Plugin Config

+

You can directly configure serializable options in the plugin options:

+
import { commentPlugin } from '@vuepress/plugin-comment'
+import { defineUserConfig } from 'vuepress'
+
+export default defineUserConfig({
+  plugins: [
+    commentPlugin({
+      provider: 'Artalk',
+      // other options
+      // ...
+    }),
+  ],
+})
+

Client Config

+

You can use the defineArtalkConfig function to customize Artalk:

+
import { defineArtalkConfig } from '@vuepress/plugin-comment/client'
+import { defineClientConfig } from 'vuepress/client'
+
+defineArtalkConfig({
+  // Artalk config
+})
+
]]>
+
+ + Giscus + https://ecosystem.vuejs.press/ecosystem/plugins/blog/comment/giscus/ + https://ecosystem.vuejs.press/ecosystem/plugins/blog/comment/giscus/ + Giscus + Giscus Giscus is a commenting system based on GitHub Discussion that is easy to start. Preparation Create a public repository and open discussion panel as a place to store comme... + Giscus is a commenting system based on GitHub Discussion that is easy to start.

+ +

Preparation

+
    +
  1. +

    Create a public repository and open discussion panel as a place to store comments.

    +
  2. +
  3. +

    Install the Giscus App to have permission to access the corresponding repository.

    +
  4. +
  5. +

    After completing the above steps, please go to the Giscus page to get your settings.

    +

    You just need to fill in the repository and Discussion categories, then scroll to the "Enable giscus" section at the bottom of the page and obtain four attributes: data-repo, data-repo-id, data-category and data-category-id.

    +
  6. +
+

Config

+

Please set provider: "Giscus" and pass data-repo, data-repo-id, data-category and data-category-id as plugin options as repo, repoId, category categoryId.

+

For other options, see Giscus Config.

+

Theme

+

By default, the theme of Giscus is light or dark (based on darkmode status).

+
+

Darkmode

+

To let Giscus apply the correct theme, you need to pass a boolean value to <CommentService /> via darkmode property, indicating whether darkmode is currently enabled.

+
+

If you want to customize theme in lightmode and darkmode, you can set lightTheme and darkTheme option with a built-in theme keyword or a custom CSS link starting with https://.

+]]>
+
+
+
\ No newline at end of file diff --git a/rss.xsl b/rss.xsl new file mode 100644 index 0000000000..75170d8057 --- /dev/null +++ b/rss.xsl @@ -0,0 +1,506 @@ + + + + + + + RSS Feed + + + + + + +
+ + + +

+ +

+

+ +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Language: + +
Published Date: + +
Last Build Date: + +
Copyright: + +
+ Catetory: + + + , + + +
+
+ +
+ + +
+
+
+ +
+
+ + + + + + + + + + + + + + , + + + + + + + + + + + +
+
+
+ +
+ +
+
+
+ + + +
+
diff --git a/sitemap.xml b/sitemap.xml new file mode 100644 index 0000000000..2935900064 --- /dev/null +++ b/sitemap.xml @@ -0,0 +1,3 @@ + + +https://ecosystem.vuejs.press/ecosystem/2025-01-10T18:07:54.000Zdailyhttps://ecosystem.vuejs.press/ecosystem/plugins/2025-01-10T18:07:54.000Zdailyhttps://ecosystem.vuejs.press/ecosystem/themes/2025-01-10T18:07:54.000Zdailyhttps://ecosystem.vuejs.press/ecosystem/themes/guidelines.html2025-01-10T18:07:54.000Zdailyhttps://ecosystem.vuejs.press/ecosystem/tools/2025-01-10T18:07:54.000Zdailyhttps://ecosystem.vuejs.press/ecosystem/zh/2025-01-10T18:07:54.000Zdailyhttps://ecosystem.vuejs.press/ecosystem/plugins/analytics/2025-01-10T18:07:54.000Zdailyhttps://ecosystem.vuejs.press/ecosystem/plugins/analytics/baidu-analytics.html2025-01-10T18:07:54.000Zdailyhttps://ecosystem.vuejs.press/ecosystem/plugins/analytics/google-analytics.html2025-01-10T18:07:54.000Zdailyhttps://ecosystem.vuejs.press/ecosystem/plugins/analytics/umami-analytics.html2025-01-10T18:07:54.000Zdailyhttps://ecosystem.vuejs.press/ecosystem/plugins/blog/2025-01-10T18:07:54.000Zdailyhttps://ecosystem.vuejs.press/ecosystem/plugins/development/2025-01-10T18:07:54.000Zdailyhttps://ecosystem.vuejs.press/ecosystem/plugins/development/active-header-links.html2025-01-10T18:07:54.000Zdailyhttps://ecosystem.vuejs.press/ecosystem/plugins/development/git.html2025-01-10T18:07:54.000Zdailyhttps://ecosystem.vuejs.press/ecosystem/plugins/development/palette.html2025-01-10T18:07:54.000Zdailyhttps://ecosystem.vuejs.press/ecosystem/plugins/development/reading-time.html2025-01-10T18:07:54.000Zdailyhttps://ecosystem.vuejs.press/ecosystem/plugins/development/rtl.html2025-01-10T18:07:54.000Zdailyhttps://ecosystem.vuejs.press/ecosystem/plugins/development/theme-data.html2025-01-10T18:07:54.000Zdailyhttps://ecosystem.vuejs.press/ecosystem/plugins/development/toc.html2025-01-10T18:07:54.000Zdailyhttps://ecosystem.vuejs.press/ecosystem/plugins/features/2025-01-10T18:07:54.000Zdailyhttps://ecosystem.vuejs.press/ecosystem/plugins/features/back-to-top.html2025-01-10T18:07:54.000Zdailyhttps://ecosystem.vuejs.press/ecosystem/plugins/features/catalog.html2025-01-10T18:07:54.000Zdailyhttps://ecosystem.vuejs.press/ecosystem/plugins/features/copy-code.html2025-01-10T18:07:54.000Zdailyhttps://ecosystem.vuejs.press/ecosystem/plugins/features/copyright.html2025-01-10T18:07:54.000Zdailyhttps://ecosystem.vuejs.press/ecosystem/plugins/features/icon.html2025-01-10T18:07:54.000Zdailyhttps://ecosystem.vuejs.press/ecosystem/plugins/features/medium-zoom.html2025-01-10T18:07:54.000Zdailyhttps://ecosystem.vuejs.press/ecosystem/plugins/features/notice.html2025-01-10T18:07:54.000Zdailyhttps://ecosystem.vuejs.press/ecosystem/plugins/features/nprogress.html2025-01-10T18:07:54.000Zdailyhttps://ecosystem.vuejs.press/ecosystem/plugins/features/photo-swipe.html2025-01-10T18:07:54.000Zdailyhttps://ecosystem.vuejs.press/ecosystem/plugins/features/watermark.html2025-01-10T18:07:54.000Zdailyhttps://ecosystem.vuejs.press/ecosystem/plugins/markdown/2025-01-10T18:07:54.000Zdailyhttps://ecosystem.vuejs.press/ecosystem/plugins/markdown/append-date.html2025-01-10T18:07:54.000Zdailyhttps://ecosystem.vuejs.press/ecosystem/plugins/markdown/links-check.html2025-01-10T18:07:54.000Zdailyhttps://ecosystem.vuejs.press/ecosystem/plugins/markdown/markdown-container.html2025-01-10T18:07:54.000Zdailyhttps://ecosystem.vuejs.press/ecosystem/plugins/markdown/markdown-ext.html2025-01-10T18:07:54.000Zdailyhttps://ecosystem.vuejs.press/ecosystem/plugins/markdown/markdown-hint.html2025-01-10T18:07:54.000Zdailyhttps://ecosystem.vuejs.press/ecosystem/plugins/markdown/markdown-image.html2025-01-10T18:07:54.000Zdailyhttps://ecosystem.vuejs.press/ecosystem/plugins/markdown/markdown-include.html2025-01-10T18:07:54.000Zdailyhttps://ecosystem.vuejs.press/ecosystem/plugins/markdown/markdown-math.html2025-01-10T18:07:54.000Zdailyhttps://ecosystem.vuejs.press/ecosystem/plugins/markdown/markdown-stylize.html2025-01-10T18:07:54.000Zdailyhttps://ecosystem.vuejs.press/ecosystem/plugins/markdown/markdown-tab.html2025-01-10T18:07:54.000Zdailyhttps://ecosystem.vuejs.press/ecosystem/plugins/markdown/prismjs.html2025-01-10T18:07:54.000Zdailyhttps://ecosystem.vuejs.press/ecosystem/plugins/markdown/shiki.html2025-01-10T18:07:54.000Zdailyhttps://ecosystem.vuejs.press/ecosystem/plugins/pwa/2025-01-10T18:07:54.000Zdailyhttps://ecosystem.vuejs.press/ecosystem/plugins/pwa/remove-pwa.html2025-01-10T18:07:54.000Zdailyhttps://ecosystem.vuejs.press/ecosystem/plugins/search/2025-01-10T18:07:54.000Zdailyhttps://ecosystem.vuejs.press/ecosystem/plugins/search/docsearch.html2025-01-10T18:07:54.000Zdailyhttps://ecosystem.vuejs.press/ecosystem/plugins/search/guidelines.html2025-01-10T18:07:54.000Zdailyhttps://ecosystem.vuejs.press/ecosystem/plugins/search/search.html2025-01-10T18:07:54.000Zdailyhttps://ecosystem.vuejs.press/ecosystem/plugins/search/slimsearch.html2025-01-10T18:07:54.000Zdailyhttps://ecosystem.vuejs.press/ecosystem/plugins/seo/2025-01-10T18:07:54.000Zdailyhttps://ecosystem.vuejs.press/ecosystem/plugins/tools/2025-01-10T18:07:54.000Zdailyhttps://ecosystem.vuejs.press/ecosystem/plugins/tools/cache.html2025-01-10T18:07:54.000Zdailyhttps://ecosystem.vuejs.press/ecosystem/plugins/tools/google-tag-manager.html2025-01-10T18:07:54.000Zdailyhttps://ecosystem.vuejs.press/ecosystem/plugins/tools/redirect.html2025-01-10T18:07:54.000Zdailyhttps://ecosystem.vuejs.press/ecosystem/plugins/tools/register-components.html2025-01-10T18:07:54.000Zdailyhttps://ecosystem.vuejs.press/ecosystem/themes/default/2025-01-10T18:07:54.000Zdailyhttps://ecosystem.vuejs.press/ecosystem/themes/default/components.html2025-01-10T18:07:54.000Zdailyhttps://ecosystem.vuejs.press/ecosystem/themes/default/config.html2025-01-10T18:07:54.000Zdailyhttps://ecosystem.vuejs.press/ecosystem/themes/default/extending.html2025-01-10T18:07:54.000Zdailyhttps://ecosystem.vuejs.press/ecosystem/themes/default/frontmatter.html2025-01-10T18:07:54.000Zdailyhttps://ecosystem.vuejs.press/ecosystem/themes/default/locale.html2025-01-10T18:07:54.000Zdailyhttps://ecosystem.vuejs.press/ecosystem/themes/default/markdown.html2025-01-10T18:07:54.000Zdailyhttps://ecosystem.vuejs.press/ecosystem/themes/default/plugin.html2025-01-10T18:07:54.000Zdailyhttps://ecosystem.vuejs.press/ecosystem/themes/default/styles.html2025-01-10T18:07:54.000Zdailyhttps://ecosystem.vuejs.press/ecosystem/tools/helper/2025-01-10T18:07:54.000Zdailyhttps://ecosystem.vuejs.press/ecosystem/tools/helper/client.html2025-01-10T18:07:54.000Zdailyhttps://ecosystem.vuejs.press/ecosystem/tools/helper/shared.html2025-01-10T18:07:54.000Zdailyhttps://ecosystem.vuejs.press/ecosystem/tools/helper/style.html2025-01-10T18:07:54.000Zdailyhttps://ecosystem.vuejs.press/ecosystem/zh/plugins/2025-01-10T18:07:54.000Zdailyhttps://ecosystem.vuejs.press/ecosystem/zh/themes/2025-01-10T18:07:54.000Zdailyhttps://ecosystem.vuejs.press/ecosystem/zh/themes/guidelines.html2025-01-10T18:07:54.000Zdailyhttps://ecosystem.vuejs.press/ecosystem/zh/tools/2025-01-10T18:07:54.000Zdailyhttps://ecosystem.vuejs.press/ecosystem/plugins/blog/blog/2025-01-10T18:07:54.000Zdailyhttps://ecosystem.vuejs.press/ecosystem/plugins/blog/blog/config.html2025-01-10T18:07:54.000Zdailyhttps://ecosystem.vuejs.press/ecosystem/plugins/blog/blog/guide.html2025-01-10T18:07:54.000Zdailyhttps://ecosystem.vuejs.press/ecosystem/plugins/blog/comment/2025-01-10T18:07:54.000Zdailyhttps://ecosystem.vuejs.press/ecosystem/plugins/blog/comment/guide.html2025-01-10T18:07:54.000Zdailyhttps://ecosystem.vuejs.press/ecosystem/plugins/blog/feed/2025-01-10T18:07:54.000Zdailyhttps://ecosystem.vuejs.press/ecosystem/plugins/blog/feed/channel.html2025-01-10T18:07:54.000Zdailyhttps://ecosystem.vuejs.press/ecosystem/plugins/blog/feed/config.html2025-01-10T18:07:54.000Zdailyhttps://ecosystem.vuejs.press/ecosystem/plugins/blog/feed/frontmatter.html2025-01-10T18:07:54.000Zdailyhttps://ecosystem.vuejs.press/ecosystem/plugins/blog/feed/getter.html2025-01-10T18:07:54.000Zdailyhttps://ecosystem.vuejs.press/ecosystem/plugins/blog/feed/guide.html2025-01-10T18:07:54.000Zdailyhttps://ecosystem.vuejs.press/ecosystem/plugins/development/sass-palette/2025-01-10T18:07:54.000Zdailyhttps://ecosystem.vuejs.press/ecosystem/plugins/development/sass-palette/config.html2025-01-10T18:07:54.000Zdailyhttps://ecosystem.vuejs.press/ecosystem/plugins/development/sass-palette/guide.html2025-01-10T18:07:54.000Zdailyhttps://ecosystem.vuejs.press/ecosystem/plugins/markdown/revealjs/2025-01-10T18:07:54.000Zdailyhttps://ecosystem.vuejs.press/ecosystem/plugins/markdown/revealjs/demo.html2025-01-10T18:07:54.000Zdailyhttps://ecosystem.vuejs.press/ecosystem/plugins/markdown/revealjs/themes.html2025-01-10T18:07:54.000Zdailyhttps://ecosystem.vuejs.press/ecosystem/plugins/pwa/pwa/2025-01-10T18:07:54.000Zdailyhttps://ecosystem.vuejs.press/ecosystem/plugins/pwa/pwa/config.html2025-01-10T18:07:54.000Zdailyhttps://ecosystem.vuejs.press/ecosystem/plugins/pwa/pwa/guide.html2025-01-10T18:07:54.000Zdailyhttps://ecosystem.vuejs.press/ecosystem/plugins/seo/seo/2025-01-10T18:07:54.000Zdailyhttps://ecosystem.vuejs.press/ecosystem/plugins/seo/seo/config.html2025-01-10T18:07:54.000Zdailyhttps://ecosystem.vuejs.press/ecosystem/plugins/seo/seo/guide.html2025-01-10T18:07:54.000Zdailyhttps://ecosystem.vuejs.press/ecosystem/plugins/seo/sitemap/2025-01-10T18:07:54.000Zdailyhttps://ecosystem.vuejs.press/ecosystem/plugins/seo/sitemap/config.html2025-01-10T18:07:54.000Zdailyhttps://ecosystem.vuejs.press/ecosystem/plugins/seo/sitemap/frontmatter.html2025-01-10T18:07:54.000Zdailyhttps://ecosystem.vuejs.press/ecosystem/plugins/seo/sitemap/guide.html2025-01-10T18:07:54.000Zdailyhttps://ecosystem.vuejs.press/ecosystem/tools/helper/node/bundler.html2025-01-10T18:07:54.000Zdailyhttps://ecosystem.vuejs.press/ecosystem/tools/helper/node/locales.html2025-01-10T18:07:54.000Zdailyhttps://ecosystem.vuejs.press/ecosystem/tools/helper/node/page.html2025-01-10T18:07:54.000Zdailyhttps://ecosystem.vuejs.press/ecosystem/zh/plugins/analytics/2025-01-10T18:07:54.000Zdailyhttps://ecosystem.vuejs.press/ecosystem/zh/plugins/analytics/baidu-analytics.html2025-01-10T18:07:54.000Zdailyhttps://ecosystem.vuejs.press/ecosystem/zh/plugins/analytics/google-analytics.html2025-01-10T18:07:54.000Zdailyhttps://ecosystem.vuejs.press/ecosystem/zh/plugins/analytics/umami-analytics.html2025-01-10T18:07:54.000Zdailyhttps://ecosystem.vuejs.press/ecosystem/zh/plugins/blog/2025-01-10T18:07:54.000Zdailyhttps://ecosystem.vuejs.press/ecosystem/zh/plugins/development/2025-01-10T18:07:54.000Zdailyhttps://ecosystem.vuejs.press/ecosystem/zh/plugins/development/active-header-links.html2025-01-10T18:07:54.000Zdailyhttps://ecosystem.vuejs.press/ecosystem/zh/plugins/development/git.html2025-01-10T18:07:54.000Zdailyhttps://ecosystem.vuejs.press/ecosystem/zh/plugins/development/palette.html2025-01-10T18:07:54.000Zdailyhttps://ecosystem.vuejs.press/ecosystem/zh/plugins/development/reading-time.html2025-01-10T18:07:54.000Zdailyhttps://ecosystem.vuejs.press/ecosystem/zh/plugins/development/rtl.html2025-01-10T18:07:54.000Zdailyhttps://ecosystem.vuejs.press/ecosystem/zh/plugins/development/theme-data.html2025-01-10T18:07:54.000Zdailyhttps://ecosystem.vuejs.press/ecosystem/zh/plugins/development/toc.html2025-01-10T18:07:54.000Zdailyhttps://ecosystem.vuejs.press/ecosystem/zh/plugins/features/2025-01-10T18:07:54.000Zdailyhttps://ecosystem.vuejs.press/ecosystem/zh/plugins/features/back-to-top.html2025-01-10T18:07:54.000Zdailyhttps://ecosystem.vuejs.press/ecosystem/zh/plugins/features/catalog.html2025-01-10T18:07:54.000Zdailyhttps://ecosystem.vuejs.press/ecosystem/zh/plugins/features/copy-code.html2025-01-10T18:07:54.000Zdailyhttps://ecosystem.vuejs.press/ecosystem/zh/plugins/features/copyright.html2025-01-10T18:07:54.000Zdailyhttps://ecosystem.vuejs.press/ecosystem/zh/plugins/features/icon.html2025-01-10T18:07:54.000Zdailyhttps://ecosystem.vuejs.press/ecosystem/zh/plugins/features/medium-zoom.html2025-01-10T18:07:54.000Zdailyhttps://ecosystem.vuejs.press/ecosystem/zh/plugins/features/notice.html2025-01-10T18:07:54.000Zdailyhttps://ecosystem.vuejs.press/ecosystem/zh/plugins/features/nprogress.html2025-01-10T18:07:54.000Zdailyhttps://ecosystem.vuejs.press/ecosystem/zh/plugins/features/photo-swipe.html2025-01-10T18:07:54.000Zdailyhttps://ecosystem.vuejs.press/ecosystem/zh/plugins/features/watermark.html2025-01-10T18:07:54.000Zdailyhttps://ecosystem.vuejs.press/ecosystem/zh/plugins/markdown/2025-01-10T18:07:54.000Zdailyhttps://ecosystem.vuejs.press/ecosystem/zh/plugins/markdown/append-date.html2025-01-10T18:07:54.000Zdailyhttps://ecosystem.vuejs.press/ecosystem/zh/plugins/markdown/links-check.html2025-01-10T18:07:54.000Zdailyhttps://ecosystem.vuejs.press/ecosystem/zh/plugins/markdown/markdown-container.html2025-01-10T18:07:54.000Zdailyhttps://ecosystem.vuejs.press/ecosystem/zh/plugins/markdown/markdown-ext.html2025-01-10T18:07:54.000Zdailyhttps://ecosystem.vuejs.press/ecosystem/zh/plugins/markdown/markdown-hint.html2025-01-10T18:07:54.000Zdailyhttps://ecosystem.vuejs.press/ecosystem/zh/plugins/markdown/markdown-image.html2025-01-10T18:07:54.000Zdailyhttps://ecosystem.vuejs.press/ecosystem/zh/plugins/markdown/markdown-include.html2025-01-10T18:07:54.000Zdailyhttps://ecosystem.vuejs.press/ecosystem/zh/plugins/markdown/markdown-math.html2025-01-10T18:07:54.000Zdailyhttps://ecosystem.vuejs.press/ecosystem/zh/plugins/markdown/markdown-stylize.html2025-01-10T18:07:54.000Zdailyhttps://ecosystem.vuejs.press/ecosystem/zh/plugins/markdown/markdown-tab.html2025-01-10T18:07:54.000Zdailyhttps://ecosystem.vuejs.press/ecosystem/zh/plugins/markdown/prismjs.html2025-01-10T18:07:54.000Zdailyhttps://ecosystem.vuejs.press/ecosystem/zh/plugins/markdown/shiki.html2025-01-10T18:07:54.000Zdailyhttps://ecosystem.vuejs.press/ecosystem/zh/plugins/pwa/2025-01-10T18:07:54.000Zdailyhttps://ecosystem.vuejs.press/ecosystem/zh/plugins/pwa/remove-pwa.html2025-01-10T18:07:54.000Zdailyhttps://ecosystem.vuejs.press/ecosystem/zh/plugins/search/2025-01-10T18:07:54.000Zdailyhttps://ecosystem.vuejs.press/ecosystem/zh/plugins/search/docsearch.html2025-01-10T18:07:54.000Zdailyhttps://ecosystem.vuejs.press/ecosystem/zh/plugins/search/guidelines.html2025-01-10T18:07:54.000Zdailyhttps://ecosystem.vuejs.press/ecosystem/zh/plugins/search/search.html2025-01-10T18:07:54.000Zdailyhttps://ecosystem.vuejs.press/ecosystem/zh/plugins/search/slimsearch.html2025-01-10T18:07:54.000Zdailyhttps://ecosystem.vuejs.press/ecosystem/zh/plugins/seo/2025-01-10T18:07:54.000Zdailyhttps://ecosystem.vuejs.press/ecosystem/zh/plugins/tools/2025-01-10T18:07:54.000Zdailyhttps://ecosystem.vuejs.press/ecosystem/zh/plugins/tools/cache.html2025-01-10T18:07:54.000Zdailyhttps://ecosystem.vuejs.press/ecosystem/zh/plugins/tools/google-tag-manager.html2025-01-10T18:07:54.000Zdailyhttps://ecosystem.vuejs.press/ecosystem/zh/plugins/tools/redirect.html2025-01-10T18:07:54.000Zdailyhttps://ecosystem.vuejs.press/ecosystem/zh/plugins/tools/register-components.html2025-01-10T18:07:54.000Zdailyhttps://ecosystem.vuejs.press/ecosystem/zh/themes/default/2025-01-10T18:07:54.000Zdailyhttps://ecosystem.vuejs.press/ecosystem/zh/themes/default/components.html2025-01-10T18:07:54.000Zdailyhttps://ecosystem.vuejs.press/ecosystem/zh/themes/default/config.html2025-01-10T18:07:54.000Zdailyhttps://ecosystem.vuejs.press/ecosystem/zh/themes/default/extending.html2025-01-10T18:07:54.000Zdailyhttps://ecosystem.vuejs.press/ecosystem/zh/themes/default/frontmatter.html2025-01-10T18:07:54.000Zdailyhttps://ecosystem.vuejs.press/ecosystem/zh/themes/default/locale.html2025-01-10T18:07:54.000Zdailyhttps://ecosystem.vuejs.press/ecosystem/zh/themes/default/markdown.html2025-01-10T18:07:54.000Zdailyhttps://ecosystem.vuejs.press/ecosystem/zh/themes/default/plugin.html2025-01-10T18:07:54.000Zdailyhttps://ecosystem.vuejs.press/ecosystem/zh/themes/default/styles.html2025-01-10T18:07:54.000Zdailyhttps://ecosystem.vuejs.press/ecosystem/zh/tools/helper/2025-01-10T18:07:54.000Zdailyhttps://ecosystem.vuejs.press/ecosystem/zh/tools/helper/client.html2025-01-10T18:07:54.000Zdailyhttps://ecosystem.vuejs.press/ecosystem/zh/tools/helper/shared.html2025-01-10T18:07:54.000Zdailyhttps://ecosystem.vuejs.press/ecosystem/zh/tools/helper/style.html2025-01-10T18:07:54.000Zdailyhttps://ecosystem.vuejs.press/ecosystem/plugins/blog/comment/artalk/2025-01-10T18:07:54.000Zdailyhttps://ecosystem.vuejs.press/ecosystem/plugins/blog/comment/artalk/config.html2024-08-20T15:23:47.000Zdailyhttps://ecosystem.vuejs.press/ecosystem/plugins/blog/comment/giscus/2025-01-10T18:07:54.000Zdailyhttps://ecosystem.vuejs.press/ecosystem/plugins/blog/comment/giscus/config.html2024-08-20T15:23:47.000Zdailyhttps://ecosystem.vuejs.press/ecosystem/plugins/blog/comment/twikoo/2025-01-10T18:07:54.000Zdailyhttps://ecosystem.vuejs.press/ecosystem/plugins/blog/comment/twikoo/config.html2024-08-20T15:23:47.000Zdailyhttps://ecosystem.vuejs.press/ecosystem/plugins/blog/comment/waline/2025-01-10T18:07:54.000Zdailyhttps://ecosystem.vuejs.press/ecosystem/plugins/blog/comment/waline/config.html2024-12-29T05:44:20.000Zdailyhttps://ecosystem.vuejs.press/ecosystem/zh/plugins/blog/blog/2025-01-10T18:07:54.000Zdailyhttps://ecosystem.vuejs.press/ecosystem/zh/plugins/blog/blog/config.html2025-01-10T18:07:54.000Zdailyhttps://ecosystem.vuejs.press/ecosystem/zh/plugins/blog/blog/guide.html2025-01-10T18:07:54.000Zdailyhttps://ecosystem.vuejs.press/ecosystem/zh/plugins/blog/comment/2024-05-29T05:40:07.000Zdailyhttps://ecosystem.vuejs.press/ecosystem/zh/plugins/blog/comment/guide.html2025-01-10T18:07:54.000Zdailyhttps://ecosystem.vuejs.press/ecosystem/zh/plugins/blog/feed/2025-01-10T18:07:54.000Zdailyhttps://ecosystem.vuejs.press/ecosystem/zh/plugins/blog/feed/channel.html2025-01-10T18:07:54.000Zdailyhttps://ecosystem.vuejs.press/ecosystem/zh/plugins/blog/feed/config.html2025-01-10T18:07:54.000Zdailyhttps://ecosystem.vuejs.press/ecosystem/zh/plugins/blog/feed/frontmatter.html2025-01-10T18:07:54.000Zdailyhttps://ecosystem.vuejs.press/ecosystem/zh/plugins/blog/feed/getter.html2025-01-10T18:07:54.000Zdailyhttps://ecosystem.vuejs.press/ecosystem/zh/plugins/blog/feed/guide.html2025-01-10T18:07:54.000Zdailyhttps://ecosystem.vuejs.press/ecosystem/zh/plugins/development/sass-palette/2025-01-10T18:07:54.000Zdailyhttps://ecosystem.vuejs.press/ecosystem/zh/plugins/development/sass-palette/config.html2025-01-10T18:07:54.000Zdailyhttps://ecosystem.vuejs.press/ecosystem/zh/plugins/development/sass-palette/guide.html2025-01-10T18:07:54.000Zdailyhttps://ecosystem.vuejs.press/ecosystem/zh/plugins/markdown/revealjs/2025-01-10T18:07:54.000Zdailyhttps://ecosystem.vuejs.press/ecosystem/zh/plugins/markdown/revealjs/demo.html2025-01-10T18:07:54.000Zdailyhttps://ecosystem.vuejs.press/ecosystem/zh/plugins/markdown/revealjs/themes.html2025-01-10T18:07:54.000Zdailyhttps://ecosystem.vuejs.press/ecosystem/zh/plugins/pwa/pwa/2025-01-10T18:07:54.000Zdailyhttps://ecosystem.vuejs.press/ecosystem/zh/plugins/pwa/pwa/config.html2025-01-10T18:07:54.000Zdailyhttps://ecosystem.vuejs.press/ecosystem/zh/plugins/pwa/pwa/guide.html2025-01-10T18:07:54.000Zdailyhttps://ecosystem.vuejs.press/ecosystem/zh/plugins/seo/seo/2025-01-10T18:07:54.000Zdailyhttps://ecosystem.vuejs.press/ecosystem/zh/plugins/seo/seo/config.html2025-01-10T18:07:54.000Zdailyhttps://ecosystem.vuejs.press/ecosystem/zh/plugins/seo/seo/guide.html2025-01-10T18:07:54.000Zdailyhttps://ecosystem.vuejs.press/ecosystem/zh/plugins/seo/sitemap/2025-01-10T18:07:54.000Zdailyhttps://ecosystem.vuejs.press/ecosystem/zh/plugins/seo/sitemap/config.html2025-01-10T18:07:54.000Zdailyhttps://ecosystem.vuejs.press/ecosystem/zh/plugins/seo/sitemap/frontmatter.html2025-01-10T18:07:54.000Zdailyhttps://ecosystem.vuejs.press/ecosystem/zh/plugins/seo/sitemap/guide.html2025-01-10T18:07:54.000Zdailyhttps://ecosystem.vuejs.press/ecosystem/zh/tools/helper/node/bundler.html2025-01-10T18:07:54.000Zdailyhttps://ecosystem.vuejs.press/ecosystem/zh/tools/helper/node/locales.html2025-01-10T18:07:54.000Zdailyhttps://ecosystem.vuejs.press/ecosystem/zh/tools/helper/node/page.html2025-01-10T18:07:54.000Zdailyhttps://ecosystem.vuejs.press/ecosystem/zh/plugins/blog/comment/artalk/2025-01-10T18:07:54.000Zdailyhttps://ecosystem.vuejs.press/ecosystem/zh/plugins/blog/comment/artalk/config.html2024-08-20T15:23:47.000Zdailyhttps://ecosystem.vuejs.press/ecosystem/zh/plugins/blog/comment/giscus/2025-01-10T18:07:54.000Zdailyhttps://ecosystem.vuejs.press/ecosystem/zh/plugins/blog/comment/giscus/config.html2024-08-20T15:23:47.000Zdailyhttps://ecosystem.vuejs.press/ecosystem/zh/plugins/blog/comment/twikoo/2025-01-10T18:07:54.000Zdailyhttps://ecosystem.vuejs.press/ecosystem/zh/plugins/blog/comment/twikoo/config.html2024-08-20T15:23:47.000Zdailyhttps://ecosystem.vuejs.press/ecosystem/zh/plugins/blog/comment/waline/2025-01-10T18:07:54.000Zdailyhttps://ecosystem.vuejs.press/ecosystem/zh/plugins/blog/comment/waline/config.html2024-12-29T05:44:20.000Zdailyhttps://ecosystem.vuejs.press/ecosystem/tools/helper/node/dailyhttps://ecosystem.vuejs.press/ecosystem/zh/tools/helper/node/daily \ No newline at end of file diff --git a/sitemap.xsl b/sitemap.xsl new file mode 100644 index 0000000000..a76881a481 --- /dev/null +++ b/sitemap.xsl @@ -0,0 +1,207 @@ + + + + + + + XML Sitemap + + + + + +
+

Sitemap

+ + + + + + + + + + + + + + + + + + + + + +
+ + PriorityChange FrequencyLast Updated Time
+ + + + + + + + + + + + + 0.5 + + + + + + + + + - + + + + +
+
+ + + +
+
diff --git a/themes/default/components.html b/themes/default/components.html new file mode 100644 index 0000000000..0faa29be8d --- /dev/null +++ b/themes/default/components.html @@ -0,0 +1,48 @@ + + + + + + + + + Built-in Components | VuePress Ecosystem + + + + + +
+ + + diff --git a/themes/default/config.html b/themes/default/config.html new file mode 100644 index 0000000000..b396a08cbd --- /dev/null +++ b/themes/default/config.html @@ -0,0 +1,204 @@ + + + + + + + + + Config | VuePress Ecosystem + + + + + +
+ + + diff --git a/themes/default/extending.html b/themes/default/extending.html new file mode 100644 index 0000000000..7fe0cc0bd0 --- /dev/null +++ b/themes/default/extending.html @@ -0,0 +1,103 @@ + + + + + + + + + Extending | VuePress Ecosystem + + + + + +
+ + + diff --git a/themes/default/frontmatter.html b/themes/default/frontmatter.html new file mode 100644 index 0000000000..130b77aa00 --- /dev/null +++ b/themes/default/frontmatter.html @@ -0,0 +1,93 @@ + + + + + + + + + Frontmatter | VuePress Ecosystem + + + + + +
+ + + diff --git a/themes/default/index.html b/themes/default/index.html new file mode 100644 index 0000000000..f8b6b362f7 --- /dev/null +++ b/themes/default/index.html @@ -0,0 +1,49 @@ + + + + + + + + + theme-default | VuePress Ecosystem + + + + + + + + + diff --git a/themes/default/locale.html b/themes/default/locale.html new file mode 100644 index 0000000000..adff9d9635 --- /dev/null +++ b/themes/default/locale.html @@ -0,0 +1,62 @@ + + + + + + + + + Locale Config | VuePress Ecosystem + + + + + +
+ + + diff --git a/themes/default/markdown.html b/themes/default/markdown.html new file mode 100644 index 0000000000..a0d162dc4e --- /dev/null +++ b/themes/default/markdown.html @@ -0,0 +1,117 @@ + + + + + + + + + Markdown | VuePress Ecosystem + + + + + +
+ + + diff --git a/themes/default/plugin.html b/themes/default/plugin.html new file mode 100644 index 0000000000..2f65b07e0f --- /dev/null +++ b/themes/default/plugin.html @@ -0,0 +1,51 @@ + + + + + + + + + Plugins Config | VuePress Ecosystem + + + + + +
+ + + diff --git a/themes/default/styles.html b/themes/default/styles.html new file mode 100644 index 0000000000..9ffd16d717 --- /dev/null +++ b/themes/default/styles.html @@ -0,0 +1,157 @@ + + + + + + + + + Styles | VuePress Ecosystem + + + + + +
+ + + diff --git a/themes/guidelines.html b/themes/guidelines.html new file mode 100644 index 0000000000..60ed0dc151 --- /dev/null +++ b/themes/guidelines.html @@ -0,0 +1,43 @@ + + + + + + + + + Theme Guidelines | VuePress Ecosystem + + + + + +
+ + + diff --git a/themes/index.html b/themes/index.html new file mode 100644 index 0000000000..fd82810c96 --- /dev/null +++ b/themes/index.html @@ -0,0 +1,43 @@ + + + + + + + + + Themes | VuePress Ecosystem + + + + + + + + + diff --git a/tools/helper/client.html b/tools/helper/client.html new file mode 100644 index 0000000000..f91a187d32 --- /dev/null +++ b/tools/helper/client.html @@ -0,0 +1,130 @@ + + + + + + + + + Client Related | VuePress Ecosystem + + + + + +
+ + + diff --git a/tools/helper/index.html b/tools/helper/index.html new file mode 100644 index 0000000000..e1d037c083 --- /dev/null +++ b/tools/helper/index.html @@ -0,0 +1,43 @@ + + + + + + + + + @vuepress/helper | VuePress Ecosystem + + + + + + + + + diff --git a/tools/helper/node/bundler.html b/tools/helper/node/bundler.html new file mode 100644 index 0000000000..9a3de4a1f2 --- /dev/null +++ b/tools/helper/node/bundler.html @@ -0,0 +1,201 @@ + + + + + + + + + Bundler Related | VuePress Ecosystem + + + + + +
+ + + diff --git a/tools/helper/node/index.html b/tools/helper/node/index.html new file mode 100644 index 0000000000..8b15e6112c --- /dev/null +++ b/tools/helper/node/index.html @@ -0,0 +1,43 @@ + + + + + + + + + Node | VuePress Ecosystem + + + + + + + + + diff --git a/tools/helper/node/locales.html b/tools/helper/node/locales.html new file mode 100644 index 0000000000..e6c46ede3e --- /dev/null +++ b/tools/helper/node/locales.html @@ -0,0 +1,65 @@ + + + + + + + + + Locales Related | VuePress Ecosystem + + + + + +
+ + + diff --git a/tools/helper/node/page.html b/tools/helper/node/page.html new file mode 100644 index 0000000000..eaeda4e4a7 --- /dev/null +++ b/tools/helper/node/page.html @@ -0,0 +1,117 @@ + + + + + + + + + Page Related | VuePress Ecosystem + + + + + +
+ + + diff --git a/tools/helper/shared.html b/tools/helper/shared.html new file mode 100644 index 0000000000..1b64143244 --- /dev/null +++ b/tools/helper/shared.html @@ -0,0 +1,147 @@ + + + + + + + + + Shared Methods | VuePress Ecosystem + + + + + +
+ + + diff --git a/tools/helper/style.html b/tools/helper/style.html new file mode 100644 index 0000000000..6d6e94ad18 --- /dev/null +++ b/tools/helper/style.html @@ -0,0 +1,43 @@ + + + + + + + + + Styles | VuePress Ecosystem + + + + + + + + + diff --git a/tools/index.html b/tools/index.html new file mode 100644 index 0000000000..f520a85d80 --- /dev/null +++ b/tools/index.html @@ -0,0 +1,43 @@ + + + + + + + + + Tool Packages | VuePress Ecosystem + + + + + + + + + diff --git a/zh/atom.xml b/zh/atom.xml new file mode 100644 index 0000000000..0d0e537550 --- /dev/null +++ b/zh/atom.xml @@ -0,0 +1,6036 @@ + + + https://ecosystem.vuejs.press/ecosystem/zh/ + VuePress 生态系统 + VuePress 官方主题和插件 + 2025-01-10T18:17:55.756Z + @vuepress/plugin-feed + + + + 插件 + https://ecosystem.vuejs.press/ecosystem/zh/plugins/ + + 2025-01-10T18:07:54.000Z + + + + + 主题 + https://ecosystem.vuejs.press/ecosystem/zh/themes/ + + 2025-01-10T18:07:54.000Z + + + + + 主题指南 + https://ecosystem.vuejs.press/ecosystem/zh/themes/guidelines.html + + 2025-01-10T18:07:54.000Z + 为了避免主题开发者和用户设置不必要的选项,我们制定了一套主题创建时应遵循的指南。

+

DOM 结构

+

一个主题必须实现以下 DOM 结构:

+
    +
  • 容器:一个包含整个主题的元素。此元素应该有一个 vp-container 属性。
  • +
  • 内容:一个包含 markdown 渲染结果的元素。此元素应该有一个 vp-content 属性。
  • +
+

一个主题可以有以下可选元素:

+
    +
  • 导航栏:站点的导航栏。此元素应该有一个 vp-navbar 属性。
  • +
  • 侧边栏:站点的侧边栏。此元素应该有一个 vp-sidebar 属性。
  • +
  • 大纲:主要内容的标题或大纲。此元素应该有一个 vp-outline 属性。
  • +
  • 评论:评论服务(评论框和评论列表)。此元素应该有一个 vp-comment 属性。
  • +
  • 页脚:站点的页脚。此元素应该有一个 vp-footer 属性。
  • +
]]>
+ 为了避免主题开发者和用户设置不必要的选项,我们制定了一套主题创建时应遵循的指南。

+

DOM 结构

+

一个主题必须实现以下 DOM 结构:

+
    +
  • 容器:一个包含整个主题的元素。此元素应该有一个 vp-container 属性。
  • +
  • 内容:一个包含 markdown 渲染结果的元素。此元素应该有一个 vp-content 属性。
  • +
+

一个主题可以有以下可选元素:

+
    +
  • 导航栏:站点的导航栏。此元素应该有一个 vp-navbar 属性。
  • +
  • 侧边栏:站点的侧边栏。此元素应该有一个 vp-sidebar 属性。
  • +
  • 大纲:主要内容的标题或大纲。此元素应该有一个 vp-outline 属性。
  • +
  • 评论:评论服务(评论框和评论列表)。此元素应该有一个 vp-comment 属性。
  • +
  • 页脚:站点的页脚。此元素应该有一个 vp-footer 属性。
  • +
+

一个主题必须:

+
    +
  • 在暗色模式下,将 html 元素的 data-theme 设置为 dark
  • +
  • 在亮色模式下,将 html 元素的 data-theme 设置为 light
  • +
+

如果主题只有一种颜色方案,主题仍然需要将 data-theme 设置为 lightdark,以指示默认颜色方案。

+

组件

+

为了支持搜索插件,主题应检查 <SearchBox /> 是否已全局注册,并在其自己的导航栏或侧边栏中呈现(如果可用)。

+

颜色变量

+

一个主题必须实现以下颜色变量:

+

文字

+
    +
  • --vp-c-text:默认文本颜色。
  • +
  • --vp-c-text-mute:用于静音文本的颜色,例如“非活动菜单”或“信息文本”。
  • +
  • --vp-c-text-subtle:用于细微文本的颜色,例如“占位符”或“插入符号”。
  • +
+

背景

+
    +
  • --vp-c-bg:用于主屏幕的背景颜色。
  • +
  • --vp-c-bg-alt:用于“侧边栏”或“代码块”等地方的备用背景颜色。
  • +
  • --vp-c-bg-elv:用于“浮动”部分的提升背景颜色,例如“对话框”。
  • +
+

阴影

+
    +
  • --vp-c-shadow:阴影颜色
  • +
+

强调

+

用于交互组件的强调颜色和品牌颜色。

+
    +
  • +

    --vp-c-accent:主要用于彩色文本的最实色。它必须满足与放在 --vp-c-accent-soft 顶部时的对比度。

    +
  • +
  • +

    --vp-c-accent-hover:用于悬停状态的颜色。

    +
  • +
  • +

    --vp-c-accent-bg:用于实色背景的颜色。它必须满足与放在其顶部的 --vp-c-accent-text 的对比度。

    +
  • +
  • +

    --vp-c-accent-text:用于 --vp-c-accent-bg 背景的文本颜色。它必须满足与 --vp-c-accent-bg 的对比度。

    +
  • +
  • +

    --vp-c-accent-soft:用于自定义容器或徽章等细微背景的颜色。当将 --vp-c-accent 颜色放在其顶部时,它必须满足对比度。

    +

    软色必须是半透明的 alpha 通道。这是至关重要的,因为它允许将多个“软”颜色叠加在一起以创建强调,例如在自定义容器内部有内联代码块时。

    +
  • +
+

边框

+
    +
  • --vp-c-border:交互组件的边框颜色。例如,这应该用于按钮轮廓。
  • +
  • --vp-c-border-hard:较暗的边框颜色,用于紧贴文本的“硬”边框,例如表格和 kbd。
  • +
  • --vp-c-divider:分隔符的颜色,用于在同一组件内分隔部分,例如在“h2”标题上放置分隔符。
  • +
+

控件

+
    +
  • --vp-c-control:用于交互控件(例如按钮或复选框)的背景颜色。
  • +
  • --vp-c-control-hover:用于交互控件悬停状态的背景颜色。
  • +
  • --vp-c-control-disabled:用于交互控件禁用状态的颜色。
  • +
+

过渡时间

+
    +
  • --vp-t-color:颜色过渡时间。
  • +
  • --vp-t-transform:变换过渡时间。
  • +
+

案例

+]]>
+
+ + 工具包 + https://ecosystem.vuejs.press/ecosystem/zh/tools/ + + 2025-01-10T18:07:54.000Z + + + + + 统计分析插件 + https://ecosystem.vuejs.press/ecosystem/zh/plugins/analytics/ + + 2025-01-10T18:07:54.000Z + + + + + baidu-analytics + https://ecosystem.vuejs.press/ecosystem/zh/plugins/analytics/baidu-analytics.html + + 2025-01-10T18:07:54.000Z + + + + + google-analytics + https://ecosystem.vuejs.press/ecosystem/zh/plugins/analytics/google-analytics.html + + 2025-01-10T18:07:54.000Z + + + + + umami-analytics + https://ecosystem.vuejs.press/ecosystem/zh/plugins/analytics/umami-analytics.html + + 2025-01-10T18:07:54.000Z + + + + + 博客插件 + https://ecosystem.vuejs.press/ecosystem/zh/plugins/blog/ + + 2025-01-10T18:07:54.000Z + + + + + 开发插件 + https://ecosystem.vuejs.press/ecosystem/zh/plugins/development/ + + 2025-01-10T18:07:54.000Z + + + + + active-header-links + https://ecosystem.vuejs.press/ecosystem/zh/plugins/development/active-header-links.html + + 2025-01-10T18:07:54.000Z + + + + + git + https://ecosystem.vuejs.press/ecosystem/zh/plugins/development/git.html + + 2025-01-10T18:07:54.000Z + + + + + palette + https://ecosystem.vuejs.press/ecosystem/zh/plugins/development/palette.html + + 2025-01-10T18:07:54.000Z + + + + + reading-time + https://ecosystem.vuejs.press/ecosystem/zh/plugins/development/reading-time.html + + 2025-01-10T18:07:54.000Z + + + + + rtl + https://ecosystem.vuejs.press/ecosystem/zh/plugins/development/rtl.html + + 2025-01-10T18:07:54.000Z + + + + + theme-data + https://ecosystem.vuejs.press/ecosystem/zh/plugins/development/theme-data.html + + 2025-01-10T18:07:54.000Z + + + + + toc + https://ecosystem.vuejs.press/ecosystem/zh/plugins/development/toc.html + + 2025-01-10T18:07:54.000Z + + + + + 功能插件 + https://ecosystem.vuejs.press/ecosystem/zh/plugins/features/ + + 2025-01-10T18:07:54.000Z + + + + + back-to-top + https://ecosystem.vuejs.press/ecosystem/zh/plugins/features/back-to-top.html + + 2025-01-10T18:07:54.000Z + + + + + catalog + https://ecosystem.vuejs.press/ecosystem/zh/plugins/features/catalog.html + + 2025-01-10T18:07:54.000Z + + + + + copy-code + https://ecosystem.vuejs.press/ecosystem/zh/plugins/features/copy-code.html + + 2025-01-10T18:07:54.000Z + + + + + copyright + https://ecosystem.vuejs.press/ecosystem/zh/plugins/features/copyright.html + + 2025-01-10T18:07:54.000Z + + + + + icon + https://ecosystem.vuejs.press/ecosystem/zh/plugins/features/icon.html + + 2025-01-10T18:07:54.000Z + + + + + medium-zoom + https://ecosystem.vuejs.press/ecosystem/zh/plugins/features/medium-zoom.html + + 2025-01-10T18:07:54.000Z + + + + + notice + https://ecosystem.vuejs.press/ecosystem/zh/plugins/features/notice.html + + 2025-01-10T18:07:54.000Z + + + + + nprogress + https://ecosystem.vuejs.press/ecosystem/zh/plugins/features/nprogress.html + + 2025-01-10T18:07:54.000Z + + + + + photo-swipe + https://ecosystem.vuejs.press/ecosystem/zh/plugins/features/photo-swipe.html + + 2025-01-10T18:07:54.000Z + + + + + watermark + https://ecosystem.vuejs.press/ecosystem/zh/plugins/features/watermark.html + + 2025-01-10T18:07:54.000Z + + + + + Markdown 插件 + https://ecosystem.vuejs.press/ecosystem/zh/plugins/markdown/ + + 2025-01-10T18:07:54.000Z + + + + + append-date + https://ecosystem.vuejs.press/ecosystem/zh/plugins/markdown/append-date.html + + 2025-01-10T18:07:54.000Z + + + + + links-check + https://ecosystem.vuejs.press/ecosystem/zh/plugins/markdown/links-check.html + + 2025-01-10T18:07:54.000Z + + + + + markdown-container + https://ecosystem.vuejs.press/ecosystem/zh/plugins/markdown/markdown-container.html + + 2025-01-10T18:07:54.000Z + + + + + markdown-ext + https://ecosystem.vuejs.press/ecosystem/zh/plugins/markdown/markdown-ext.html + + 2025-01-10T18:07:54.000Z + + + + + markdown-hint + https://ecosystem.vuejs.press/ecosystem/zh/plugins/markdown/markdown-hint.html + + 2025-01-10T18:07:54.000Z + + + + + markdown-image + https://ecosystem.vuejs.press/ecosystem/zh/plugins/markdown/markdown-image.html + + 2025-01-10T18:07:54.000Z + + + + + markdown-include + https://ecosystem.vuejs.press/ecosystem/zh/plugins/markdown/markdown-include.html + + 2025-01-10T18:07:54.000Z + + + + + markdown-math + https://ecosystem.vuejs.press/ecosystem/zh/plugins/markdown/markdown-math.html + + 2025-01-10T18:07:54.000Z + + + + + markdown-stylize + https://ecosystem.vuejs.press/ecosystem/zh/plugins/markdown/markdown-stylize.html + + 2025-01-10T18:07:54.000Z + + + + + markdown-tab + https://ecosystem.vuejs.press/ecosystem/zh/plugins/markdown/markdown-tab.html + + 2025-01-10T18:07:54.000Z + + + + + prismjs + https://ecosystem.vuejs.press/ecosystem/zh/plugins/markdown/prismjs.html + + 2025-01-10T18:07:54.000Z + + + + + shiki + https://ecosystem.vuejs.press/ecosystem/zh/plugins/markdown/shiki.html + + 2025-01-10T18:07:54.000Z + + + + + 渐进式应用插件 + https://ecosystem.vuejs.press/ecosystem/zh/plugins/pwa/ + + 2025-01-10T18:07:54.000Z + + + + + remove-pwa + https://ecosystem.vuejs.press/ecosystem/zh/plugins/pwa/remove-pwa.html + + 2025-01-10T18:07:54.000Z + + + + + 搜索插件 + https://ecosystem.vuejs.press/ecosystem/zh/plugins/search/ + + 2025-01-10T18:07:54.000Z + + + + + docsearch + https://ecosystem.vuejs.press/ecosystem/zh/plugins/search/docsearch.html + + 2025-01-10T18:07:54.000Z + + + + + 搜索插件指南 + https://ecosystem.vuejs.press/ecosystem/zh/plugins/search/guidelines.html + + 2025-01-10T18:07:54.000Z + 为了使 VuePress 主题开箱即用地支持搜索插件,我们有一套创建搜索插件时应遵循的指南。

+

组件名称

+
    +
  • +

    如果搜索插件提供了适合在导航栏或侧边栏中显示的搜索框,则应将其命名为 <SearchBox /> 并进行全局注册。

    +
  • +
  • +

    如果搜索插件提供了适合在单个页面中显示的复杂搜索结果组件(包含输入和结果列表),则应将其命名为 <SearchPanel /> 并进行全局注册。

    +

    搜索插件应在每个语言环境中自动生成一个包含 <SearchPanel /> 组件的 /search.html 页面,但不得覆盖任何现有页面。

    +
  • +
]]>
+ 为了使 VuePress 主题开箱即用地支持搜索插件,我们有一套创建搜索插件时应遵循的指南。

+

组件名称

+
    +
  • +

    如果搜索插件提供了适合在导航栏或侧边栏中显示的搜索框,则应将其命名为 <SearchBox /> 并进行全局注册。

    +
  • +
  • +

    如果搜索插件提供了适合在单个页面中显示的复杂搜索结果组件(包含输入和结果列表),则应将其命名为 <SearchPanel /> 并进行全局注册。

    +

    搜索插件应在每个语言环境中自动生成一个包含 <SearchPanel /> 组件的 /search.html 页面,但不得覆盖任何现有页面。

    +
  • +
+]]>
+
+ + search + https://ecosystem.vuejs.press/ecosystem/zh/plugins/search/search.html + + 2025-01-10T18:07:54.000Z + + + + + slimsearch + https://ecosystem.vuejs.press/ecosystem/zh/plugins/search/slimsearch.html + + 2025-01-10T18:07:54.000Z + + + + + 搜索引擎优化插件 + https://ecosystem.vuejs.press/ecosystem/zh/plugins/seo/ + + 2025-01-10T18:07:54.000Z + + + + + 工具插件 + https://ecosystem.vuejs.press/ecosystem/zh/plugins/tools/ + + 2025-01-10T18:07:54.000Z + + + + + cache + https://ecosystem.vuejs.press/ecosystem/zh/plugins/tools/cache.html + + 2025-01-10T18:07:54.000Z + + + + + google-tag-manager + https://ecosystem.vuejs.press/ecosystem/zh/plugins/tools/google-tag-manager.html + + 2025-01-10T18:07:54.000Z + + + + + redirect + https://ecosystem.vuejs.press/ecosystem/zh/plugins/tools/redirect.html + + 2025-01-10T18:07:54.000Z + + + + + register-components + https://ecosystem.vuejs.press/ecosystem/zh/plugins/tools/register-components.html + + 2025-01-10T18:07:54.000Z + + + + + 默认主题 + https://ecosystem.vuejs.press/ecosystem/zh/themes/default/ + + 2025-01-10T18:07:54.000Z + + + + + 内置组件 + https://ecosystem.vuejs.press/ecosystem/zh/themes/default/components.html + + 2025-01-10T18:07:54.000Z + + + + + 配置 + https://ecosystem.vuejs.press/ecosystem/zh/themes/default/config.html + + 2025-01-10T18:07:54.000Z + 基础配置 +

hostname

+
    +
  • +

    类型: string

    +
  • +
  • +

    详情:

    +

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

    +
  • +
+

locales

+
    +
  • +

    类型: { [path: string]: Partial<DefaultThemeLocaleData> }

    +
  • +
  • +

    默认值: {}

    +
  • +
  • +

    详情:

    +

    多语言支持的各个语言 locales 。

    +

    所有在 Locale 配置 章节内的配置项都可以在 locales 中使用。

    +

    该配置项仅能在默认主题内生效,注意不要和 站点配置 中的 locales 混淆。

    +
  • +
  • +

    参考:

    + +
  • +
]]>
+ 基础配置 +

hostname

+
    +
  • +

    类型: string

    +
  • +
  • +

    详情:

    +

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

    +
  • +
+

locales

+
    +
  • +

    类型: { [path: string]: Partial<DefaultThemeLocaleData> }

    +
  • +
  • +

    默认值: {}

    +
  • +
  • +

    详情:

    +

    多语言支持的各个语言 locales 。

    +

    所有在 Locale 配置 章节内的配置项都可以在 locales 中使用。

    +

    该配置项仅能在默认主题内生效,注意不要和 站点配置 中的 locales 混淆。

    +
  • +
  • +

    参考:

    + +
  • +
+

Locale 配置

+

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

+

colorMode

+ +

colorModeSwitch

+ +

externalLinkIcon

+
    +
  • +

    类型: boolean

    +
  • +
  • +

    默认值: true

    +
  • +
  • +

    详情:

    +

    是否在外部链接上显示外部链接图标。

    +
  • +
+

home

+
    +
  • +

    类型: string

    +
  • +
  • +

    默认值: /

    +
  • +
  • +

    详情:

    +

    首页的路径。

    +

    它将被用于:

    +
      +
    • 导航栏中 Logo 的链接
    • +
    • 404 页面的 返回首页 链接
    • +
    +
  • +
+

navbar

+
    +
  • +

    类型: false | NavbarOptions

    +
  • +
  • +

    默认值: []

    +
  • +
  • +

    详情:

    +

    导航栏配置。

    +

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

    +

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

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

    示例 1:

    +
  • +
+
export default {
+  theme: defaultTheme({
+    navbar: [
+      // NavbarLink
+      {
+        text: 'Foo',
+        link: '/foo/',
+      },
+      // NavbarGroup
+      {
+        text: 'Group',
+        prefix: '/group/',
+        children: ['foo.md', 'bar.md'],
+      },
+      // 字符串 - 页面文件路径
+      '/bar/README.md',
+    ],
+  }),
+}
+
    +
  • 示例 2:
  • +
+
export default {
+  theme: defaultTheme({
+    navbar: [
+      // 嵌套 Group - 最大深度为 2
+      {
+        text: 'Group',
+        prefix: '/group/',
+        children: [
+          {
+            text: 'SubGroup1',
+            prefix: 'sub1/',
+            children: [
+              'foo.md', // 解析为 `/guide/group/sub1/bar.md`
+              'bar.md', // 解析为 `/guide/group/sub1/bar.md`
+
+              // 一个外部链接
+              {
+                text: 'Example',
+                link: 'https://example.com',
+              },
+            ],
+          },
+          {
+            text: 'SubGroup2',
+            prefix: 'sub2/',
+            // 项目内链接的 .md 或 .html 后缀是可以省略的
+            children: [
+              'foo', // 解析为 `/guide/group/sub2/foo.md`
+              'bar', // 解析为 `/guide/group/sub2/bar.md`
+
+              // 不在 SubGroup2 内的链接
+              '/baz/', // 解析为 `/baz/README.md`
+            ],
+          },
+        ],
+      },
+      // 控制元素何时被激活
+      {
+        text: 'Group 2',
+        children: [
+          {
+            text: 'Always active',
+            link: '/',
+            // 该元素将一直处于激活状态
+            activeMatch: '/',
+          },
+          {
+            text: 'Active on /foo/',
+            link: '/not-foo/',
+            // 该元素在当前路由路径是 /foo/ 开头时激活
+            // 支持正则表达式
+            activeMatch: '^/foo/',
+          },
+        ],
+      },
+    ],
+  }),
+}
+

logo

+
    +
  • +

    类型: 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',
+  }),
+}
+

sidebar

+
    +
  • +

    类型: false | SidebarOptions

    +
  • +
  • +

    默认值: 'heading'

    +
  • +
  • +

    详情:

    +

    侧边栏配置。

    +

    你可以通过页面的 sidebar frontmatter 来覆盖这个全局配置。

    +

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

    +

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

    +

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

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

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

    +
      +
    • Key 为路径前缀。
    • +
    • Value 为 侧边栏数组"heading" 以自动为相应路径生成基于标题的侧边栏。
    • +
    +
  • +
  • +

    示例 1:

    +
  • +
+
export default {
+  theme: defaultTheme({
+    // 侧边栏数组
+    // 所有页面会使用相同的侧边栏
+    sidebar: [
+      // SidebarItem
+      {
+        text: 'Foo',
+        prefix: '/foo/',
+        link: '/foo/',
+        children: [
+          // SidebarItem
+          {
+            text: 'github',
+            link: 'https://github.com',
+            children: [],
+          },
+          // 字符串 - 页面文件路径
+          'bar.md', // 解析为 `/foo/bar.md`
+          '/ray.md', // 解析为 `/ray.md`
+        ],
+      },
+      // 字符串 - 页面文件路径
+      '/bar/README.md',
+    ],
+  }),
+}
+
    +
  • 示例 2:
  • +
+
export default {
+  theme: defaultTheme({
+    // 侧边栏对象
+    // 不同子路径下的页面会使用不同的侧边栏
+    sidebar: {
+      '/guide/': [
+        {
+          text: 'Guide',
+          // 相对路径会自动追加子路径前缀
+          children: [
+            'introduction.md', // 解析为 `/guide/introduction.md`
+            'getting-started.md', // 解析为 `/guide/getting-started.md`
+          ],
+        },
+      ],
+      '/reference/': 'heading',
+    },
+  }),
+}
+
    +
  • 示例 3:
  • +
+
export default {
+  theme: defaultTheme({
+    // 可折叠的侧边栏
+    sidebar: {
+      '/reference/': [
+        {
+          text: 'VuePress Reference',
+          collapsible: true,
+          // 基于项目路径的 .md 或 .html 后缀是可以省略的
+          children: ['cli', 'config'],
+        },
+        {
+          text: 'Bundlers Reference',
+          collapsible: true,
+          // 前缀可以是相对路径,等同于 `prefix: /reference/bundler/`
+          prefix: 'bundler/',
+          children: ['vite', 'webpack'],
+        },
+      ],
+    },
+  }),
+}
+

sidebarDepth

+
    +
  • +

    类型: number

    +
  • +
  • +

    默认值: 2

    +
  • +
  • +

    详情:

    +

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

    +
      +
    • 设为 0 来禁用所有级别的页面标题。
    • +
    • 设为 1 来包含 <h2> 标题。
    • +
    • 设为 2 来包含 <h2><h3> 标题。
    • +
    • ...
    • +
    +

    你可以通过页面的 sidebarDepth frontmatter 来覆盖这个全局配置。

    +
  • +
+

editLink

+
    +
  • +

    类型: boolean

    +
  • +
  • +

    默认值: true

    +
  • +
  • +

    详情:

    +

    是否启用 编辑此页 链接。

    +

    你可以通过页面的 editLink frontmatter 来覆盖这个全局配置。

    +
  • +
+

editLinkPattern

+
    +
  • +

    类型: string

    +
  • +
  • +

    详情:

    +

    编辑此页 链接的 Pattern 。

    +

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

    +

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

    +
  • +
  • +

    用法:

    +

    | Pattern | 描述 |
    +|

    +
  • +
+]]>
+
+ + 继承 + https://ecosystem.vuejs.press/ecosystem/zh/themes/default/extending.html + + 2025-01-10T18:07:54.000Z + VuePress 默认主题有着大量的用户,因此我们对它进行了一些便于继承的设计,以便用户轻松进行定制化。

+

布局插槽

+

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

+
    +
  • navbar
  • +
  • navbar-before
  • +
  • navbar-after
  • +
  • sidebar
  • +
  • sidebar-top
  • +
  • sidebar-bottom
  • +
  • page
  • +
  • page-top
  • +
  • page-bottom
  • +
  • page-content-top
  • +
  • page-content-bottom
  • +
]]>
+ 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
extending-a-theme
+

组件替换

+

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

+

默认主题将所有 非全局的组件 都注册了一个带 @theme 前缀的 alias 。例如,VPHomeFooter.vue 的别名是 @theme/VPHomeFooter.vue

+

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

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

修改行为

+

默认主题的核心行为大多都被抽离成可组合式 API 或工具函数,并同样提供了 @theme 前缀的 alias

+

比如,如果你想为默认主题的主题数据添加一些默认值,你可以通过覆盖 @theme/useThemeDatauseThemeData 函数来实现。

+

开发一个子主题

+

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

+
import type { DefaultThemeOptions } from '@vuepress/theme-default'
+import { defaultTheme } from '@vuepress/theme-default'
+import type { Theme } from 'vuepress/core'
+import { getDirname, path } from 'vuepress/utils'
+
+const __dirname = import.meta.dirname || getDirname(import.meta.url)
+
+export const childTheme = (options: DefaultThemeOptions): Theme => ({
+  name: 'vuepress-theme-child',
+  extends: defaultTheme(options),
+
+  // 在子主题的客户端配置文件中覆盖布局
+  // 注意,你在发布到 NPM 之前会将 TS 构建为 JS ,因此这里需要设置为 JS 文件的路径
+  clientConfigFile: path.resolve(__dirname, './client.js'),
+
+  // 覆盖组件别名
+  alias: {
+    '@theme/VPHomeFooter.vue': path.resolve(
+      __dirname,
+      './components/MyHomeFooter.vue',
+    ),
+  },
+})
+
]]>
+
+ + Frontmatter + https://ecosystem.vuejs.press/ecosystem/zh/themes/default/frontmatter.html + + 2025-01-10T18:07:54.000Z + + + + + 语言配置 + https://ecosystem.vuejs.press/ecosystem/zh/themes/default/locale.html + + 2025-01-10T18:07:54.000Z + 这些选项用于配置与语言相关的文本。

+

如果你的站点是以英语以外的其他语言提供服务的,你应该为每个语言设置这些选项来提供翻译。

+

repoLabel

+
    +
  • +

    类型: string

    +
  • +
  • +

    详情:

    +

    项目仓库的标签。

    +

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

    +

    如果你不明确指定该配置项,它将会根据 repo 配置项自动推断。

    +
  • +
]]>
+ 这些选项用于配置与语言相关的文本。

+

如果你的站点是以英语以外的其他语言提供服务的,你应该为每个语言设置这些选项来提供翻译。

+

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: '简体中文',
+      },
+    },
+  }),
+}
+

navbarLabel

+
    +
  • +

    类型:null | string

    +
  • +
  • +

    详情:

    +

    导航栏中主导航 aria-label 属性的值。

    +
  • +
+

pageNavbarLabel

+
    +
  • +

    类型: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 页面中 返回首页 链接的文字。

    +
  • +
+

toggleColorMode

+
    +
  • +

    类型: string

    +
  • +
  • +

    默认值: 'toggle color mode'

    +
  • +
  • +

    详情:

    +

    切换颜色模式按钮的标题文字。

    +

    它主要是为了站点的可访问性 (a11y) 。

    +
  • +
  • +

    参考:

    + +
  • +
+

toggleSidebar

+
    +
  • +

    类型: string

    +
  • +
  • +

    默认值: 'toggle sidebar'

    +
  • +
  • +

    详情:

    +

    切换侧边栏按钮的标题文字。

    +

    它主要是为了站点的可访问性 (a11y) 。

    +
  • +
+

prev

+
    +
  • +

    类型: string | false

    +
  • +
  • +

    默认值: 'Prev'

    +
  • +
  • +

    详情:

    +

    上一页按钮的文字。设置为 false 时,将隐藏上一页按钮。

    +
  • +
+

next

+
    +
  • +

    类型: string | false

    +
  • +
  • +

    默认值: 'Next'

    +
  • +
  • +

    详情:

    +

    下一页按钮的文字。设置为 false 时,将隐藏下一页按钮。

    +
  • +
+]]>
+
+ + Markdown + https://ecosystem.vuejs.press/ecosystem/zh/themes/default/markdown.html + + 2025-01-10T18:07:54.000Z + + + + + 插件配置 + https://ecosystem.vuejs.press/ecosystem/zh/themes/default/plugin.html + + 2025-01-10T18:07:54.000Z + 你可以通过 themePlugins 设置默认主题使用的插件。

+

默认主题使用了一些插件,如果你确实不需要该插件,你可以选择禁用它。在禁用插件之前,请确保你已了解它的用途。

+
import { defaultTheme } from '@vuepress/theme-default'
+
+export default {
+  theme: defaultTheme({
+    themePlugins: {
+      // 在这里自定义主题插件
+    },
+  }),
+}
+
]]>
+ 你可以通过 themePlugins 设置默认主题使用的插件。

+

默认主题使用了一些插件,如果你确实不需要该插件,你可以选择禁用它。在禁用插件之前,请确保你已了解它的用途。

+
import { defaultTheme } from '@vuepress/theme-default'
+
+export default {
+  theme: defaultTheme({
+    themePlugins: {
+      // 在这里自定义主题插件
+    },
+  }),
+}
+

themePlugins.activeHeaderLinks

+ +

themePlugins.backToTop

+
    +
  • +

    类型: BackToTopPluginOptions | boolean

    +
  • +
  • +

    默认值: true

    +
  • +
  • +

    详情:

    +

    是否启用 @vuepress/plugin-back-to-top

    +

    支持对象格式以作为插件选项。

    +
  • +
+

themePlugins.container

+ +

themePlugins.copyCode

+
    +
  • +

    类型: CopyCodePluginOptions | boolean

    +
  • +
  • +

    默认值: true

    +
  • +
  • +

    详情:

    +

    是否启用 @vuepress/plugin-copy-code

    +

    支持对象格式以作为插件选项。

    +
  • +
+

themePlugins.git

+
    +
  • +

    类型: boolean

    +
  • +
  • +

    默认值: true

    +
  • +
  • +

    详情:

    +

    是否启用 @vuepress/plugin-git

    +
  • +
+

themePlugins.hint

+ +

themePlugins.links-check

+ +

themePlugins.mediumZoom

+ +

themePlugins.nprogress

+ +

themePlugins.prismjs

+ +

themePlugins.seo

+
    +
  • +

    类型: SeoPluginOptions | boolean

    +
  • +
  • +

    默认值: true

    +
  • +
  • +

    详情:

    +

    是否启用 @vuepress/plugin-seo

    +

    支持对象格式以作为插件选项。

    +
  • +
+

themePlugins.sitemap

+
    +
  • +

    类型: SitemapPluginOptions | boolean

    +
  • +
  • +

    默认值: true

    +
  • +
  • +

    详情:

    +

    是否启用 @vuepress/plugin-sitemap

    +

    支持对象格式以作为插件选项。

    +
  • +
+

themePlugins.tab

+ +]]>
+
+ + 样式 + https://ecosystem.vuejs.press/ecosystem/zh/themes/default/styles.html + + 2025-01-10T18:07:54.000Z + + + + + @vuepress/helper + https://ecosystem.vuejs.press/ecosystem/zh/tools/helper/ + + 2025-01-10T18:07:54.000Z + + + + + 客户端相关 + https://ecosystem.vuejs.press/ecosystem/zh/tools/helper/client.html + + 2025-01-10T18:07:54.000Z + 这些函数仅在 @vuepress/helper/client 中可用。

+

可组合 API

+

hasGlobalComponent

+

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

+
+

提示

+
    +
  1. 组件的局部导入不影响结果。
  2. +
  3. 当在 setup 之外调用时,你需要将 app 实例作为第二个参数传递。
  4. +
+
]]>
+ 这些函数仅在 @vuepress/helper/client 中可用。

+

可组合 API

+

hasGlobalComponent

+

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

+
+

提示

+
    +
  1. 组件的局部导入不影响结果。
  2. +
  3. 当在 setup 之外调用时,你需要将 app 实例作为第二个参数传递。
  4. +
+
+
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 // '标题'
+
+

工具

+

getHeaders

+

获取当前页面指定的 标题列表。

+
export const getHeaders: (options: GetHeadersOptions) => MenuItem[]
+

参数:

+
export interface GetHeadersOptions {
+  /**
+   * 页面标题选择器
+   *
+   * @default '[vp-content] h1, [vp-content] h2, [vp-content] h3, [vp-content] h4, [vp-content] h5, [vp-content] h6'
+   */
+  selector?: string
+  /**
+   * 忽略标题内的特定元素选择器
+   *
+   * 它将作为 `document.querySelectorAll` 的参数。
+   * 因此,你应该传入一个 `CSS 选择器` 字符串
+   *
+   * @default []
+   */
+  ignore?: string[]
+  /**
+   * 指定获取的标题层级
+   *
+   * `1` 至 `6` 表示 `<h1>` 至 `<h6>`
+   *
+   * - `false`: 不返回标题列表
+   * - `number`: 只获取指定的单个层级的标题。
+   * - `[number, number]: 标题层级元组,第一个数字应小于第二个数字。例如,`[2, 4]` 表示显示从 `<h2>` 到 `<h4>` 的所有标题。
+   * - `deep`: 等同于 `[2, 6]`, 表示获取从 `<h2>` 到 `<h6>` 的所有标题。
+   *
+   * @default 2
+   */
+  levels?: HeaderLevels
+}
+

返回结果:

+
export interface Header {
+  /**
+   * 当前标题的层级
+   *
+   * `1` 至 `6` 表示 `<h1>` 至 `<h6>`
+   */
+  level: number
+  /**
+   * 当前标题的内容
+   */
+  title: string
+  /**
+   * 标题的 标识
+   *
+   * 这通常是标题元素的 `id` 属性值
+   */
+  slug: string
+  /**
+   * 标题的链接
+   *
+   * 通常使用`#${slug}`作为锚点哈希
+   */
+  link: string
+  /**
+   * 标题的子标题列表
+   */
+  children: Header[]
+}
+
+export type HeaderLevels = number | 'deep' | false | [number, number]
+
+export type MenuItem = Omit<Header, 'children' | 'slug'> & {
+  element: HTMLHeadElement
+  children?: MenuItem[]
+}
+
Examples +
onMounted(() => {
+  const headers = getHeaders({
+    selector: '[vp-content] :where(h1,h2,h3,h4,h5,h6)',
+    levels: [2, 3], // 只有 h2 和 h3
+    ignore: ['.badge'], // 忽略标题内的 <Badge />
+  })
+  console.log(headers)
+})
+
+]]>
+
+ + 共享方法 + https://ecosystem.vuejs.press/ecosystem/zh/tools/helper/shared.html + + 2025-01-10T18:07:54.000Z + 以下函数在 Node.js 和客户端上均可用。

+

这些函数在 @vuepress/helper @vuepress/helper/client@vuepress/helper/shared 中都可用。

+

数据相关

+

此方法在 MarkdownIt 插件中很有用。有些时候你可能需要在 Markdown 插件中生成组件,并将复杂的数据写入到组件属性中,一个通常做法是使用 JSON.stringify + encodeURIComponent,并在客户端 decodeURIComponent + JSON.parse。但如果内容包含很多特殊字符,转换结果会很长。

]]>
+ 以下函数在 Node.js 和客户端上均可用。

+

这些函数在 @vuepress/helper @vuepress/helper/client@vuepress/helper/shared 中都可用。

+

数据相关

+

此方法在 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。
  • +
  • isLinkRelative(x): x 是否不是外部 URL 或绝对 URL。
  • +
  • ensureEndingSlash(x): 确保 x 以斜杠结尾。
  • +
  • ensureLeadingSlash(x): 确保 x 以斜杠开头。
  • +
  • removeEndingSlash(x): 确保 x 不以斜杠结尾。
  • +
  • removeLeadingSlash(x): 确保 x 不以斜杠开头。
  • +
+]]>
+
+ + 样式 + https://ecosystem.vuejs.press/ecosystem/zh/tools/helper/style.html + + 2025-01-10T18:07:54.000Z + 提供了如下样式文件。

+

规范化

+

@vuepress/helper/normalize.css 是一个 CSS 文件,用于规范化浏览器的默认样式。推荐在社区主题中引入它。

+]]>
+ 提供了如下样式文件。

+

规范化

+

@vuepress/helper/normalize.css 是一个 CSS 文件,用于规范化浏览器的默认样式。推荐在社区主题中引入它。

+]]>
+
+ + blog + https://ecosystem.vuejs.press/ecosystem/zh/plugins/blog/blog/ + + 2025-01-10T18:07:54.000Z + + + + + 配置 + https://ecosystem.vuejs.press/ecosystem/zh/plugins/blog/blog/config.html + + 2025-01-10T18:07:54.000Z + 插件选项 +

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
  • +
  • 默认值: `
  • +
+]]>
+ 插件选项 +

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 | false | ((name: string) => string)
+
+  /**
+   * 项目页面布局组件名称
+   *
+   * @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>[]
+}
+
]]>
+
+ + 指南 + https://ecosystem.vuejs.press/ecosystem/zh/plugins/blog/blog/guide.html + + 2025-01-10T18:07:54.000Z + 使用 @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, unknown> = {
+          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 布局:

+
<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" :key="path">
+        <RouteLink :key="name" :to="path" class="category">
+          {{ name }}
+          <span class="category-num">
+            {{ items.length }}
+          </span>
+        </RouteLink>
+      </li>
+    </ul>
+  </div>
+</template>
+

TagList 布局:

+
<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>
+    <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 v-if="categoryMap.currentItems" class="article-wrapper">
+      <div v-if="!categoryMap.currentItems.length">Nothing in here.</div>
+      <article
+        v-for="{ info, path } in categoryMap.currentItems"
+        :key="path"
+        class="article"
+        @click="$router.push(path)"
+      >
+        <header class="title">
+          {{ info.title }}
+        </header>
+        <hr />
+        <div class="article-info">
+          <span v-if="info.author" class="author"
+            >Author: {{ info.author }}</span
+          >
+          <span v-if="info.date" 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 布局:

+
<script setup lang="ts">
+import { useBlogType } from '@vuepress/plugin-blog/client'
+
+import ParentLayout from '@vuepress/theme-default/layouts/Layout.vue'
+import ArticleList from '../components/ArticleList.vue'
+
+const stars = useBlogType('star')
+</script>
+<template>
+  <div v-if="stars.items?.length" class="article-wrapper">
+    <article
+      v-for="{ info, path } in stars.items"
+      :key="path"
+      class="article"
+      @click="$router.push(path)"
+    >
+      <header class="title">
+        {{ info.title }}
+      </header>
+      <hr />
+      <div class="article-info">
+        <span v-if="info.author" class="author">Author: {{ info.author }}</span>
+        <span v-if="info.date" 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 v-else>Nothing in here.</div>
+</template>
+

有关返回类型,请参阅 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 内容中找到有效的摘要分隔符,如果找到,它将使用分隔符之前的内容,分隔符默认为 `

+]]>
+ 使用 @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, unknown> = {
+          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 布局:

+
<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" :key="path">
+        <RouteLink :key="name" :to="path" class="category">
+          {{ name }}
+          <span class="category-num">
+            {{ items.length }}
+          </span>
+        </RouteLink>
+      </li>
+    </ul>
+  </div>
+</template>
+

TagList 布局:

+
<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>
+    <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 v-if="categoryMap.currentItems" class="article-wrapper">
+      <div v-if="!categoryMap.currentItems.length">Nothing in here.</div>
+      <article
+        v-for="{ info, path } in categoryMap.currentItems"
+        :key="path"
+        class="article"
+        @click="$router.push(path)"
+      >
+        <header class="title">
+          {{ info.title }}
+        </header>
+        <hr />
+        <div class="article-info">
+          <span v-if="info.author" class="author"
+            >Author: {{ info.author }}</span
+          >
+          <span v-if="info.date" 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 布局:

+
<script setup lang="ts">
+import { useBlogType } from '@vuepress/plugin-blog/client'
+
+import ParentLayout from '@vuepress/theme-default/layouts/Layout.vue'
+import ArticleList from '../components/ArticleList.vue'
+
+const stars = useBlogType('star')
+</script>
+<template>
+  <div v-if="stars.items?.length" class="article-wrapper">
+    <article
+      v-for="{ info, path } in stars.items"
+      :key="path"
+      class="article"
+      @click="$router.push(path)"
+    >
+      <header class="title">
+        {{ info.title }}
+      </header>
+      <hr />
+      <div class="article-info">
+        <span v-if="info.author" class="author">Author: {{ info.author }}</span>
+        <span v-if="info.date" 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 v-else>Nothing in here.</div>
+</template>
+

有关返回类型,请参阅 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

+
+]]>
+
+ + comment + https://ecosystem.vuejs.press/ecosystem/zh/plugins/blog/comment/ + + 2024-05-29T05:40:07.000Z + + + + + 指南 + https://ecosystem.vuejs.press/ecosystem/zh/plugins/blog/comment/guide.html + + 2025-01-10T18:07:54.000Z + 设置选项 +

你既可以在 Node.js 一侧使用插件选项设置选项,也可以通过客户端配置文件在浏览器一侧设置选项。

+

通过插件选项

+
import { commentPlugin } from '@vuepress/plugin-comment'
+
+// .vuepress/config.ts
+export default {
+  plugins: [
+    commentPlugin({
+      provider: 'Artalk', // Artalk | Giscus | Waline | Twikoo
+
+      // 在这里放置其他选项
+      // ...
+    }),
+  ],
+}
+
]]>
+ 设置选项 +

你既可以在 Node.js 一侧使用插件选项设置选项,也可以通过客户端配置文件在浏览器一侧设置选项。

+

通过插件选项

+
import { commentPlugin } from '@vuepress/plugin-comment'
+
+// .vuepress/config.ts
+export default {
+  plugins: [
+    commentPlugin({
+      provider: 'Artalk', // Artalk | Giscus | Waline | Twikoo
+
+      // 在这里放置其他选项
+      // ...
+    }),
+  ],
+}
+

通过客户端配置文件

+
import {
+  defineArtalkConfig,
+  // defineGiscusConfig,
+  // defineTwikooConfig,
+  // defineWalineConfig,
+} from '@vuepress/plugin-comment/client'
+import { defineClientConfig } from 'vuepress/client'
+
+defineArtalkConfig({
+  // 选项
+})
+

有以下你需要注意的限制:

+
    +
  • +

    provider、多语言设置和其他资源相关选项必须在插件选项中设置。

    +

    为确保 tree-shaking 有效,我们必须在 Node 一侧优化入口,以便打包器可以了解最终打包中应包含哪些资源。

    +

    这些选项将在配置参考中用

    +
  • +
  • +

    不能序列化为 JSON 的选项必须在客户端配置中设置。

    +

    接收复杂值的选项(例如:函数)不能在插件选项中设置,因为插件运行在 Node.js 环境下,所以我们无法将这些值和它们的上下文传递给浏览器。

    +

    这些选项将在配置参考中用

    +
  • +
+

添加评论

+

该插件全局注册了一个组件 <CommentService />

+
    +
  • 如果你是用户,你应该使用 alias 和布局槽来插入组件。 我们建议你在 <PageNav /> 组件之后插入评论组件 (<CommentService />),本页可作为一个 Demo 作为参考。
  • +
  • 如果你是主题开发者,你应该将这个组件插入到你的主题布局中。
  • +
+

默认情况下,<CommentService /> 组件是全局启用的,你可以在插件选项和页面 frontmatter 中使用 comment 选项来控制它。

+
    +
  • 你可以通过在页面 frontmatter 中设置 comment: false 在本地禁用它。
  • +
  • 要使其全局禁用,请在插件选项中将 comment 设置为 false。 然后你可以在页面 frontmatter 中设置 comment: true 以在局部启用它。
  • +
+

你可以在页面 frontmatter 中设置 commentID 选项来自定义评论 ID,该 ID 用于标识要用于页面的评论存储项。默认情况下,它将是页面的 path ,这意味着如果你将站点部署到多个位置,站点间具有相同内容的页面将共享相同的评论数据。

+

可用的评论服务

+

目前你可以选择 GiscusWalineArtalkTwikoo

+
+

推荐的评论服务

+
    +
  • 面向程序员和开发人员: Giscus
  • +
  • 面向公众: Waline
  • +
+
+

通用选项

+

provider

+
    +
  • 类型: "Artalk" | "Giscus" | "Twikoo" | "Waline" | "None"
  • +
  • 默认值: "None"
  • +
  • 详情:评论服务提供者。
  • +
+

comment

+
    +
  • 类型: boolean
  • +
  • 默认值: true
  • +
  • 详情:是否默认启用评论功能。
  • +
+]]>
+
+ + feed + https://ecosystem.vuejs.press/ecosystem/zh/plugins/blog/feed/ + + 2025-01-10T18:07:54.000Z + + + + + 频道设置 + https://ecosystem.vuejs.press/ecosystem/zh/plugins/blog/feed/channel.html + + 2025-01-10T18:07:54.000Z + channel 插件选项用于配置 feed 的频道。

+

channel.title

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

频道的标题

+

channel.link

+
    +
  • 类型:string
  • +
  • 默认值:部署的网址 (通过 options.hostnamecontext.base 生成)
  • +
]]>
+ channel 插件选项用于配置 feed 的频道。

+

channel.title

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

频道的标题

+

channel.link

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

频道地址

+

channel.description

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

频道描述信息

+

channel.language

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

频道使用的语言

+

channel.copyright

+
    +
  • 类型: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

+
+]]>
+
+ + 插件配置 + https://ecosystem.vuejs.press/ecosystem/zh/plugins/blog/feed/config.html + + 2025-01-10T18:07:54.000Z + hostname +
    +
  • 类型:string
  • +
  • 必填:是
  • +
+

部署网站的域名。

+

atom

+
    +
  • 类型:boolean
  • +
  • 默认值:false
  • +
+

是否启用 Atom 格式输出。

+

json

+
    +
  • 类型:boolean
  • +
  • 默认值:false
  • +
+

是否启用 JSON 格式输出。

]]>
+ 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 }) =>
    +  Boolean(frontmatter.feed ?? (filePathRelative && !frontmatter.home))
    +
  • +
+

自定义的过滤函数,用于过滤哪些项目在 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 外,上述任何选项均受支持。

+]]>
+
+ + Frontmatter 配置 + https://ecosystem.vuejs.press/ecosystem/zh/plugins/blog/feed/frontmatter.html + + 2025-01-10T18:07:54.000Z + 你可以通过配置每个页面的 Frontmatter,来对每个 Feed 项目生成进行单独的控制。

+

添加与移除

+

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

+

读取的 Frontmatter 信息

+

title

+
    +
  • 类型:string
  • +
+

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

+

description

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

+

添加与移除

+

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

+

读取的 Frontmatter 信息

+

title

+
    +
  • 类型:string
  • +
+

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

+

description

+
    +
  • 类型:string
  • +
+

页面描述

+

date

+
    +
  • 类型:Date
  • +
+

页面的发布日期

+

article

+
    +
  • 类型:boolean
  • +
+

该页面是否是文章

+
+

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

+
+

copyright

+
    +
  • 类型: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。

+
+]]>
+
+ + Feed 获取器 + https://ecosystem.vuejs.press/ecosystem/zh/plugins/blog/feed/getter.html + + 2025-01-10T18:07:54.000Z + 你可以通过控制插件选项中的 getter 来完全控制 Feed 项目的生成。

+

getter.title

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

项目标题获取器

+

getter.link

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

项目链接获取器

+

getter.description

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

+

getter.title

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

项目标题获取器

+

getter.link

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

项目链接获取器

+

getter.description

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

项目描述获取器

+
+

提示

+

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

+
+

getter.content

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

项目内容获取器

+

getter.author

+
    +
  • 类型:(page: Page, app: App) => FeedAuthor[]
  • +
+

项目作者获取器。

+
+

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

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

getter.category

+
    +
  • 类型:(page: Page, app: App) => 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, app: App) => 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, app: App) => Date | undefined
  • +
+

项目发布日期获取器

+

getter.lastUpdateDate

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

项目最后更新日期获取器

+

getter.image

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

项目图片获取器

+
+

确保返回一个完整的 URL。

+
+

getter.contributor

+
    +
  • 类型:(page: Page, app: App) => FeedContributor[]
  • +
+

项目贡献者获取器

+
+

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

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

getter.copyright

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

项目版权获取器

+]]>
+
+ + 指南 + https://ecosystem.vuejs.press/ecosystem/zh/plugins/blog/feed/guide.html + + 2025-01-10T18:07:54.000Z + 使用 +

插件可为你生成以下三种格式的 feed 文件:

+
    +
  • Atom 1.0
  • +
  • JSON 1.1
  • +
  • RSS 2.0
  • +
+

请按照需要生成的格式,在插件选项中设置 atom, jsonrsstrue

+

为了正确生成 Feed 链接,你需要在插件选项中设置 hostname

+

可读的预览

+

当你在浏览器中打开 Feed 文件时,我们会通过 xsl 模板将 atom 和 rss feed xml 魔法般地转换为可读的 html。你可以查看本站的 atomrss feed 作为案例!

]]>
+ 使用 +

插件可为你生成以下三种格式的 feed 文件:

+
    +
  • Atom 1.0
  • +
  • JSON 1.1
  • +
  • RSS 2.0
  • +
+

请按照需要生成的格式,在插件选项中设置 atom, jsonrsstrue

+

为了正确生成 Feed 链接,你需要在插件选项中设置 hostname

+

可读的预览

+

当你在浏览器中打开 Feed 文件时,我们会通过 xsl 模板将 atom 和 rss feed xml 魔法般地转换为可读的 html。你可以查看本站的 atomrss 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 分别对不同语言提供不同的默认设置。

+]]>
+
+ + sass-palette + https://ecosystem.vuejs.press/ecosystem/zh/plugins/development/sass-palette/ + + 2025-01-10T18:07:54.000Z + + + + + 配置 + https://ecosystem.vuejs.press/ecosystem/zh/plugins/development/sass-palette/config.html + + 2025-01-10T18:07:54.000Z + 插件选项 +

id

+
    +
  • 类型: string
  • +
  • 必填: 是
  • +
+

调色板的唯一 ID,用于避免重复注册。

+

config

+
    +
  • 类型: string
  • +
  • 默认值: `.vuepress/styles/${id}-palette.scss`
  • +
+

用户配置文件路径,相对于源文件夹。

+
+

提示

+

这是用户设置样式变量的文件。

+

默认路径的文件名拥有上方的 ID 前缀。

+
]]>
+ 插件选项 +

id

+
    +
  • 类型: string
  • +
  • 必填: 是
  • +
+

调色板的唯一 ID,用于避免重复注册。

+

config

+
    +
  • 类型: string
  • +
  • 默认值: `.vuepress/styles/${id}-palette.scss`
  • +
+

用户配置文件路径,相对于源文件夹。

+
+

提示

+

这是用户设置样式变量的文件。

+

默认路径的文件名拥有上方的 ID 前缀。

+
+

defaultConfig

+
    +
  • 类型: string
  • +
  • 默认值: "@vuepress/plugin-sass-palette/styles/default/config.scss"
  • +
+

默认的配置文件路径,应为绝对路径。

+
+

提示

+

这是你应该通过 !default 来提供默认样式变量的文件。

+
+

palette

+
    +
  • 类型: string
  • +
  • 默认值: `.vuepress/styles/${id}-palette.scss`
  • +
+

用户的调色板文件路径,相对于源文件夹。

+
+

提示

+

这是用户控制注入 CSS 变量的文件。所有的变量会被转换为连字符格式然后被注入。

+

默认路径的文件名拥有上方的 ID 前缀。

+
+

defaultPalette

+
    +
  • 类型: string
  • +
  • 默认值: "@vuepress/plugin-sass-palette/styles/default/palette.scss"
  • +
+

默认的调色板文件路径,应为绝对路径。

+
+

提示

+

这是你应该通过 !default 来提供默认调色板值的文件。所有的变量会被转换为连字符格式然后被注入。

+
+

generator

+
    +
  • 类型: string
  • +
  • 必填: 否
  • +
+

自定义的生成器,用于生成调色板配置的衍生值。

+

如: 你可能想要根据 $theme-color 的值提供一个 $theme-color-light

+

style

+
    +
  • 类型: string
  • +
  • 必填: 否
  • +
+

用户的样式文件路径,相对于源文件夹。.

+

别名

+

可用的别名如下:

+
    +
  • 配置: @sass-palette/${id}-config (基于 id)
  • +
  • 调色板: @sass-palette/${id}-palette (基于 id)
  • +
  • 样式: @sass-palette/${id}-style (仅在设置了 style 选项时可用)
  • +
  • 助手: @sass-palette/helper
  • +
+]]>
+
+ + 指南 + https://ecosystem.vuejs.press/ecosystem/zh/plugins/development/sass-palette/guide.html + + 2025-01-10T18:07:54.000Z + 相比于 @vuepress/plugin-palette 插件,本插件允许你:

+
    +
  • 基于用户配置派生相关样式
  • +
  • 在插件中调用并提供和主题类似的样式自定义
  • +
  • 跨越多个插件或主题通过 id 选项分组应用
  • +
+

在使用插件前,你需要了解 id 选项,以及三个样式概念: 配置、调色板和派生器。

+

ID 选项

+

首先,你应该了解此插件的设计目标是提供跨越插件和主题的支持 (而并不像官方插件仅面向主题)。

]]>
+ 相比于 @vuepress/plugin-palette 插件,本插件允许你:

+
    +
  • 基于用户配置派生相关样式
  • +
  • 在插件中调用并提供和主题类似的样式自定义
  • +
  • 跨越多个插件或主题通过 id 选项分组应用
  • +
+

在使用插件前,你需要了解 id 选项,以及三个样式概念: 配置、调色板和派生器。

+

ID 选项

+

首先,你应该了解此插件的设计目标是提供跨越插件和主题的支持 (而并不像官方插件仅面向主题)。

+

我们提供了 id 选项来完成此目标,它将允许你:

+
    +
  • +

    在插件 (或主题) 间共享同一个样式系统。

    +

    所有别名和模块名称都具有 ID 前缀,这意味着你可以在你的插件 (或主题) 中使用一套样式变量来统一你的样式,而不会受到其他插件 (或主题) 的影响。

    +

    用户可以在同一个文件中配置所有颜色变量、断点和其他样式配置,并自动应用在具有相同 ID 的主题和插件上。

    +
    +

    示例

    +

    vuepress-theme-hope 及其它的相关插件都使用相同 ID hope 调用插件,因此用户在主题中配置的样式会自动在所有插件中生效。

    +
    +
  • +
  • +

    设置不同的 ID 时,插件们和主题之间互相完全独立。我们建议你使用你的插件名称设置 id 变量。

    +

    使用默认设置,用户将在 .vuepress/styles 文件夹下设置你的插件样式,其中 Sass 文件以你的 ID 前缀开头。你可以使用 ${id}-config${id}-palette 访问所需的变量。

    +
    +

    示例

    +

    vuepress-theme-hope 正在使用 ID "hope",而假设 vuepress-plugin-abc 正在使用 "abc"。他们可以分别使用 hope-config hope-paletteabc-config abc-palette 模块名称获取自己的变量。

    +
    +
  • +
  • +

    通过相同 ID 调用插件不会有任何副作用。

    +
    +

    示例

    +

    vuepress-theme-hope 及其它的相关插件都使用相同 ID hope 调用插件。

    +
    +
  • +
+

配置

+

配置文件仅用于提供 Sass 变量。它所包含 Sass 变量可以在其他文件中通过 ${id}-config 使用。

+

你可以指定一个文件作为用户配置文件。这样你可以稍后在插件 Sass 文件中访问包含每个变量的模块。此外,你还可以提供默认配置文件,你可以在其中使用 !default 为变量设置默认值。

+
一个例子 +

假设,你正在 vuepress-plugin-abc 中这样调用插件:

+
useSassPalette(app, {
+  id: 'abc',
+  defaultConfig: 'vuepress-plugin-abc/styles/config.scss',
+})
+

用户配置:

+
$navbar-height: 3.5rem;
+

默认配置:

+
$navbar-height: 2rem !default;
+$sidebar-width: 18rem !default;
+

你可以在插件 Sass 文件中获取到如下变量:

+
// Vue 单文件组件的 <style lang="scss"> 块或脚本中直接导入的 Scss 文件中
+@debug abc-config.$navbar-height; // 3.5rem
+@debug abc-config.$sidebar-width; // 18rem
+
+

限制

+

我们利用 additionalData 选项让 ${id}-config 模块在你的样式中可用,但这有一些限制。

+

additionalData 仅适用于 SASS 入口,因此 ${id}-config 仅适用于:

+
    +
  • Vue 单文件组件的 <style lang="scss">
  • +
  • 脚本中直接导入的 scss 文件 (例如: 客户端应用程序增强文件中的 import "./a-scss-file.scss") 。
  • +
+

如果 scss 文件不是直接导入的,而是通过 @use@import API 导入的,模块将不可用。因此,在这种情况下,你应该通过 @use "@sass-palette/${id}-config"; 手动导入模块。

+

调色板

+

调色板文件用于 CSS 变量注入,其中每个变量将被注入到 root 中,变量名称转换为 kebab-name 格式。

+

你可以指定一个文件作为用户调色板文件,默认文件名是 ${id}-palette.scss。 此外,你还可以提供一个默认的调色板文件,你可以在其中使用 !default 为变量设置默认值。

+
一个例子 +

假设,你正在 vuepress-plugin-abc 中这样调用插件:

+
useSassPalette(app, {
+  id: 'abc',
+  defaultPalette: 'vuepress-plugin-abc/styles/palette.scss',
+})
+

用户调色板:

+
$color-a: red;
+

默认调色板:

+
$color-a: blue !default;
+$color-b: green !default;
+

那么 root 选择器将会拥有下列 CSS 变量:

+
:root {
+  --color-a: red;
+  --color-b: green;
+}
+
+

和配置文件一样,调色板文件提供了一个 ${$id}-palette 模块 (也包含生成器的值),它也受 additionalData 选项的限制,因此如果你想在其他 Sass 文件中使用它,你应该手动导入模块。

+

颜色设置

+

由于默认主题支持深色模式,因此你可能希望在浅色模式和深色模式下使用不同的颜色。

+

为此,你应该使用包含 lightdark 键的映射设置颜色变量。 稍后,此插件将为你生成不同的颜色。

+
一个例子 +
// 用户调色板
+$text-color: (
+  light: #222,
+  dark: #999,
+);
+
+// 默认调色板
+$text-color: (
+  light: #2c3e50,
+  dark: #9e9e9e,
+) !default;
+$bg-color: (
+  light: #fff,
+  dark: #1e1e1e,
+) !default;
+

然后你会得到:

+
:root {
+  --text-color: #222;
+  --bg-color: #fff;
+}
+
+[data-theme='dark'] {
+  --text-color: #999;
+  --bg-color: #1e1e1e;
+}
+
+

允许的变量类型

+

调色板中只允许使用颜色 (或深浅模式颜色对象)、长度和字符串。任何其他类型都将被删除。

+
+

为什么除了字符串只允许颜色和长度

+

在常见情况下,你可能只想计算颜色和长度。所以放弃其他类型支持是相当安全的,因为你想要的任何其他值都可以转换为字符串。

+
示例 +

如果你想要一个 --move-transitionwidth 0.3s ease,你可以使用字符串:

+
// 这将被 sass 视为一个类型为 (length, time, function) 的列表
+// 并会触发警告并被插件删除
+$moveTransition: width 0.3s ease;
+
+// 这会得到你想要的
+// :root {
+//   --move-transition: width 0.3s ease
+// }
+$moveTransition: 'width 0.3s ease';
+
+
+

辅助模块

+

我们公开了 @vuepress/plugin-sass-palette 使用的内部函数,作为辅助模块。

+

你可以通过 @sass-palette/helper 别名使用此辅助模块,并调用其函数来自己实现类似的功能。

+

生成器

+

生成器文件面向开发人员使用配置或调色板文件变量生成衍生值。

+

你可以在此文件中直接获取调色板的变量值,并生成基于它们的新值。

+

生成器变量也将像调色板一样作为 CSS 变量注入,它们也在调色板模块中可用。

+
示例 +

你可能想要一个基于 $theme-color$theme-color-light。所以你可以这样写一个生成器:

+
@use 'sass:color';
+@use '@sass-palette/helper';
+
+$theme-color-light: (
+  light: color.scale(helper.get-color($theme-color), $lightness: 10%),
+  dark: color.scale(helper.get-dark-color($theme-color), $lightness: 10%),
+) !default;
+

你也可以通过导入配置文件来基于配置文件提供的变量生成值:

+
// id 为 "abc" 的生成器
+@use 'sass:color';
+@use '@sass-palette/abc-config';
+@use '@sass-palette/helper';
+
+$code-c-bg: abc-config.$highlighter == 'shiki'? #fff: #f8f8f8;
+
+

用户样式

+

如果你是主题开发人员,你可能希望为你的用户提供一种自定义主题或网站的方法。

+

在这种情况下,你应该在使用此插件时将 style 选项设置为用户样式文件。

+

稍后,你应该通过在你的主题样式之后导入 @sass-palette/${id}-style 来手动包含用户样式文件。

+
+

提示

+

@sass-palette/${id}-style 是用户样式文件的别名,你可以在 JS/TS/SASS 中导入它。

+
+]]>
+
+ + revealjs + https://ecosystem.vuejs.press/ecosystem/zh/plugins/markdown/revealjs/ + + 2025-01-10T18:07:54.000Z + + + + + 幻灯片演示 + https://ecosystem.vuejs.press/ecosystem/zh/plugins/markdown/revealjs/demo.html + + 2025-01-10T18:07:54.000Z + + + 幻灯片主题 + https://ecosystem.vuejs.press/ecosystem/zh/plugins/markdown/revealjs/themes.html + + 2025-01-10T18:07:54.000Z + auto +
+

基于主题模式

+
+

black

+

white

+

league

+

beige

+

sky

+

night

+

serif

+

simple

+

solarized

]]>
+ auto +
+

基于主题模式

+
+]]>
+
+ + pwa + https://ecosystem.vuejs.press/ecosystem/zh/plugins/pwa/pwa/ + + 2025-01-10T18:07:54.000Z + + + + + 配置 + https://ecosystem.vuejs.press/ecosystem/zh/plugins/pwa/pwa/config.html + + 2025-01-10T18:07:54.000Z + 选项 +

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
    • +
    +
    +
  • +
  • +

    参考:

    + +
  • +
]]>
+ 选项 +

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.maskIcon

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

apple.statusBarColor

+
    +
  • 类型:'black-translucent' | 'black' | 'default
  • +
  • 详情:Safari 状态栏颜色
  • +
+

foundComponent

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

readyComponent

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

appendBase

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

generateSwConfig

+
    +
  • +

    详情:

    +

    传递给 workbox-build 的选项,具体详情,请见 Workbox 文档

    +
  • +
+

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]: Partial<PwaPluginLocaleData>
    +}
    +
  • +
  • +

    详情:

    +

    PWA 插件的国际化配置。

    +
  • +
+
内置支持语言 +
    +
  • 简体中文 (zh-CN)
  • +
  • 繁体中文 (zh-TW)
  • +
  • 英文(美国) (en-US)
  • +
  • 德语 (de-DE)
  • +
  • 俄语 (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

+
    +
  • +

    详情:

    +

    返回此插件的事件派发器。

    +

    你可以添加监听器函数到 register-service-worker 提供的事件。

    +
  • +
  • +

    示例:

    +
  • +
+
import { usePwaEvent } from '@vuepress/plugin-pwa/client'
+
+export default {
+  setup(): void {
+    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(): void {
+    onMounted(() => {
+      forceUpdate()
+    })
+  },
+}
+

registerSW

+
    +
  • +

    详情:

    +

    手动注册 Service Worker。

    +
  • +
  • +

    参数:

    +
  • +
+

| 参数 | 类型 | 描述 |
+|

+]]>
+
+ + 指南 + https://ecosystem.vuejs.press/ecosystem/zh/plugins/pwa/pwa/guide.html + + 2025-01-10T18:07:54.000Z + 介绍 +

将你的 VuePress 站点变成渐进式网络应用程序 (PWA)[1]

+

此插件使用 workbox-build 生成 Service Worker 文件,并使用 register-service-worker 注册 Service Worker。

]]>
+ 介绍 +

将你的 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 清单[3]文件是必要的,并且你的 PWA 应满足可安装性[4]要求。

+

你可以通过设置 manifest 选项来自定义 manifest 文件,或者在 public 文件夹中提供 manifest.webmanifestmanifest.json。前者优先级更高。

+

插件会自动为你生成 manifest.webmanifest,并在每个页面的 <head> 中添加清单链接声明,但是 你至少应该通过 manifest.icons 或 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 页面。

+ +

大小控制

+

为了防止在预缓存列表中包含大文件,任何 > 2 MB 的文件或 > 1 MB 的图片都将被忽略。 你可以通过 maxSizemaxImageSize 来自定义大小限制 (单位为 KB)。

+

更新控制

+

我们提供 update 选项控制用户如何接收更新。

+

update 选项的默认值是 "available",这意味着当网站内容更新后,新的 SW 会在后台静默安装,并在安装结束后弹窗提示用户新内容就绪。用户可以自主选择是否立即刷新查看新内容。这意味在新 SW 就绪前用户会访问旧版本网站。

+

如果你的文档仍在建设期,希望尽早提示用户他可能在阅读已过时的内容,你可以将其设置为 "hint"。这样用户在进入文档后数秒内就可以收到新内容已发布的通知。但这样做的负面效果是如果用户在新 SW 就绪前选择更新,那么他将在新 SW 安装并接管页面前,需要从互联网获取页面的全部资源。

+

如果你的文档很稳定,或者你在托管博客,不太关心用户立即接收到最新版本,你可以将其设置为 "disable",这意味着新的 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 type="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 type="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. +
  3. Service Worker 简要介绍

    +
      +
    1. +

      Service Worker 会在注册过程中获取注册在其中的所有文件并缓存它们。

      +
    2. +
    3. +

      注册成功后,Service Worker 激活,并开始代理并控制你的全部请求。

      +
    4. +
    5. +

      每当你想要通过浏览器发起访问请求后,Service Worker 将会查看其是否存在与自身缓存列表中,若存在则直接返回缓存好的结果,否则调用自身的 fetch 方法进行获取。你可以通过自定义 fetch 方法,来完全控制网页内资源获取请求的结果,比如在离线时提供一个 fallback 的网页。

      +
    6. +
    7. +

      每次用户重新打开网站时,Service Worker 会向自身注册时的地址发出校验命令,如果检测到新版本的 Service Worker,则会更新自身,并开始缓存注册在新 Service Worker 中的资源列表。成功获取内容更新后,Service Worker 将会触发 update 事件。可以通过此事件提示用户,比如将在右下角显示一个弹出窗口,提示用户新内容可用并允许用户触发更新。

      +
    8. +
    + ↩︎ ↩︎
  4. +
  5. 清单文件

    +

    清单文件使用 JSON 格式,负责声明 PWA 各项信息,如名称、描述、图标、快捷动作等。

    +

    为了使你的站点能够被注册为 PWA,你需要满足 manifest 基本的规范,才能使浏览器认为该网站为一个可安装的 PWA 并允许用户安装它。

    +
    +

    提示

    +

    Manifest 的标准与规范,请详见 MDN 网络 App 清单W3C Manifest

    +
    + ↩︎
  6. +
  7. 可安装性

    +

    想要让网站可以注册为 PWA,网站需要自行成功注册有效的 Service Worker,同时拥有合法的 manifest 清单文件并在网站中声明它。

    +

    清单文件应至少包含 name(或 short_name) icons start_url

    +

    在 Safari 中,SW 的最大缓存空间为 50 MB。 ↩︎ ↩︎

    +
  8. +
  9. SSG: Static Site Generating,静态站点生成。 ↩︎

    +
  10. +
  11. SEO: Search Engine Optimization,搜索引擎增强,

    +

    详见 SEO 介绍 ↩︎

    +
  12. +
  13. SPA: Single Page Application, 单页应用

    +

    大多只有主页,并使用 history mode 处理路由,而不是真的在页面之间导航。 ↩︎

    +
  14. +
+
+]]>
+
+ + seo + https://ecosystem.vuejs.press/ecosystem/zh/plugins/seo/seo/ + + 2025-01-10T18:07:54.000Z + + + + + 选项 + https://ecosystem.vuejs.press/ecosystem/zh/plugins/seo/seo/config.html + + 2025-01-10T18:07:54.000Z + hostname +
    +
  • +

    类型:string

    +
  • +
  • +

    必填:是

    +
  • +
  • +

    详情:

    +

    部署域名

    +
  • +
+

author

+
    +
  • +

    类型:Author

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

    详情:

    +

    默认作者

    +
  • +
]]>
+ hostname +
    +
  • +

    类型:string

    +
  • +
  • +

    必填:是

    +
  • +
  • +

    详情:

    +

    部署域名

    +
  • +
+

author

+
    +
  • +

    类型:Author

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

    详情:

    +

    默认作者

    +
  • +
+

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 信息 */
    +  ogpInfo: 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>

    +
  • +
+]]>
+
+ + 指南 + https://ecosystem.vuejs.press/ecosystem/zh/plugins/seo/seo/guide.html + + 2025-01-10T18:07:54.000Z + 本插件会通过向网站 <head> 注入标签,让你的网站完全支持 开放内容协议 OGPJSON-LD 1.1,以全面增强站点的搜索引擎优化性。

+]]>
+ 本插件会通过向网站 <head> 注入标签,让你的网站完全支持 开放内容协议 OGPJSON-LD 1.1,以全面增强站点的搜索引擎优化性。

+ +

开箱即用

+

插件开箱即用,在不做任何配置的情况下,会尽可能通过页面内容,提取对应的信息补全 OGP 与 JSON-LD 所需的必要标签。

+

默认情况下,插件会读取站点配置、主题配置与页面的 frontmatter 来尽可能自动生成。诸如站点名称,页面标题,页面类型,写作日期,最后更新日期,文章标签均会自动生成。

+

默认的 OGP 生成逻辑

+

| 属性名称 | 值 |
+| :

+]]>
+
+ + sitemap + https://ecosystem.vuejs.press/ecosystem/zh/plugins/seo/sitemap/ + + 2025-01-10T18:07:54.000Z + + + + + 配置 + https://ecosystem.vuejs.press/ecosystem/zh/plugins/seo/sitemap/config.html + + 2025-01-10T18:07:54.000Z + hostname +
    +
  • +

    类型:string

    +
  • +
  • +

    必填:是

    +
  • +
  • +

    详情:

    +

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

    +
  • +
+

extraUrls

+
    +
  • +

    类型:string[]

    +
  • +
  • +

    详情:

    +

    需要额外包含的网址。

    +

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

    +
  • +
  • +

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

    +
  • +
]]>
+ 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 插件生成。

    +
  • +
+]]>
+
+ + Frontmatter + https://ecosystem.vuejs.press/ecosystem/zh/plugins/seo/sitemap/frontmatter.html + + 2025-01-10T18:07:54.000Z + sitemap +
    +
  • +

    类型:SitemapFrontmatterOptions | false

    +
  • +
  • +

    详情:

    +

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

    +
  • +
+

sitemap.changefreq

+
    +
  • +

    类型:"always" | "hourly" | "daily" | "weekly" | "monthly" | "yearly" | "never"

    +
  • +
  • +

    默认值:"daily"

    +
  • +
  • +

    详情:

    +

    页面默认更新频率。它会覆盖插件选项中的 changefreq 选项。

    +
  • +
]]>
+ sitemap +
    +
  • +

    类型:SitemapFrontmatterOptions | false

    +
  • +
  • +

    详情:

    +

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

    +
  • +
+

sitemap.changefreq

+
    +
  • +

    类型:"always" | "hourly" | "daily" | "weekly" | "monthly" | "yearly" | "never"

    +
  • +
  • +

    默认值:"daily"

    +
  • +
  • +

    详情:

    +

    页面默认更新频率。它会覆盖插件选项中的 changefreq 选项。

    +
  • +
+

sitemap.priority

+
    +
  • +

    类型:number

    +
  • +
  • +

    默认值:0.5

    +
  • +
  • +

    详情:

    +

    页面优先级,范围 01

    +
  • +
+]]>
+
+ + 指南 + https://ecosystem.vuejs.press/ecosystem/zh/plugins/seo/sitemap/guide.html + + 2025-01-10T18:07:54.000Z + 本插件会为你的网站自动生成 Sitemap。为了使插件正常工作,你需要将部署的域名传递给插件的 hostname 选项。如果你想在开发服务器中预览,请配置 devServer 选项。

+

插件会自动根据页面的 Git 的时间戳生成页面的最后更新时间,同时会根据站点的多语言配置声明页面的其他语言版本替代地址。

+

控制 Sitemap 链接

+

默认情况下,所有除 404 页面以外的网站链接均会被添加进 Sitemap。

+

如果你希望在 VuePress 项目页面之外,添加其他页面链接到 Sitemap,请将它们变成数组传入插件的 extraUrls 选项。

]]>
+ 本插件会为你的网站自动生成 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: /
+
+]]>
+
+ + 打包器相关 + https://ecosystem.vuejs.press/ecosystem/zh/tools/helper/node/bundler.html + + 2025-01-10T18:07:54.000Z + 打包器函数用于在主题和插件中追加或修改打包器选项。

+

这些函数仅在 @vuepress/helper 中可用。

+
+

提示

+

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

+

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

+
// 导入你需要的函数
+import { addCustomElement } from '@vuepress/helper'
+
+export const yourPlugin = {
+  // ...
+  extendsBundlerOptions: (bundlerOptions, app) => {
+    // 在此添加它们
+    addCustomElement(bundlerOptions, app, 'my-custom-element')
+  },
+}
+
]]>
+ 打包器函数用于在主题和插件中追加或修改打包器选项。

+

这些函数仅在 @vuepress/helper 中可用。

+
+

提示

+

所有函数都应在 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: RegExp | string[] | string,
+) => 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<Buffer | string>
+
+  /**
+   * 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,
+    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 {
    +  addViteOptimizeDepsExclude,
    +  addViteOptimizeDepsInclude,
    +  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
    +})
    +
    +
  • +
+]]>
+
+ + 多语言相关 + https://ecosystem.vuejs.press/ecosystem/zh/tools/helper/node/locales.html + + 2025-01-10T18:07:54.000Z + 这些函数仅在 @vuepress/helper 中可用。

+

getFullLocaleConfig

+

一个从内置的 locale 信息和用户配置中获取完整 locale 配置的辅助函数。

+
export interface GetLocaleConfigOption<T extends LocaleData> {
+  app: App
+  default: DefaultLocaleInfo<T>
+  config?: LocaleConfig<T> | undefined
+  name?: string
+}
+
+export const getFullLocaleConfig: <T extends LocaleData>(
+  options: GetLocaleConfigOption<T>,
+) => ExactLocaleConfig<T>
+
]]>
+ 这些函数仅在 @vuepress/helper 中可用。

+

getFullLocaleConfig

+

一个从内置的 locale 信息和用户配置中获取完整 locale 配置的辅助函数。

+
export interface GetLocaleConfigOption<T extends LocaleData> {
+  app: App
+  default: DefaultLocaleInfo<T>
+  config?: LocaleConfig<T> | undefined
+  name?: string
+}
+
+export const getFullLocaleConfig: <T extends LocaleData>(
+  options: GetLocaleConfigOption<T>,
+) => ExactLocaleConfig<T>
+
    +
  • +

    app 参数是 VuePress Node app 实例。

    +
  • +
  • +

    default 参数是默认的 locale 信息,应该是一个 locale 信息设置的数组。

    +

    每个 locale 信息设置应该是一个包含两个元素的元组:

    +
      +
    • 第一个元素是 locale 信息设置所属的语言代码数组。
    • +
    • 第二个元素是 locale 信息设置。
    • +
    +

    default 参数的示例:

    +
    const defaultLocaleInfo = [
    +  [
    +    ['en'],
    +    { title: 'VuePress', description: 'Vue-powered Static Site Generator' },
    +  ],
    +  [
    +    ['zh', 'zh-CN'],
    +    { title: 'VuePress', description: 'Vue 驱动的静态网站生成器' },
    +  ],
    +  [['zh-TW'], { title: 'VuePress', description: 'Vue 驅動的靜態網站生成器' }],
    +]
    +
  • +
  • +

    config 参数是用户 locale 配置,是可选的。

    +

    它应该是一个以 localePath 为键,以部分 locale 信息设置为值的对象。

    +

    config 参数的示例:

    +
    const userLocaleConfig = {
    +  '/zh/': { description: '由 Vue 驱动的静态网站生成器' },
    +  '/zh-TW/': { description: '由 Vue 驅動的靜態網站生成器' },
    +}
    +
  • +
  • +

    name 参数是插件名称,是可选的,仅用于日志记录。

    +
  • +
+

函数将自动合并默认的 locale 信息和用户 locale 配置,并返回最终的 locale 配置,其中用户 locale 配置将覆盖默认的 locale 信息。

+

默认的 locale 信息将根据站点配置中每个 locale 的当前语言选择,当 locale 的语言代码在默认的 locale 信息中找不到时,它将回退到以下存在的第一个:

+
    +
  • en-US 的 locale 信息
  • +
  • en 的 locale 信息
  • +
  • 默认 locale 信息的第一个元素
  • +
+]]>
+
+ + 页面相关 + https://ecosystem.vuejs.press/ecosystem/zh/tools/helper/node/page.html + + 2025-01-10T18:07:54.000Z + 页面常见信息生成器。

+

这些函数仅在 @vuepress/helper 中可用。

+

getPageExcerpt

+

获取页面摘要。

+
export interface PageExcerptOptions {
+  /**
+   * 摘要分隔符
+   *
+   * @default "
+
]]>
+ 页面常见信息生成器。

+

这些函数仅在 @vuepress/helper 中可用。

+

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
+
]]>
+
+ + Artalk + https://ecosystem.vuejs.press/ecosystem/zh/plugins/blog/comment/artalk/ + + 2025-01-10T18:07:54.000Z + Artalk 是一款简洁的自托管评论系统,你可以在服务器上轻松部署并置入前端页面中。

+

来到你的博客,或是任意位置,放置 Artalk 评论框,让页面具备丰富的社会化功能。

+]]>
+ Artalk 是一款简洁的自托管评论系统,你可以在服务器上轻松部署并置入前端页面中。

+

来到你的博客,或是任意位置,放置 Artalk 评论框,让页面具备丰富的社会化功能。

+ +

安装

+
npm i -D artalk
+

部署 Artalk 服务端

+

请参见 Artalk 文档

+

配置

+

请配置 provider: "Artalk" 并将你的服务端地址传入插件选项中的 server

+

其他的配置项详见 Artalk 配置

+
+

提示

+

插件保留 el 选项在页面自行插入 Artalk。同时插件会自动根据 VuePress 信息为你自动设置 pageTitle, pageKeysite 选项。

+
+

夜间模式

+

为了能使 Artalk 应用正确的主题,你需要通过 darkmode 属性向 <CommentService /> 传入一个布尔值,代表当前是否开启夜间模式。

+]]>
+
+ + Artalk 选项 + https://ecosystem.vuejs.press/ecosystem/zh/plugins/blog/comment/artalk/config.html + + 2024-08-20T15:23:47.000Z + 配置 +

详见 Artalk 配置

+
    +
  • +

    el pageTitle, pageKeysite 选项为插件的保留选项,将从 VuePress 配置中自动推断,不可设置。

    +
  • +
  • +

    imgUploaderavatarURLBuilder 这两个函数选项只能在客户端配置。

    +
  • +
]]>
+ 配置 +

详见 Artalk 配置

+
    +
  • +

    el pageTitle, pageKeysite 选项为插件的保留选项,将从 VuePress 配置中自动推断,不可设置。

    +
  • +
  • +

    imgUploaderavatarURLBuilder 这两个函数选项只能在客户端配置。

    +
  • +
+

插件配置

+

你可以直接在插件选项中配置可序列化的选项:

+
import { commentPlugin } from '@vuepress/plugin-comment'
+import { defineUserConfig } from 'vuepress'
+
+export default defineUserConfig({
+  plugins: [
+    commentPlugin({
+      provider: 'Artalk',
+      // 其他选项
+      // ...
+    }),
+  ],
+})
+

客户端配置

+

你可以使用 defineArtalkConfig 函数来配置 Artalk。

+
import { defineArtalkConfig } from '@vuepress/plugin-comment/client'
+import { defineClientConfig } from 'vuepress/client'
+
+defineArtalkConfig({
+  // Artalk 选项
+})
+
]]>
+
+ + Giscus + https://ecosystem.vuejs.press/ecosystem/zh/plugins/blog/comment/giscus/ + + 2025-01-10T18:07:54.000Z + Giscus 是一个基于 GitHub Discussion 的评论系统,启用简便。

+]]>
+ Giscus 是一个基于 GitHub Discussion 的评论系统,启用简便。

+ +

准备工作

+
    +
  1. +

    你需要创建一个公开仓库,并开启评论区,以作为评论存放的地点

    +
  2. +
  3. +

    你需要安装 Giscus App,使其有权限访问对应仓库。

    +
  4. +
  5. +

    在完成以上步骤后,请前往 Giscus 页面 获得你的设置。

    +

    你只需要填写仓库和 Discussion 分类,之后滚动到页面下部的 “启用 giscus” 部分,获取 data-repo, data-repo-id, data-categorydata-category-id 这四个属性。

    +
  6. +
+

配置

+

请配置 provider: "Giscus" 并将 data-repo, data-repo-id, data-categorydata-category-id 作为插件选项传入 repo, repoId, category categoryId

+

其他的配置项详见 Giscus 配置

+

主题

+

默认情况下,Giscus 使用 lightdark 主题 (基于夜间模式状态)。

+
+

夜间模式

+

为了能使 Giscus 应用正确的主题,你需要为 <CommentService /> 通过 darkmode 属性传入一个布尔值,代表当前是否开启夜间模式。

+
+

如果你想在日间模式和夜间模式下自定义主题,你可以设置 lightThemedarkTheme 选项,使用内置主题关键字或以 https:// 开头的自定义 css 链接。

+]]>
+
+
\ No newline at end of file diff --git a/zh/atom.xsl b/zh/atom.xsl new file mode 100644 index 0000000000..ac613618d9 --- /dev/null +++ b/zh/atom.xsl @@ -0,0 +1,534 @@ + + + + + + + Atom Feed + + + + + + +
+ + + +

+ + atom logo + + +

+

+ +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Last update time: + +
Author: + + , + + +
Contributor: + + , + + + +
Categories: + + , + + +
Copyright: + +
+
+ +
+ + +
+
+
+ +
+
+ + + + + + , + + + + + + + + + + + , + + + + + + + + + + + + + + + + + + + + + +
+
+
+ +
+ +
+
+
+ + + +
+
diff --git a/zh/feed.json b/zh/feed.json new file mode 100644 index 0000000000..220753871c --- /dev/null +++ b/zh/feed.json @@ -0,0 +1,1010 @@ +{ + "version": "https://jsonfeed.org/version/1.1", + "title": "VuePress 生态系统", + "home_page_url": "https://ecosystem.vuejs.press/ecosystem/zh/", + "feed_url": "https://ecosystem.vuejs.press/ecosystem/zh/feed.json", + "description": "VuePress 官方主题和插件", + "items": [ + { + "title": "插件", + "url": "https://ecosystem.vuejs.press/ecosystem/zh/plugins/", + "id": "https://ecosystem.vuejs.press/ecosystem/zh/plugins/", + "summary": "插件", + "content_html": "\n", + "date_modified": "2025-01-10T18:07:54.000Z", + "authors": [], + "tags": [] + }, + { + "title": "主题", + "url": "https://ecosystem.vuejs.press/ecosystem/zh/themes/", + "id": "https://ecosystem.vuejs.press/ecosystem/zh/themes/", + "summary": "主题", + "content_html": "\n", + "date_modified": "2025-01-10T18:07:54.000Z", + "authors": [], + "tags": [] + }, + { + "title": "主题指南", + "url": "https://ecosystem.vuejs.press/ecosystem/zh/themes/guidelines.html", + "id": "https://ecosystem.vuejs.press/ecosystem/zh/themes/guidelines.html", + "summary": "主题指南 为了避免主题开发者和用户设置不必要的选项,我们制定了一套主题创建时应遵循的指南。 DOM 结构 一个主题必须实现以下 DOM 结构: 容器:一个包含整个主题的元素。此元素应该有一个 vp-container 属性。 内容:一个包含 markdown 渲染结果的元素。此元素应该有一个 vp-content 属性。 一个主题可以有以下可选元素: ...", + "content_html": "\n

为了避免主题开发者和用户设置不必要的选项,我们制定了一套主题创建时应遵循的指南。

\n

DOM 结构

\n

一个主题必须实现以下 DOM 结构:

\n
    \n
  • 容器:一个包含整个主题的元素。此元素应该有一个 vp-container 属性。
  • \n
  • 内容:一个包含 markdown 渲染结果的元素。此元素应该有一个 vp-content 属性。
  • \n
\n

一个主题可以有以下可选元素:

\n
    \n
  • 导航栏:站点的导航栏。此元素应该有一个 vp-navbar 属性。
  • \n
  • 侧边栏:站点的侧边栏。此元素应该有一个 vp-sidebar 属性。
  • \n
  • 大纲:主要内容的标题或大纲。此元素应该有一个 vp-outline 属性。
  • \n
  • 评论:评论服务(评论框和评论列表)。此元素应该有一个 vp-comment 属性。
  • \n
  • 页脚:站点的页脚。此元素应该有一个 vp-footer 属性。
  • \n
\n

一个主题必须:

\n
    \n
  • 在暗色模式下,将 html 元素的 data-theme 设置为 dark
  • \n
  • 在亮色模式下,将 html 元素的 data-theme 设置为 light
  • \n
\n

如果主题只有一种颜色方案,主题仍然需要将 data-theme 设置为 lightdark,以指示默认颜色方案。

\n

组件

\n

为了支持搜索插件,主题应检查 <SearchBox /> 是否已全局注册,并在其自己的导航栏或侧边栏中呈现(如果可用)。

\n

颜色变量

\n

一个主题必须实现以下颜色变量:

\n

文字

\n
    \n
  • --vp-c-text:默认文本颜色。
  • \n
  • --vp-c-text-mute:用于静音文本的颜色,例如“非活动菜单”或“信息文本”。
  • \n
  • --vp-c-text-subtle:用于细微文本的颜色,例如“占位符”或“插入符号”。
  • \n
\n

背景

\n
    \n
  • --vp-c-bg:用于主屏幕的背景颜色。
  • \n
  • --vp-c-bg-alt:用于“侧边栏”或“代码块”等地方的备用背景颜色。
  • \n
  • --vp-c-bg-elv:用于“浮动”部分的提升背景颜色,例如“对话框”。
  • \n
\n

阴影

\n
    \n
  • --vp-c-shadow:阴影颜色
  • \n
\n

强调

\n

用于交互组件的强调颜色和品牌颜色。

\n
    \n
  • \n

    --vp-c-accent:主要用于彩色文本的最实色。它必须满足与放在 --vp-c-accent-soft 顶部时的对比度。

    \n
  • \n
  • \n

    --vp-c-accent-hover:用于悬停状态的颜色。

    \n
  • \n
  • \n

    --vp-c-accent-bg:用于实色背景的颜色。它必须满足与放在其顶部的 --vp-c-accent-text 的对比度。

    \n
  • \n
  • \n

    --vp-c-accent-text:用于 --vp-c-accent-bg 背景的文本颜色。它必须满足与 --vp-c-accent-bg 的对比度。

    \n
  • \n
  • \n

    --vp-c-accent-soft:用于自定义容器或徽章等细微背景的颜色。当将 --vp-c-accent 颜色放在其顶部时,它必须满足对比度。

    \n

    软色必须是半透明的 alpha 通道。这是至关重要的,因为它允许将多个“软”颜色叠加在一起以创建强调,例如在自定义容器内部有内联代码块时。

    \n
  • \n
\n

边框

\n
    \n
  • --vp-c-border:交互组件的边框颜色。例如,这应该用于按钮轮廓。
  • \n
  • --vp-c-border-hard:较暗的边框颜色,用于紧贴文本的“硬”边框,例如表格和 kbd。
  • \n
  • --vp-c-divider:分隔符的颜色,用于在同一组件内分隔部分,例如在“h2”标题上放置分隔符。
  • \n
\n

控件

\n
    \n
  • --vp-c-control:用于交互控件(例如按钮或复选框)的背景颜色。
  • \n
  • --vp-c-control-hover:用于交互控件悬停状态的背景颜色。
  • \n
  • --vp-c-control-disabled:用于交互控件禁用状态的颜色。
  • \n
\n

过渡时间

\n
    \n
  • --vp-t-color:颜色过渡时间。
  • \n
  • --vp-t-transform:变换过渡时间。
  • \n
\n

案例

\n", + "date_modified": "2025-01-10T18:07:54.000Z", + "authors": [], + "tags": [] + }, + { + "title": "工具包", + "url": "https://ecosystem.vuejs.press/ecosystem/zh/tools/", + "id": "https://ecosystem.vuejs.press/ecosystem/zh/tools/", + "summary": "工具包", + "content_html": "\n", + "date_modified": "2025-01-10T18:07:54.000Z", + "authors": [], + "tags": [] + }, + { + "title": "统计分析插件", + "url": "https://ecosystem.vuejs.press/ecosystem/zh/plugins/analytics/", + "id": "https://ecosystem.vuejs.press/ecosystem/zh/plugins/analytics/", + "summary": "统计分析插件", + "content_html": "\n", + "date_modified": "2025-01-10T18:07:54.000Z", + "authors": [], + "tags": [] + }, + { + "title": "baidu-analytics", + "url": "https://ecosystem.vuejs.press/ecosystem/zh/plugins/analytics/baidu-analytics.html", + "id": "https://ecosystem.vuejs.press/ecosystem/zh/plugins/analytics/baidu-analytics.html", + "summary": "baidu-analytics", + "content_html": "\n", + "date_modified": "2025-01-10T18:07:54.000Z", + "authors": [], + "tags": [] + }, + { + "title": "google-analytics", + "url": "https://ecosystem.vuejs.press/ecosystem/zh/plugins/analytics/google-analytics.html", + "id": "https://ecosystem.vuejs.press/ecosystem/zh/plugins/analytics/google-analytics.html", + "summary": "google-analytics", + "content_html": "\n", + "date_modified": "2025-01-10T18:07:54.000Z", + "authors": [], + "tags": [] + }, + { + "title": "umami-analytics", + "url": "https://ecosystem.vuejs.press/ecosystem/zh/plugins/analytics/umami-analytics.html", + "id": "https://ecosystem.vuejs.press/ecosystem/zh/plugins/analytics/umami-analytics.html", + "summary": "umami-analytics", + "content_html": "\n", + "date_modified": "2025-01-10T18:07:54.000Z", + "authors": [], + "tags": [] + }, + { + "title": "博客插件", + "url": "https://ecosystem.vuejs.press/ecosystem/zh/plugins/blog/", + "id": "https://ecosystem.vuejs.press/ecosystem/zh/plugins/blog/", + "summary": "博客插件", + "content_html": "\n", + "date_modified": "2025-01-10T18:07:54.000Z", + "authors": [], + "tags": [] + }, + { + "title": "开发插件", + "url": "https://ecosystem.vuejs.press/ecosystem/zh/plugins/development/", + "id": "https://ecosystem.vuejs.press/ecosystem/zh/plugins/development/", + "summary": "开发插件", + "content_html": "\n", + "date_modified": "2025-01-10T18:07:54.000Z", + "authors": [], + "tags": [] + }, + { + "title": "active-header-links", + "url": "https://ecosystem.vuejs.press/ecosystem/zh/plugins/development/active-header-links.html", + "id": "https://ecosystem.vuejs.press/ecosystem/zh/plugins/development/active-header-links.html", + "summary": "active-header-links", + "content_html": "\n", + "date_modified": "2025-01-10T18:07:54.000Z", + "authors": [], + "tags": [] + }, + { + "title": "git", + "url": "https://ecosystem.vuejs.press/ecosystem/zh/plugins/development/git.html", + "id": "https://ecosystem.vuejs.press/ecosystem/zh/plugins/development/git.html", + "summary": "git", + "content_html": "\n", + "date_modified": "2025-01-10T18:07:54.000Z", + "authors": [], + "tags": [] + }, + { + "title": "palette", + "url": "https://ecosystem.vuejs.press/ecosystem/zh/plugins/development/palette.html", + "id": "https://ecosystem.vuejs.press/ecosystem/zh/plugins/development/palette.html", + "summary": "palette", + "content_html": "\n", + "date_modified": "2025-01-10T18:07:54.000Z", + "authors": [], + "tags": [] + }, + { + "title": "reading-time", + "url": "https://ecosystem.vuejs.press/ecosystem/zh/plugins/development/reading-time.html", + "id": "https://ecosystem.vuejs.press/ecosystem/zh/plugins/development/reading-time.html", + "summary": "reading-time", + "content_html": "\n", + "date_modified": "2025-01-10T18:07:54.000Z", + "authors": [], + "tags": [] + }, + { + "title": "rtl", + "url": "https://ecosystem.vuejs.press/ecosystem/zh/plugins/development/rtl.html", + "id": "https://ecosystem.vuejs.press/ecosystem/zh/plugins/development/rtl.html", + "summary": "rtl", + "content_html": "\n", + "date_modified": "2025-01-10T18:07:54.000Z", + "authors": [], + "tags": [] + }, + { + "title": "theme-data", + "url": "https://ecosystem.vuejs.press/ecosystem/zh/plugins/development/theme-data.html", + "id": "https://ecosystem.vuejs.press/ecosystem/zh/plugins/development/theme-data.html", + "summary": "theme-data", + "content_html": "\n", + "date_modified": "2025-01-10T18:07:54.000Z", + "authors": [], + "tags": [] + }, + { + "title": "toc", + "url": "https://ecosystem.vuejs.press/ecosystem/zh/plugins/development/toc.html", + "id": "https://ecosystem.vuejs.press/ecosystem/zh/plugins/development/toc.html", + "content_html": "\n", + "date_modified": "2025-01-10T18:07:54.000Z", + "authors": [], + "tags": [] + }, + { + "title": "功能插件", + "url": "https://ecosystem.vuejs.press/ecosystem/zh/plugins/features/", + "id": "https://ecosystem.vuejs.press/ecosystem/zh/plugins/features/", + "summary": "功能插件", + "content_html": "\n", + "date_modified": "2025-01-10T18:07:54.000Z", + "authors": [], + "tags": [] + }, + { + "title": "back-to-top", + "url": "https://ecosystem.vuejs.press/ecosystem/zh/plugins/features/back-to-top.html", + "id": "https://ecosystem.vuejs.press/ecosystem/zh/plugins/features/back-to-top.html", + "summary": "back-to-top", + "content_html": "\n", + "date_modified": "2025-01-10T18:07:54.000Z", + "authors": [], + "tags": [] + }, + { + "title": "catalog", + "url": "https://ecosystem.vuejs.press/ecosystem/zh/plugins/features/catalog.html", + "id": "https://ecosystem.vuejs.press/ecosystem/zh/plugins/features/catalog.html", + "summary": "catalog", + "content_html": "\n", + "date_modified": "2025-01-10T18:07:54.000Z", + "authors": [], + "tags": [] + }, + { + "title": "copy-code", + "url": "https://ecosystem.vuejs.press/ecosystem/zh/plugins/features/copy-code.html", + "id": "https://ecosystem.vuejs.press/ecosystem/zh/plugins/features/copy-code.html", + "summary": "copy-code", + "content_html": "\n", + "date_modified": "2025-01-10T18:07:54.000Z", + "authors": [], + "tags": [] + }, + { + "title": "copyright", + "url": "https://ecosystem.vuejs.press/ecosystem/zh/plugins/features/copyright.html", + "id": "https://ecosystem.vuejs.press/ecosystem/zh/plugins/features/copyright.html", + "summary": "copyright", + "content_html": "\n", + "date_modified": "2025-01-10T18:07:54.000Z", + "authors": [], + "tags": [] + }, + { + "title": "icon", + "url": "https://ecosystem.vuejs.press/ecosystem/zh/plugins/features/icon.html", + "id": "https://ecosystem.vuejs.press/ecosystem/zh/plugins/features/icon.html", + "summary": "icon", + "content_html": "\n", + "date_modified": "2025-01-10T18:07:54.000Z", + "authors": [], + "tags": [] + }, + { + "title": "medium-zoom", + "url": "https://ecosystem.vuejs.press/ecosystem/zh/plugins/features/medium-zoom.html", + "id": "https://ecosystem.vuejs.press/ecosystem/zh/plugins/features/medium-zoom.html", + "summary": "medium-zoom", + "content_html": "\n", + "date_modified": "2025-01-10T18:07:54.000Z", + "authors": [], + "tags": [] + }, + { + "title": "notice", + "url": "https://ecosystem.vuejs.press/ecosystem/zh/plugins/features/notice.html", + "id": "https://ecosystem.vuejs.press/ecosystem/zh/plugins/features/notice.html", + "summary": "notice", + "content_html": "\n", + "date_modified": "2025-01-10T18:07:54.000Z", + "authors": [], + "tags": [] + }, + { + "title": "nprogress", + "url": "https://ecosystem.vuejs.press/ecosystem/zh/plugins/features/nprogress.html", + "id": "https://ecosystem.vuejs.press/ecosystem/zh/plugins/features/nprogress.html", + "summary": "nprogress", + "content_html": "\n", + "date_modified": "2025-01-10T18:07:54.000Z", + "authors": [], + "tags": [] + }, + { + "title": "photo-swipe", + "url": "https://ecosystem.vuejs.press/ecosystem/zh/plugins/features/photo-swipe.html", + "id": "https://ecosystem.vuejs.press/ecosystem/zh/plugins/features/photo-swipe.html", + "summary": "photo-swipe", + "content_html": "\n", + "date_modified": "2025-01-10T18:07:54.000Z", + "authors": [], + "tags": [] + }, + { + "title": "watermark", + "url": "https://ecosystem.vuejs.press/ecosystem/zh/plugins/features/watermark.html", + "id": "https://ecosystem.vuejs.press/ecosystem/zh/plugins/features/watermark.html", + "summary": "watermark", + "content_html": "\n", + "date_modified": "2025-01-10T18:07:54.000Z", + "authors": [], + "tags": [] + }, + { + "title": "Markdown 插件", + "url": "https://ecosystem.vuejs.press/ecosystem/zh/plugins/markdown/", + "id": "https://ecosystem.vuejs.press/ecosystem/zh/plugins/markdown/", + "summary": "Markdown 插件", + "content_html": "\n", + "date_modified": "2025-01-10T18:07:54.000Z", + "authors": [], + "tags": [] + }, + { + "title": "append-date", + "url": "https://ecosystem.vuejs.press/ecosystem/zh/plugins/markdown/append-date.html", + "id": "https://ecosystem.vuejs.press/ecosystem/zh/plugins/markdown/append-date.html", + "summary": "append-date", + "content_html": "\n", + "date_modified": "2025-01-10T18:07:54.000Z", + "authors": [], + "tags": [] + }, + { + "title": "links-check", + "url": "https://ecosystem.vuejs.press/ecosystem/zh/plugins/markdown/links-check.html", + "id": "https://ecosystem.vuejs.press/ecosystem/zh/plugins/markdown/links-check.html", + "summary": "links-check", + "content_html": "\n", + "date_modified": "2025-01-10T18:07:54.000Z", + "authors": [], + "tags": [] + }, + { + "title": "markdown-container", + "url": "https://ecosystem.vuejs.press/ecosystem/zh/plugins/markdown/markdown-container.html", + "id": "https://ecosystem.vuejs.press/ecosystem/zh/plugins/markdown/markdown-container.html", + "summary": "markdown-container", + "content_html": "\n", + "date_modified": "2025-01-10T18:07:54.000Z", + "authors": [], + "tags": [] + }, + { + "title": "markdown-ext", + "url": "https://ecosystem.vuejs.press/ecosystem/zh/plugins/markdown/markdown-ext.html", + "id": "https://ecosystem.vuejs.press/ecosystem/zh/plugins/markdown/markdown-ext.html", + "summary": "markdown-ext", + "content_html": "\n", + "date_modified": "2025-01-10T18:07:54.000Z", + "authors": [], + "tags": [] + }, + { + "title": "markdown-hint", + "url": "https://ecosystem.vuejs.press/ecosystem/zh/plugins/markdown/markdown-hint.html", + "id": "https://ecosystem.vuejs.press/ecosystem/zh/plugins/markdown/markdown-hint.html", + "summary": "markdown-hint", + "content_html": "\n", + "date_modified": "2025-01-10T18:07:54.000Z", + "authors": [], + "tags": [] + }, + { + "title": "markdown-image", + "url": "https://ecosystem.vuejs.press/ecosystem/zh/plugins/markdown/markdown-image.html", + "id": "https://ecosystem.vuejs.press/ecosystem/zh/plugins/markdown/markdown-image.html", + "summary": "markdown-image", + "content_html": "\n", + "image": "https://ecosystem.vuejs.press/ecosystem/images/icon/github-light.svg#dark", + "date_modified": "2025-01-10T18:07:54.000Z", + "authors": [], + "tags": [] + }, + { + "title": "markdown-include", + "url": "https://ecosystem.vuejs.press/ecosystem/zh/plugins/markdown/markdown-include.html", + "id": "https://ecosystem.vuejs.press/ecosystem/zh/plugins/markdown/markdown-include.html", + "summary": "markdown-include", + "content_html": "\n", + "date_modified": "2025-01-10T18:07:54.000Z", + "authors": [], + "tags": [] + }, + { + "title": "markdown-math", + "url": "https://ecosystem.vuejs.press/ecosystem/zh/plugins/markdown/markdown-math.html", + "id": "https://ecosystem.vuejs.press/ecosystem/zh/plugins/markdown/markdown-math.html", + "summary": "markdown-math", + "content_html": "\n", + "date_modified": "2025-01-10T18:07:54.000Z", + "authors": [], + "tags": [] + }, + { + "title": "markdown-stylize", + "url": "https://ecosystem.vuejs.press/ecosystem/zh/plugins/markdown/markdown-stylize.html", + "id": "https://ecosystem.vuejs.press/ecosystem/zh/plugins/markdown/markdown-stylize.html", + "summary": "markdown-stylize", + "content_html": "\n", + "date_modified": "2025-01-10T18:07:54.000Z", + "authors": [], + "tags": [] + }, + { + "title": "markdown-tab", + "url": "https://ecosystem.vuejs.press/ecosystem/zh/plugins/markdown/markdown-tab.html", + "id": "https://ecosystem.vuejs.press/ecosystem/zh/plugins/markdown/markdown-tab.html", + "summary": "markdown-tab", + "content_html": "\n", + "date_modified": "2025-01-10T18:07:54.000Z", + "authors": [], + "tags": [] + }, + { + "title": "prismjs", + "url": "https://ecosystem.vuejs.press/ecosystem/zh/plugins/markdown/prismjs.html", + "id": "https://ecosystem.vuejs.press/ecosystem/zh/plugins/markdown/prismjs.html", + "summary": "prismjs", + "content_html": "\n", + "date_modified": "2025-01-10T18:07:54.000Z", + "authors": [], + "tags": [] + }, + { + "title": "shiki", + "url": "https://ecosystem.vuejs.press/ecosystem/zh/plugins/markdown/shiki.html", + "id": "https://ecosystem.vuejs.press/ecosystem/zh/plugins/markdown/shiki.html", + "summary": "shiki", + "content_html": "\n", + "date_modified": "2025-01-10T18:07:54.000Z", + "authors": [], + "tags": [] + }, + { + "title": "渐进式应用插件", + "url": "https://ecosystem.vuejs.press/ecosystem/zh/plugins/pwa/", + "id": "https://ecosystem.vuejs.press/ecosystem/zh/plugins/pwa/", + "summary": "渐进式应用插件", + "content_html": "\n", + "date_modified": "2025-01-10T18:07:54.000Z", + "authors": [], + "tags": [] + }, + { + "title": "remove-pwa", + "url": "https://ecosystem.vuejs.press/ecosystem/zh/plugins/pwa/remove-pwa.html", + "id": "https://ecosystem.vuejs.press/ecosystem/zh/plugins/pwa/remove-pwa.html", + "summary": "remove-pwa", + "content_html": "\n", + "date_modified": "2025-01-10T18:07:54.000Z", + "authors": [], + "tags": [] + }, + { + "title": "搜索插件", + "url": "https://ecosystem.vuejs.press/ecosystem/zh/plugins/search/", + "id": "https://ecosystem.vuejs.press/ecosystem/zh/plugins/search/", + "summary": "搜索插件", + "content_html": "\n", + "date_modified": "2025-01-10T18:07:54.000Z", + "authors": [], + "tags": [] + }, + { + "title": "docsearch", + "url": "https://ecosystem.vuejs.press/ecosystem/zh/plugins/search/docsearch.html", + "id": "https://ecosystem.vuejs.press/ecosystem/zh/plugins/search/docsearch.html", + "summary": "docsearch", + "content_html": "\n", + "date_modified": "2025-01-10T18:07:54.000Z", + "authors": [], + "tags": [] + }, + { + "title": "搜索插件指南", + "url": "https://ecosystem.vuejs.press/ecosystem/zh/plugins/search/guidelines.html", + "id": "https://ecosystem.vuejs.press/ecosystem/zh/plugins/search/guidelines.html", + "summary": "搜索插件指南 为了使 VuePress 主题开箱即用地支持搜索插件,我们有一套创建搜索插件时应遵循的指南。 组件名称 如果搜索插件提供了适合在导航栏或侧边栏中显示的搜索框,则应将其命名为 并进行全局注册。 如果搜索插件提供了适合在单个页面中显示的复杂搜索结果组件(包含输入和结果列表),则应将其命名为 为了使 VuePress 主题开箱即用地支持搜索插件,我们有一套创建搜索插件时应遵循的指南。

\n

组件名称

\n
    \n
  • \n

    如果搜索插件提供了适合在导航栏或侧边栏中显示的搜索框,则应将其命名为 <SearchBox /> 并进行全局注册。

    \n
  • \n
  • \n

    如果搜索插件提供了适合在单个页面中显示的复杂搜索结果组件(包含输入和结果列表),则应将其命名为 <SearchPanel /> 并进行全局注册。

    \n

    搜索插件应在每个语言环境中自动生成一个包含 <SearchPanel /> 组件的 /search.html 页面,但不得覆盖任何现有页面。

    \n
  • \n
\n", + "date_modified": "2025-01-10T18:07:54.000Z", + "authors": [], + "tags": [] + }, + { + "title": "search", + "url": "https://ecosystem.vuejs.press/ecosystem/zh/plugins/search/search.html", + "id": "https://ecosystem.vuejs.press/ecosystem/zh/plugins/search/search.html", + "summary": "search", + "content_html": "\n", + "date_modified": "2025-01-10T18:07:54.000Z", + "authors": [], + "tags": [] + }, + { + "title": "slimsearch", + "url": "https://ecosystem.vuejs.press/ecosystem/zh/plugins/search/slimsearch.html", + "id": "https://ecosystem.vuejs.press/ecosystem/zh/plugins/search/slimsearch.html", + "summary": "slimsearch", + "content_html": "\n", + "date_modified": "2025-01-10T18:07:54.000Z", + "authors": [], + "tags": [] + }, + { + "title": "搜索引擎优化插件", + "url": "https://ecosystem.vuejs.press/ecosystem/zh/plugins/seo/", + "id": "https://ecosystem.vuejs.press/ecosystem/zh/plugins/seo/", + "summary": "搜索引擎优化插件", + "content_html": "\n", + "date_modified": "2025-01-10T18:07:54.000Z", + "authors": [], + "tags": [] + }, + { + "title": "工具插件", + "url": "https://ecosystem.vuejs.press/ecosystem/zh/plugins/tools/", + "id": "https://ecosystem.vuejs.press/ecosystem/zh/plugins/tools/", + "summary": "工具插件", + "content_html": "\n", + "date_modified": "2025-01-10T18:07:54.000Z", + "authors": [], + "tags": [] + }, + { + "title": "cache", + "url": "https://ecosystem.vuejs.press/ecosystem/zh/plugins/tools/cache.html", + "id": "https://ecosystem.vuejs.press/ecosystem/zh/plugins/tools/cache.html", + "summary": "cache", + "content_html": "\n", + "date_modified": "2025-01-10T18:07:54.000Z", + "authors": [], + "tags": [] + }, + { + "title": "google-tag-manager", + "url": "https://ecosystem.vuejs.press/ecosystem/zh/plugins/tools/google-tag-manager.html", + "id": "https://ecosystem.vuejs.press/ecosystem/zh/plugins/tools/google-tag-manager.html", + "summary": "google-tag-manager", + "content_html": "\n", + "date_modified": "2025-01-10T18:07:54.000Z", + "authors": [], + "tags": [] + }, + { + "title": "redirect", + "url": "https://ecosystem.vuejs.press/ecosystem/zh/plugins/tools/redirect.html", + "id": "https://ecosystem.vuejs.press/ecosystem/zh/plugins/tools/redirect.html", + "summary": "redirect", + "content_html": "\n", + "date_modified": "2025-01-10T18:07:54.000Z", + "authors": [], + "tags": [] + }, + { + "title": "register-components", + "url": "https://ecosystem.vuejs.press/ecosystem/zh/plugins/tools/register-components.html", + "id": "https://ecosystem.vuejs.press/ecosystem/zh/plugins/tools/register-components.html", + "summary": "register-components", + "content_html": "\n", + "date_modified": "2025-01-10T18:07:54.000Z", + "authors": [], + "tags": [] + }, + { + "title": "默认主题", + "url": "https://ecosystem.vuejs.press/ecosystem/zh/themes/default/", + "id": "https://ecosystem.vuejs.press/ecosystem/zh/themes/default/", + "summary": "默认主题", + "content_html": "\n", + "date_modified": "2025-01-10T18:07:54.000Z", + "authors": [], + "tags": [] + }, + { + "title": "内置组件", + "url": "https://ecosystem.vuejs.press/ecosystem/zh/themes/default/components.html", + "id": "https://ecosystem.vuejs.press/ecosystem/zh/themes/default/components.html", + "summary": "内置组件", + "content_html": "\n", + "date_modified": "2025-01-10T18:07:54.000Z", + "authors": [], + "tags": [] + }, + { + "title": "配置", + "url": "https://ecosystem.vuejs.press/ecosystem/zh/themes/default/config.html", + "id": "https://ecosystem.vuejs.press/ecosystem/zh/themes/default/config.html", + "summary": "配置 基础配置 hostname 类型: string 详情: 部署的域名,例如 https://example.com locales 类型: { [path: string]: Partial } 默认值: {} 详情: 多语言支持的各个语言 locales 。 所有在 Locale 配置 章节内的配...", + "content_html": "\n

基础配置

\n

hostname

\n
    \n
  • \n

    类型: string

    \n
  • \n
  • \n

    详情:

    \n

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

    \n
  • \n
\n

locales

\n
    \n
  • \n

    类型: { [path: string]: Partial<DefaultThemeLocaleData> }

    \n
  • \n
  • \n

    默认值: {}

    \n
  • \n
  • \n

    详情:

    \n

    多语言支持的各个语言 locales 。

    \n

    所有在 Locale 配置 章节内的配置项都可以在 locales 中使用。

    \n

    该配置项仅能在默认主题内生效,注意不要和 站点配置 中的 locales 混淆。

    \n
  • \n
  • \n

    参考:

    \n\n
  • \n
\n

Locale 配置

\n

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

\n

colorMode

\n\n

colorModeSwitch

\n\n

externalLinkIcon

\n
    \n
  • \n

    类型: boolean

    \n
  • \n
  • \n

    默认值: true

    \n
  • \n
  • \n

    详情:

    \n

    是否在外部链接上显示外部链接图标。

    \n
  • \n
\n

home

\n
    \n
  • \n

    类型: string

    \n
  • \n
  • \n

    默认值: /

    \n
  • \n
  • \n

    详情:

    \n

    首页的路径。

    \n

    它将被用于:

    \n
      \n
    • 导航栏中 Logo 的链接
    • \n
    • 404 页面的 返回首页 链接
    • \n
    \n
  • \n
\n

navbar

\n
    \n
  • \n

    类型: false | NavbarOptions

    \n
  • \n
  • \n

    默认值: []

    \n
  • \n
  • \n

    详情:

    \n

    导航栏配置。

    \n

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

    \n

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

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

    示例 1:

    \n
  • \n
\n
export default {\n  theme: defaultTheme({\n    navbar: [\n      // NavbarLink\n      {\n        text: 'Foo',\n        link: '/foo/',\n      },\n      // NavbarGroup\n      {\n        text: 'Group',\n        prefix: '/group/',\n        children: ['foo.md', 'bar.md'],\n      },\n      // 字符串 - 页面文件路径\n      '/bar/README.md',\n    ],\n  }),\n}
\n
    \n
  • 示例 2:
  • \n
\n
export default {\n  theme: defaultTheme({\n    navbar: [\n      // 嵌套 Group - 最大深度为 2\n      {\n        text: 'Group',\n        prefix: '/group/',\n        children: [\n          {\n            text: 'SubGroup1',\n            prefix: 'sub1/',\n            children: [\n              'foo.md', // 解析为 `/guide/group/sub1/bar.md`\n              'bar.md', // 解析为 `/guide/group/sub1/bar.md`\n\n              // 一个外部链接\n              {\n                text: 'Example',\n                link: 'https://example.com',\n              },\n            ],\n          },\n          {\n            text: 'SubGroup2',\n            prefix: 'sub2/',\n            // 项目内链接的 .md 或 .html 后缀是可以省略的\n            children: [\n              'foo', // 解析为 `/guide/group/sub2/foo.md`\n              'bar', // 解析为 `/guide/group/sub2/bar.md`\n\n              // 不在 SubGroup2 内的链接\n              '/baz/', // 解析为 `/baz/README.md`\n            ],\n          },\n        ],\n      },\n      // 控制元素何时被激活\n      {\n        text: 'Group 2',\n        children: [\n          {\n            text: 'Always active',\n            link: '/',\n            // 该元素将一直处于激活状态\n            activeMatch: '/',\n          },\n          {\n            text: 'Active on /foo/',\n            link: '/not-foo/',\n            // 该元素在当前路由路径是 /foo/ 开头时激活\n            // 支持正则表达式\n            activeMatch: '^/foo/',\n          },\n        ],\n      },\n    ],\n  }),\n}
\n

logo

\n
    \n
  • \n

    类型: null | string

    \n
  • \n
  • \n

    详情:

    \n

    Logo 图片的 URL。

    \n

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

    \n

    设置为 null 可以禁用 Logo 。

    \n
  • \n
  • \n

    示例:

    \n
  • \n
\n
export default {\n  theme: defaultTheme({\n    // Public 文件路径\n    logo: '/images/hero.png',\n    // URL\n    logo: 'https://vuejs.org/images/logo.png',\n  }),\n}
\n
\n

logoDark

\n
    \n
  • \n

    类型: null | string

    \n
  • \n
  • \n

    详情:

    \n

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

    \n

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

    \n

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

    \n
  • \n
  • \n

    参考:

    \n\n
  • \n
\n

logoAlt

\n
    \n
  • \n

    类型:null | string

    \n
  • \n
  • \n

    详情:

    \n

    指定 Logo 图片的替代文字。

    \n

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

    \n
  • \n
\n

repo

\n
    \n
  • \n

    类型: string

    \n
  • \n
  • \n

    详情:

    \n

    项目仓库的 URL。

    \n

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

    \n
  • \n
\n
export default {\n  theme: defaultTheme({\n    // 如果你按照 `organization/repository` 的格式设置它\n    // 我们会将它作为一个 GitHub 仓库\n    repo: 'vuejs/vuepress',\n    // 你也可以直接将它设置为一个 URL\n    repo: 'https://gitlab.com/foo/bar',\n  }),\n}
\n

sidebar

\n
    \n
  • \n

    类型: false | SidebarOptions

    \n
  • \n
  • \n

    默认值: 'heading'

    \n
  • \n
  • \n

    详情:

    \n

    侧边栏配置。

    \n

    你可以通过页面的 sidebar frontmatter 来覆盖这个全局配置。

    \n

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

    \n

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

    \n

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

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

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

    \n
      \n
    • Key 为路径前缀。
    • \n
    • Value 为 侧边栏数组\"heading\" 以自动为相应路径生成基于标题的侧边栏。
    • \n
    \n
  • \n
  • \n

    示例 1:

    \n
  • \n
\n
export default {\n  theme: defaultTheme({\n    // 侧边栏数组\n    // 所有页面会使用相同的侧边栏\n    sidebar: [\n      // SidebarItem\n      {\n        text: 'Foo',\n        prefix: '/foo/',\n        link: '/foo/',\n        children: [\n          // SidebarItem\n          {\n            text: 'github',\n            link: 'https://github.com',\n            children: [],\n          },\n          // 字符串 - 页面文件路径\n          'bar.md', // 解析为 `/foo/bar.md`\n          '/ray.md', // 解析为 `/ray.md`\n        ],\n      },\n      // 字符串 - 页面文件路径\n      '/bar/README.md',\n    ],\n  }),\n}
\n
    \n
  • 示例 2:
  • \n
\n
export default {\n  theme: defaultTheme({\n    // 侧边栏对象\n    // 不同子路径下的页面会使用不同的侧边栏\n    sidebar: {\n      '/guide/': [\n        {\n          text: 'Guide',\n          // 相对路径会自动追加子路径前缀\n          children: [\n            'introduction.md', // 解析为 `/guide/introduction.md`\n            'getting-started.md', // 解析为 `/guide/getting-started.md`\n          ],\n        },\n      ],\n      '/reference/': 'heading',\n    },\n  }),\n}
\n
    \n
  • 示例 3:
  • \n
\n
export default {\n  theme: defaultTheme({\n    // 可折叠的侧边栏\n    sidebar: {\n      '/reference/': [\n        {\n          text: 'VuePress Reference',\n          collapsible: true,\n          // 基于项目路径的 .md 或 .html 后缀是可以省略的\n          children: ['cli', 'config'],\n        },\n        {\n          text: 'Bundlers Reference',\n          collapsible: true,\n          // 前缀可以是相对路径,等同于 `prefix: /reference/bundler/`\n          prefix: 'bundler/',\n          children: ['vite', 'webpack'],\n        },\n      ],\n    },\n  }),\n}
\n

sidebarDepth

\n
    \n
  • \n

    类型: number

    \n
  • \n
  • \n

    默认值: 2

    \n
  • \n
  • \n

    详情:

    \n

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

    \n
      \n
    • 设为 0 来禁用所有级别的页面标题。
    • \n
    • 设为 1 来包含 <h2> 标题。
    • \n
    • 设为 2 来包含 <h2><h3> 标题。
    • \n
    • ...
    • \n
    \n

    你可以通过页面的 sidebarDepth frontmatter 来覆盖这个全局配置。

    \n
  • \n
\n

editLink

\n
    \n
  • \n

    类型: boolean

    \n
  • \n
  • \n

    默认值: true

    \n
  • \n
  • \n

    详情:

    \n

    是否启用 编辑此页 链接。

    \n

    你可以通过页面的 editLink frontmatter 来覆盖这个全局配置。

    \n
  • \n
\n

editLinkPattern

\n
    \n
  • \n

    类型: string

    \n
  • \n
  • \n

    详情:

    \n

    编辑此页 链接的 Pattern 。

    \n

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

    \n

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

    \n
  • \n
  • \n

    用法:

    \n

    | Pattern | 描述 |
    \n|

    \n
  • \n
\n", + "date_modified": "2025-01-10T18:07:54.000Z", + "authors": [], + "tags": [] + }, + { + "title": "继承", + "url": "https://ecosystem.vuejs.press/ecosystem/zh/themes/default/extending.html", + "id": "https://ecosystem.vuejs.press/ecosystem/zh/themes/default/extending.html", + "summary": "继承 VuePress 默认主题有着大量的用户,因此我们对它进行了一些便于继承的设计,以便用户轻松进行定制化。 布局插槽 默认主题的 Layout 布局提供了一些插槽: navbar navbar-before navbar-after sidebar sidebar-top sidebar-bottom page page-top page-bott...", + "content_html": "\n

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

\n

布局插槽

\n

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

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

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

\n

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

\n
import { defineClientConfig } from 'vuepress/client'\nimport Layout from './layouts/Layout.vue'\n\nexport default defineClientConfig({\n  layouts: {\n    Layout,\n  },\n})
\n

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

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

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

\n
\"extending-a-theme\"
extending-a-theme
\n

组件替换

\n

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

\n

默认主题将所有 非全局的组件 都注册了一个带 @theme 前缀的 alias 。例如,VPHomeFooter.vue 的别名是 @theme/VPHomeFooter.vue

\n

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

\n
import { defaultTheme } from '@vuepress/theme-default'\nimport { defineUserConfig } from 'vuepress'\nimport { getDirname, path } from 'vuepress/utils'\n\nconst __dirname = import.meta.dirname || getDirname(import.meta.url)\n\nexport default defineUserConfig({\n  theme: defaultTheme(),\n  alias: {\n    '@theme/VPHomeFooter.vue': path.resolve(\n      __dirname,\n      './components/MyHomeFooter.vue',\n    ),\n  },\n})
\n

修改行为

\n

默认主题的核心行为大多都被抽离成可组合式 API 或工具函数,并同样提供了 @theme 前缀的 alias

\n

比如,如果你想为默认主题的主题数据添加一些默认值,你可以通过覆盖 @theme/useThemeDatauseThemeData 函数来实现。

\n

开发一个子主题

\n

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

\n
import type { DefaultThemeOptions } from '@vuepress/theme-default'\nimport { defaultTheme } from '@vuepress/theme-default'\nimport type { Theme } from 'vuepress/core'\nimport { getDirname, path } from 'vuepress/utils'\n\nconst __dirname = import.meta.dirname || getDirname(import.meta.url)\n\nexport const childTheme = (options: DefaultThemeOptions): Theme => ({\n  name: 'vuepress-theme-child',\n  extends: defaultTheme(options),\n\n  // 在子主题的客户端配置文件中覆盖布局\n  // 注意,你在发布到 NPM 之前会将 TS 构建为 JS ,因此这里需要设置为 JS 文件的路径\n  clientConfigFile: path.resolve(__dirname, './client.js'),\n\n  // 覆盖组件别名\n  alias: {\n    '@theme/VPHomeFooter.vue': path.resolve(\n      __dirname,\n      './components/MyHomeFooter.vue',\n    ),\n  },\n})
\n
", + "image": "https://ecosystem.vuejs.press/ecosystem/images/cookbook/extending-a-theme-01.png", + "date_modified": "2025-01-10T18:07:54.000Z", + "authors": [], + "tags": [] + }, + { + "title": "Frontmatter", + "url": "https://ecosystem.vuejs.press/ecosystem/zh/themes/default/frontmatter.html", + "id": "https://ecosystem.vuejs.press/ecosystem/zh/themes/default/frontmatter.html", + "summary": "Frontmatter", + "content_html": "\n", + "date_modified": "2025-01-10T18:07:54.000Z", + "authors": [], + "tags": [] + }, + { + "title": "语言配置", + "url": "https://ecosystem.vuejs.press/ecosystem/zh/themes/default/locale.html", + "id": "https://ecosystem.vuejs.press/ecosystem/zh/themes/default/locale.html", + "summary": "语言配置 这些选项用于配置与语言相关的文本。 如果你的站点是以英语以外的其他语言提供服务的,你应该为每个语言设置这些选项来提供翻译。 repoLabel 类型: string 详情: 项目仓库的标签。 它将被用作 仓库链接 的文字。仓库链接 将会显示为导航栏的最后一个元素。 如果你不明确指定该配置项,它将会根据 配置项自动推断。 selectLangu...", + "content_html": "\n

这些选项用于配置与语言相关的文本。

\n

如果你的站点是以英语以外的其他语言提供服务的,你应该为每个语言设置这些选项来提供翻译。

\n

repoLabel

\n
    \n
  • \n

    类型: string

    \n
  • \n
  • \n

    详情:

    \n

    项目仓库的标签。

    \n

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

    \n

    如果你不明确指定该配置项,它将会根据 repo 配置项自动推断。

    \n
  • \n
\n

selectLanguageText

\n
    \n
  • \n

    类型: string

    \n
  • \n
  • \n

    详情:

    \n

    选择语言菜单 的文字。

    \n

    如果你在站点配置中设置了多个 locales ,那么 选择语言菜单 就会显示在导航栏中仓库按钮的旁边。

    \n
  • \n
\n

selectLanguageAriaLabel

\n
    \n
  • \n

    类型: string

    \n
  • \n
  • \n

    详情:

    \n

    选择语言菜单aria-label 属性。

    \n

    它主要是为了站点的可访问性 (a11y) 。

    \n
  • \n
\n

selectLanguageName

\n
    \n
  • \n

    类型: string

    \n
  • \n
  • \n

    详情:

    \n

    Locale 的语言名称。

    \n

    该配置项 仅能在主题配置的 locales 的内部生效 。它将被用作 locale 的语言名称,展示在 选择语言菜单 内。

    \n
  • \n
  • \n

    示例:

    \n
  • \n
\n
export default {\n  locales: {\n    '/': {\n      lang: 'en-US',\n    },\n    '/zh/': {\n      lang: 'zh-CN',\n    },\n  },\n  theme: defaultTheme({\n    locales: {\n      '/': {\n        selectLanguageName: 'English',\n      },\n      '/zh/': {\n        selectLanguageName: '简体中文',\n      },\n    },\n  }),\n}
\n

navbarLabel

\n
    \n
  • \n

    类型:null | string

    \n
  • \n
  • \n

    详情:

    \n

    导航栏中主导航 aria-label 属性的值。

    \n
  • \n
\n

pageNavbarLabel

\n
    \n
  • \n

    类型:null | string

    \n
  • \n
  • \n

    详情:

    \n

    下一页/上一页导航 aria-label 属性的值

    \n
  • \n
\n

editLinkText

\n
    \n
  • \n

    类型: string

    \n
  • \n
  • \n

    默认值: 'Edit this page'

    \n
  • \n
  • \n

    详情:

    \n

    编辑此页 链接的文字。

    \n
  • \n
\n

lastUpdatedText

\n
    \n
  • \n

    类型: string

    \n
  • \n
  • \n

    默认值: 'Last Updated'

    \n
  • \n
  • \n

    详情:

    \n

    最近更新时间戳 标签的文字。

    \n
  • \n
\n

contributorsText

\n
    \n
  • \n

    类型: string

    \n
  • \n
  • \n

    默认值: 'Contributors'

    \n
  • \n
  • \n

    详情:

    \n

    贡献者列表 标签的文字。

    \n
  • \n
\n

tip

\n
    \n
  • \n

    类型: string

    \n
  • \n
  • \n

    默认值: 'TIP'

    \n
  • \n
  • \n

    详情:

    \n

    Tip 自定义容器 的默认标题。

    \n
  • \n
\n

warning

\n
    \n
  • \n

    类型: string

    \n
  • \n
  • \n

    默认值: 'WARNING'

    \n
  • \n
  • \n

    详情:

    \n

    Warning 自定义容器 的默认标题。

    \n
  • \n
\n

danger

\n
    \n
  • \n

    类型: string

    \n
  • \n
  • \n

    默认值: 'DANGER'

    \n
  • \n
  • \n

    详情:

    \n

    Danger 自定义容器 的默认标题。

    \n
  • \n
\n

notFound

\n
    \n
  • \n

    类型: string[]

    \n
  • \n
  • \n

    默认值: ['Not Found']

    \n
  • \n
  • \n

    详情:

    \n

    404 页面的提示信息。

    \n

    当用户进入 404 页面时,会从数组中随机选取一条信息进行展示。

    \n
  • \n
\n

backToHome

\n
    \n
  • \n

    类型: string

    \n
  • \n
  • \n

    默认值: 'Back to home'

    \n
  • \n
  • \n

    详情:

    \n

    404 页面中 返回首页 链接的文字。

    \n
  • \n
\n

toggleColorMode

\n
    \n
  • \n

    类型: string

    \n
  • \n
  • \n

    默认值: 'toggle color mode'

    \n
  • \n
  • \n

    详情:

    \n

    切换颜色模式按钮的标题文字。

    \n

    它主要是为了站点的可访问性 (a11y) 。

    \n
  • \n
  • \n

    参考:

    \n\n
  • \n
\n

toggleSidebar

\n
    \n
  • \n

    类型: string

    \n
  • \n
  • \n

    默认值: 'toggle sidebar'

    \n
  • \n
  • \n

    详情:

    \n

    切换侧边栏按钮的标题文字。

    \n

    它主要是为了站点的可访问性 (a11y) 。

    \n
  • \n
\n

prev

\n
    \n
  • \n

    类型: string | false

    \n
  • \n
  • \n

    默认值: 'Prev'

    \n
  • \n
  • \n

    详情:

    \n

    上一页按钮的文字。设置为 false 时,将隐藏上一页按钮。

    \n
  • \n
\n

next

\n
    \n
  • \n

    类型: string | false

    \n
  • \n
  • \n

    默认值: 'Next'

    \n
  • \n
  • \n

    详情:

    \n

    下一页按钮的文字。设置为 false 时,将隐藏下一页按钮。

    \n
  • \n
\n", + "date_modified": "2025-01-10T18:07:54.000Z", + "authors": [], + "tags": [] + }, + { + "title": "Markdown", + "url": "https://ecosystem.vuejs.press/ecosystem/zh/themes/default/markdown.html", + "id": "https://ecosystem.vuejs.press/ecosystem/zh/themes/default/markdown.html", + "summary": "Markdown", + "content_html": "\n", + "date_modified": "2025-01-10T18:07:54.000Z", + "authors": [], + "tags": [] + }, + { + "title": "插件配置", + "url": "https://ecosystem.vuejs.press/ecosystem/zh/themes/default/plugin.html", + "id": "https://ecosystem.vuejs.press/ecosystem/zh/themes/default/plugin.html", + "summary": "插件配置 你可以通过 themePlugins 设置默认主题使用的插件。 默认主题使用了一些插件,如果你确实不需要该插件,你可以选择禁用它。在禁用插件之前,请确保你已了解它的用途。 themePlugins.activeHeaderLinks 类型: boolean 默认值: true 详情: 是否启用 。 themePlugins.backToTop...", + "content_html": "\n

你可以通过 themePlugins 设置默认主题使用的插件。

\n

默认主题使用了一些插件,如果你确实不需要该插件,你可以选择禁用它。在禁用插件之前,请确保你已了解它的用途。

\n
import { defaultTheme } from '@vuepress/theme-default'\n\nexport default {\n  theme: defaultTheme({\n    themePlugins: {\n      // 在这里自定义主题插件\n    },\n  }),\n}
\n

themePlugins.activeHeaderLinks

\n\n

themePlugins.backToTop

\n
    \n
  • \n

    类型: BackToTopPluginOptions | boolean

    \n
  • \n
  • \n

    默认值: true

    \n
  • \n
  • \n

    详情:

    \n

    是否启用 @vuepress/plugin-back-to-top

    \n

    支持对象格式以作为插件选项。

    \n
  • \n
\n

themePlugins.container

\n\n

themePlugins.copyCode

\n
    \n
  • \n

    类型: CopyCodePluginOptions | boolean

    \n
  • \n
  • \n

    默认值: true

    \n
  • \n
  • \n

    详情:

    \n

    是否启用 @vuepress/plugin-copy-code

    \n

    支持对象格式以作为插件选项。

    \n
  • \n
\n

themePlugins.git

\n
    \n
  • \n

    类型: boolean

    \n
  • \n
  • \n

    默认值: true

    \n
  • \n
  • \n

    详情:

    \n

    是否启用 @vuepress/plugin-git

    \n
  • \n
\n

themePlugins.hint

\n\n

themePlugins.links-check

\n\n

themePlugins.mediumZoom

\n\n

themePlugins.nprogress

\n\n

themePlugins.prismjs

\n\n

themePlugins.seo

\n
    \n
  • \n

    类型: SeoPluginOptions | boolean

    \n
  • \n
  • \n

    默认值: true

    \n
  • \n
  • \n

    详情:

    \n

    是否启用 @vuepress/plugin-seo

    \n

    支持对象格式以作为插件选项。

    \n
  • \n
\n

themePlugins.sitemap

\n
    \n
  • \n

    类型: SitemapPluginOptions | boolean

    \n
  • \n
  • \n

    默认值: true

    \n
  • \n
  • \n

    详情:

    \n

    是否启用 @vuepress/plugin-sitemap

    \n

    支持对象格式以作为插件选项。

    \n
  • \n
\n

themePlugins.tab

\n\n", + "date_modified": "2025-01-10T18:07:54.000Z", + "authors": [], + "tags": [] + }, + { + "title": "样式", + "url": "https://ecosystem.vuejs.press/ecosystem/zh/themes/default/styles.html", + "id": "https://ecosystem.vuejs.press/ecosystem/zh/themes/default/styles.html", + "summary": "样式", + "content_html": "\n", + "date_modified": "2025-01-10T18:07:54.000Z", + "authors": [], + "tags": [] + }, + { + "title": "@vuepress/helper", + "url": "https://ecosystem.vuejs.press/ecosystem/zh/tools/helper/", + "id": "https://ecosystem.vuejs.press/ecosystem/zh/tools/helper/", + "summary": "@vuepress/helper", + "content_html": "\n", + "date_modified": "2025-01-10T18:07:54.000Z", + "authors": [], + "tags": [] + }, + { + "title": "客户端相关", + "url": "https://ecosystem.vuejs.press/ecosystem/zh/tools/helper/client.html", + "id": "https://ecosystem.vuejs.press/ecosystem/zh/tools/helper/client.html", + "summary": "客户端相关 这些函数仅在 @vuepress/helper/client 中可用。 可组合 API hasGlobalComponent 检查组件是否已全局注册。 提示 组件的局部导入不影响结果。 当在 setup 之外调用时,你需要将 app 实例作为第二个参数传递。 示例 useLocaleConfig 从语言环境设置中获取当前语言环境配置。 示例...", + "content_html": "\n

这些函数仅在 @vuepress/helper/client 中可用。

\n

可组合 API

\n

hasGlobalComponent

\n

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

\n
\n

提示

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

useLocaleConfig

\n

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

\n
export const useLocaleConfig: <T extends LocaleData>(\n  localesConfig: RequiredLocaleConfig<T>,\n) => ComputedRef<T>
\n
示例\n
const localesCOnfig = {\n  '/': 'Title',\n  '/zh/': '标题',\n}\n\nconst locale = useLocaleConfig(localesConfig)\n\n// under `/page`\nlocale.value // 'Title'\n\n// under `/zh/page`\nlocale.value // '标题'
\n
\n

工具

\n

getHeaders

\n

获取当前页面指定的 标题列表。

\n
export const getHeaders: (options: GetHeadersOptions) => MenuItem[]
\n

参数:

\n
export interface GetHeadersOptions {\n  /**\n   * 页面标题选择器\n   *\n   * @default '[vp-content] h1, [vp-content] h2, [vp-content] h3, [vp-content] h4, [vp-content] h5, [vp-content] h6'\n   */\n  selector?: string\n  /**\n   * 忽略标题内的特定元素选择器\n   *\n   * 它将作为 `document.querySelectorAll` 的参数。\n   * 因此,你应该传入一个 `CSS 选择器` 字符串\n   *\n   * @default []\n   */\n  ignore?: string[]\n  /**\n   * 指定获取的标题层级\n   *\n   * `1` 至 `6` 表示 `<h1>` 至 `<h6>`\n   *\n   * - `false`: 不返回标题列表\n   * - `number`: 只获取指定的单个层级的标题。\n   * - `[number, number]: 标题层级元组,第一个数字应小于第二个数字。例如,`[2, 4]` 表示显示从 `<h2>` 到 `<h4>` 的所有标题。\n   * - `deep`: 等同于 `[2, 6]`, 表示获取从 `<h2>` 到 `<h6>` 的所有标题。\n   *\n   * @default 2\n   */\n  levels?: HeaderLevels\n}
\n

返回结果:

\n
export interface Header {\n  /**\n   * 当前标题的层级\n   *\n   * `1` 至 `6` 表示 `<h1>` 至 `<h6>`\n   */\n  level: number\n  /**\n   * 当前标题的内容\n   */\n  title: string\n  /**\n   * 标题的 标识\n   *\n   * 这通常是标题元素的 `id` 属性值\n   */\n  slug: string\n  /**\n   * 标题的链接\n   *\n   * 通常使用`#${slug}`作为锚点哈希\n   */\n  link: string\n  /**\n   * 标题的子标题列表\n   */\n  children: Header[]\n}\n\nexport type HeaderLevels = number | 'deep' | false | [number, number]\n\nexport type MenuItem = Omit<Header, 'children' | 'slug'> & {\n  element: HTMLHeadElement\n  children?: MenuItem[]\n}
\n
Examples\n
onMounted(() => {\n  const headers = getHeaders({\n    selector: '[vp-content] :where(h1,h2,h3,h4,h5,h6)',\n    levels: [2, 3], // 只有 h2 和 h3\n    ignore: ['.badge'], // 忽略标题内的 <Badge />\n  })\n  console.log(headers)\n})
\n
\n", + "date_modified": "2025-01-10T18:07:54.000Z", + "authors": [], + "tags": [] + }, + { + "title": "共享方法", + "url": "https://ecosystem.vuejs.press/ecosystem/zh/tools/helper/shared.html", + "id": "https://ecosystem.vuejs.press/ecosystem/zh/tools/helper/shared.html", + "summary": "共享方法 以下函数在 Node.js 和客户端上均可用。 这些函数在 @vuepress/helper @vuepress/helper/client 和 @vuepress/helper/shared 中都可用。 数据相关 此方法在 MarkdownIt 插件中很有用。有些时候你可能需要在 Markdown 插件中生成组件,并将复杂的数据写入到组件属...", + "content_html": "\n

以下函数在 Node.js 和客户端上均可用。

\n

这些函数在 @vuepress/helper @vuepress/helper/client@vuepress/helper/shared 中都可用。

\n

数据相关

\n

此方法在 MarkdownIt 插件中很有用。有些时候你可能需要在 Markdown 插件中生成组件,并将复杂的数据写入到组件属性中,一个通常做法是使用 JSON.stringify + encodeURIComponent,并在客户端 decodeURIComponent + JSON.parse。但如果内容包含很多特殊字符,转换结果会很长。

\n

所以我们提供 encodeDatadecodeData 来压缩和编码内容。

\n
export const encodeData: (\n  data: string,\n  level: DeflateOptions['level'] = 6,\n) => string\n\nexport const decodeData: (compressed: string) => string
\n
详情\n
const content = `\n{\n  \"type\": \"bar\",\n  \"data\": {\n    \"labels\": [\"Red\", \"Blue\", \"Yellow\", \"Green\", \"Purple\", \"Orange\"],\n    \"datasets\": [\n      {\n        \"label\": \"# of Votes\",\n        \"data\": [12, 19, 3, 5, 2, 3],\n        \"backgroundColor\": [\n          \"rgba(255, 99, 132, 0.2)\",\n          \"rgba(54, 162, 235, 0.2)\",\n          \"rgba(255, 206, 86, 0.2)\",\n          \"rgba(75, 192, 192, 0.2)\",\n          \"rgba(153, 102, 255, 0.2)\",\n          \"rgba(255, 159, 64, 0.2)\"\n        ],\n        \"borderColor\": [\n          \"rgba(255, 99, 132, 1)\",\n          \"rgba(54, 162, 235, 1)\",\n          \"rgba(255, 206, 86, 1)\",\n          \"rgba(75, 192, 192, 1)\",\n          \"rgba(153, 102, 255, 1)\",\n          \"rgba(255, 159, 64, 1)\"\n        ],\n        \"borderWidth\": 1\n      }\n    ]\n  },\n  \"options\": {\n    \"scales\": {\n      \"y\": {\n        \"beginAtZero\": true\n      }\n    }\n  }\n}\n`\n\nconst prop = encodeData(content) // \"eJyNUsFOwzAMve8rrHABKZqWlg5WxAE4cARxAMHEIV1NmQhNlaaCCe3fcdKtW0sLWGpjxy/v+UV512mlcIyfhTa2hHP4GgHYVYExsEQaxqlMpZWxbwAomaAqY5izO0wZB3apKnTrIyqlP1x2bRBzl9xWplC+eWNkniF7dmw1X4nWsfgaNtwNP2kfgH6Be22x9CPUUQ8yFwEHMeMQcog4UBFuiF0kcvGWGV3l6ZVW2uw0XDCTJfIwiOjYjAhESIcn4+BoT2MLio6pP6V+EBJ6AOSZgsmUwyl9A6ATwoiZn3lYTkTkRkycnuP8TU9ENPqUxuuA9i9BmxTNPy9A/G2/F9I23wtpW++FdIwPKzW2W5Afph+WqX2NQWz313XicT7XhV3qnB5f/ejKhVTYVACrXUqUmC3zC/uERsdgTYUdVr/Qb302+gZxe7S/\"\n\ndecodeData(prop) // will be the original content\n\n// if you use `encodeURIComponent`, it will be much longer\nencodeURIComponent(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'
\n
\n

类型助手

\n
    \n
  • isDef(x): 判断 x 是否定义。
  • \n
  • isBoolean(x): 判断 x 是否为布尔值。
  • \n
  • isString(x): 判断 x 是否为字符串。
  • \n
  • isNumber(x): 判断 x 是否为数字。
  • \n
  • isPlainObject(x): 判断值是否为纯对象。
  • \n
  • isArray(x): 判断 x 是否为数组
  • \n
  • isFunction(x): 判断 x 是否为函数。
  • \n
  • isRegExp(x): 判断 x 是否为正则表达式
  • \n
\n

字符串相关

\n
    \n
  • startsWith(a, b): 判断字符串 a 是否以指定字符串 b 开头
  • \n
  • endsWith(a, b): 判断字符串 a 是否以指定字符串 b 结尾
  • \n
\n

当 a 不是字符串时返回 false

\n

对象相关

\n
    \n
  • \n

    keys(x): 以数组形式返回对象 x 的键

    \n
  • \n
  • \n

    values(x): 以数组形式返回对象 x 的值

    \n
  • \n
  • \n

    entries(x): 将对象 x 转换为键值对数组。

    \n
  • \n
  • \n

    fromEntries(x): 将键值对数组 x 转换为对象。

    \n
  • \n
  • \n

    deepAssign(x, y, ...): Object.assign 的深度版本。

    \n
    示例\n
    // or @vuepress/helper/client\nimport { deepAssign } from '@vuepress/helper'\n\nconst defaultOptions = {\n  optionA: {\n    optionA1: 'defaultOptionA1',\n    optionA2: 'defaultOptionA2',\n    optionA3: 'defaultOptionA3',\n  },\n  optionB: true,\n  optionC: 'optionC',\n}\n\nconst userOptions = {\n  optionA: {\n    optionA1: 'optionA1',\n    optionA2: 'optionA2',\n  },\n  optionB: false,\n}\n\ndeepAssign(defaultOptions, userOptions)\n// {\n//   optionA: {\n//     optionA1: \"optionA1\",\n//     optionA2: \"optionA2\",\n//     optionA3: \"defaultOptionA3\",\n//   },\n//   optionB: false,\n//   optionC: \"optionC\",\n// }
    \n
    \n
  • \n
\n

日期相关

\n
    \n
  • \n

    getDate(x): 将输入 x 转换为日期,可以支持 Date,时间戳,日期字符串。日期字符串的支持度以环境的 Date.parse 支持度为准。当不能转换为日期时返回 null

    \n
    示例\n
    getDate('2021-01-01') // a Date object represents 2021-01-01\ngetDate(1609459200000) // a Date object represents 2021-01-01\ngetDate('2021-01-01T00:00:00.000Z') // a Date object represents 2021-01-01\ngetDate('2021/01/01') // a Date object represents 2021-01-01 (might be null in some browsers)\ngetDate('invalid date') // null\ngetDate(undefined) // null\ngetDate(-32) // null
    \n
    \n
  • \n
  • \n

    dateSorter: 将可转换为日期的值从新到旧排序,不能转换为日期的值会在最后。

    \n
    示例\n
    const arr = [\n  '2020-01-01',\n  1609459200000,\n  '2022-01-01T00:00:00.000Z',\n  '2023/01/01',\n  'invalid date',\n  undefined,\n  -32,\n]\n\narr.sort(dateSorter)\n// [\n//   '2023/01/01',\n//   '2022-01-01T00:00:00.000Z',\n//   1609459200000,\n//   '2020-01-01',\n//   'invalid date',\n//   undefined,\n//   -32,\n// ]
    \n
    \n
  • \n
\n

链接相关

\n
    \n
  • isLinkHttp(x): x 是否是有效的 HTTP URL。
  • \n
  • isLinkWithProtocol(x): x 是否是有效的带有协议的 URL。
  • \n
  • isLinkExternal(x): x 是否是有效的外部 URL。
  • \n
  • isLinkAbsolute(x): x 是否是有效的绝对 URL。
  • \n
  • isLinkRelative(x): x 是否不是外部 URL 或绝对 URL。
  • \n
  • ensureEndingSlash(x): 确保 x 以斜杠结尾。
  • \n
  • ensureLeadingSlash(x): 确保 x 以斜杠开头。
  • \n
  • removeEndingSlash(x): 确保 x 不以斜杠结尾。
  • \n
  • removeLeadingSlash(x): 确保 x 不以斜杠开头。
  • \n
\n", + "date_modified": "2025-01-10T18:07:54.000Z", + "authors": [], + "tags": [] + }, + { + "title": "样式", + "url": "https://ecosystem.vuejs.press/ecosystem/zh/tools/helper/style.html", + "id": "https://ecosystem.vuejs.press/ecosystem/zh/tools/helper/style.html", + "summary": "样式 提供了如下样式文件。 规范化 @vuepress/helper/normalize.css 是一个 CSS 文件,用于规范化浏览器的默认样式。推荐在社区主题中引入它。", + "content_html": "\n

提供了如下样式文件。

\n

规范化

\n

@vuepress/helper/normalize.css 是一个 CSS 文件,用于规范化浏览器的默认样式。推荐在社区主题中引入它。

\n", + "date_modified": "2025-01-10T18:07:54.000Z", + "authors": [], + "tags": [] + }, + { + "title": "blog", + "url": "https://ecosystem.vuejs.press/ecosystem/zh/plugins/blog/blog/", + "id": "https://ecosystem.vuejs.press/ecosystem/zh/plugins/blog/blog/", + "summary": "blog", + "content_html": "\n", + "date_modified": "2025-01-10T18:07:54.000Z", + "authors": [], + "tags": [] + }, + { + "title": "配置", + "url": "https://ecosystem.vuejs.press/ecosystem/zh/plugins/blog/blog/config.html", + "id": "https://ecosystem.vuejs.press/ecosystem/zh/plugins/blog/blog/config.html", + "summary": "配置 插件选项 getInfo 类型: (page: Page) => Record 必填: 否 参考: 详情: 获取文章信息的函数。 获取到的信息会被稍后注入至路由元数据,以便你可以在客户端中通过组合式 API 获取。 filter 类型: (page: Page) => boolean 默认值: (page) =>...", + "content_html": "\n

插件选项

\n

getInfo

\n
    \n
  • \n

    类型: (page: Page) => Record<string, unknown>

    \n
  • \n
  • \n

    必填: 否

    \n
  • \n
  • \n

    参考:

    \n\n
  • \n
  • \n

    详情:

    \n

    获取文章信息的函数。

    \n

    获取到的信息会被稍后注入至路由元数据,以便你可以在客户端中通过组合式 API 获取。

    \n
  • \n
\n

filter

\n
    \n
  • \n

    类型: (page: Page) => boolean

    \n
  • \n
  • \n

    默认值: (page) => Boolean(page.filePathRelative) && !page.frontmatter.home

    \n
  • \n
  • \n

    参考:

    \n\n
  • \n
  • \n

    详情:

    \n

    页面过滤器,此函数用于鉴别页面是否作为文章。

    \n

    默认情况下,所有从 Markdown 源文件中生成的非主页页面,会被作为文章。

    \n
  • \n
\n

category

\n\n

type

\n\n

slugify

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

excerpt

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

excerptSeparator

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

excerptLength

\n
    \n
  • \n

    类型: number

    \n
  • \n
  • \n

    默认值: 300

    \n
  • \n
  • \n

    参考:

    \n\n
  • \n
  • \n

    详情:

    \n

    自动生成的摘要的长度。

    \n
    \n

    提示

    \n

    摘要的长度会尽可能的接近这个值。如果设置为 0,意味着不自动生成摘要。

    \n
    \n
  • \n
\n

excerptFilter

\n
    \n
  • \n

    类型: (page: Page) => boolean

    \n
  • \n
  • \n

    默认值: filter 选项

    \n
  • \n
  • \n

    参考:

    \n\n
  • \n
  • \n

    详情:
    \n页面过滤器,此函数用于鉴别插件是否需要生成摘要。

    \n
    \n

    提示

    \n

    你可以使用此函数来跳过你不需要生成摘要的页面。例如:如果用户在 frontmatter 中设置了 excerptdescription,你可能希望直接使用它们。

    \n
    \n
  • \n
\n

isCustomElement

\n
    \n
  • \n

    类型: (tagName: string) => boolean

    \n
  • \n
  • \n

    默认值: () => false

    \n
  • \n
  • \n

    参考:

    \n\n
  • \n
  • \n

    详情:
    \n被认为是自定义元素的标签。

    \n

    用于判断一个标签是否是自定义元素,因为在摘要中,所有的未知标签都会被移除。

    \n
  • \n
\n

metaScope

\n
    \n
  • \n

    类型: string

    \n
  • \n
  • \n

    默认值: \"_blog\"

    \n
  • \n
  • \n

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

    \n
    \n

    提示

    \n

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

    \n
    \n
  • \n
\n

hotReload

\n
    \n
  • \n

    类型: boolean

    \n
  • \n
  • \n

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

    \n
  • \n
  • \n

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

    \n
    \n

    致主题开发者

    \n

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

    \n

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

    \n

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

    \n
    \n
  • \n
\n

博客分类配置

\n

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

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

博客类型配置

\n

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

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

可组合式 API

\n

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

\n
    \n
  • \n

    博客分类

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

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

    \n
  • \n
  • \n

    博客类型

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

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

    \n
  • \n
\n

详细的返回值如下:

\n
interface Article<T extends Record<string, unknown> = Record<string, unknown>> {\n  /** 文章路径 */\n  path: string\n  /** 文章信息 */\n  info: T\n}\n\ninterface BlogCategoryData<\n  T extends Record<string, unknown> = Record<string, unknown>,\n> {\n  /** 分类路径 */\n  path: string\n\n  /**\n   * 仅当当前路径和某个子项目匹配时可用\n   */\n  currentItems?: Article<T>[]\n\n  /** 分类映射 */\n  map: {\n    /** 当前分类下全局唯一的 key */\n    [key: string]: {\n      /** 对应键值的分类路径 */\n      path: string\n      /** 对应键值的项目 */\n      items: Article<T>[]\n    }\n  }\n}\n\ninterface BlogTypeData<\n  T extends Record<string, unknown> = Record<string, unknown>,\n> {\n  /** 类别路径 */\n  path: string\n\n  /** 当前类别下的项目 */\n  items: Article<T>[]\n}
\n
", + "date_modified": "2025-01-10T18:07:54.000Z", + "authors": [], + "tags": [] + }, + { + "title": "指南", + "url": "https://ecosystem.vuejs.press/ecosystem/zh/plugins/blog/blog/guide.html", + "id": "https://ecosystem.vuejs.press/ecosystem/zh/plugins/blog/blog/guide.html", + "summary": "指南 使用 @vuepress/plugin-blog,你可以轻松地将博客功能引入主题。 收集文章并生成信息 起步时,插件会首选过滤并选择那些需要作为文章的页面。这将剔除你不想要的页面,并在后续处理中排除它们。 默认情况下,所有从 Markdown 文件生成但不是主页的页面,都将被视作文章。 你可能需要设置 filter 选项来完全自定义要收集的页面。...", + "content_html": "\n

使用 @vuepress/plugin-blog,你可以轻松地将博客功能引入主题。

\n

收集文章并生成信息

\n

起步时,插件会首选过滤并选择那些需要作为文章的页面。这将剔除你不想要的页面,并在后续处理中排除它们。

\n
\n

默认情况下,所有从 Markdown 文件生成但不是主页的页面,都将被视作文章。

\n
\n

你可能需要设置 filter 选项来完全自定义要收集的页面。 filter 接受一个形状为 (page: Page) => boolean 的函数。

\n

接着,你应该设置 getInfo 选项为一个接受 Page 作为参数并返回包含所需信息的对象的函数。这样稍后,你可以从组合 API 中获取这些信息。

\n
案例\n
import { blogPlugin } from '@vuepress/plugin-blog'\n\nexport default {\n  name: 'vuepress-theme-xxx',\n  plugins: [\n    blogPlugin({\n      filter: ({ filePathRelative, frontmatter }) => {\n        // 舍弃那些不是从 Markdown 文件生成的页面\n        if (!filePathRelative) return false\n\n        // 舍弃 `archives` 文件夹的页面\n        if (filePathRelative.startsWith('archives/')) return false\n\n        // 舍弃那些没有使用默认布局的页面\n        if (frontmatter.home || frontmatter.layout) return false\n\n        return true\n      },\n\n      getInfo: ({ frontmatter, git = {}, data = {} }) => {\n        // 获取页面信息\n        const info: Record<string, unknown> = {\n          author: frontmatter.author || '',\n          categories: frontmatter.categories || [],\n          date: frontmatter.date || git.createdTime || null,\n          tags: frontmatter.tags || [],\n          excerpt: data.excerpt || '',\n        }\n\n        return info\n      },\n    }),\n    // 其他插件 ...\n  ],\n}
\n
\n

自定义类别和类型

\n

基本上,你的博客中需要两种“类型”:

\n
    \n
  • \n

    类别:

    \n

    “类别”是用文章的标签 (或类别) 对它们进行分组。

    \n

    例如,每篇文章可能都有对应的“分类”和“标签”。

    \n
  • \n
  • \n

    类型:

    \n

    “类型”是过滤不同条件的文章。

    \n

    例如,你的帖子中可能有日记或笔记。当帖子带有写作日期信息时,它可以称为“时间线项目”。

    \n
  • \n
\n

了解这两种类型的描述后,你可以设置 categorytype 选项,它们都接受一个数组,每个元素代表一个配置。

\n

让我们从此处 2 个例子开始。

\n

假设你想为每篇文章设置标签,并且你正在通过 frontmatter.tag 设置它们。同时,你想要在 /tag/ 中使用 TagMap 布局的标签页面,并在/tag/标签名称 中使用 TagList 布局对标签按名称进行分组,你可能需要这样的配置:

\n
import { blogPlugin } from '@vuepress/plugin-blog'\n\nexport default {\n  name: 'vuepress-theme-xxx',\n  plugins: [\n    blogPlugin({\n      // 其他配置 ...\n      category: [\n        {\n          key: 'tag',\n          getter: ({ frontmatter }) => frontmatter.tag || [],\n          path: '/tag/',\n          layout: 'TagMap',\n          frontmatter: () => ({ title: '标签页' }),\n          itemPath: '/tag/:name/',\n          itemLayout: 'TagList',\n          itemFrontmatter: (name) => ({ title: `${name}标签` }),\n        },\n      ],\n    }),\n    // 其他插件 ...\n  ],\n}
\n

此外,你可能希望为你的一些文章加注星标,并将其展示给访问者。当你在 frontmatter 中设置 star: true 来标记它们时,你可能需要这样的配置来在 /star/ 路径中以 StarList 布局显示它们:

\n
import { blogPlugin } from '@vuepress/plugin-blog'\n\nexport default {\n  name: 'vuepress-theme-xxx',\n  plugins: [\n    blogPlugin({\n      // 其他配置 ...\n      type: [\n        {\n          key: 'star',\n          filter: ({ frontmatter }) => frontmatter.star,\n          path: '/star/',\n          layout: 'StarList',\n          frontmatter: () => ({ title: '星标文章' }),\n        },\n      ],\n    }),\n    // 其他插件 ...\n  ],\n}
\n

看,设置这两种类型很容易。有关完整选项,请参阅 博客分类配置博客分类配置

\n

在客户端使用组合 API

\n

当生成每个页面时,插件将在 frontmatter.blog 中设置如下信息

\n
interface BlogFrontmatterOptions {\n  /** 当前页面的类型 */\n  type: 'category' | 'type'\n  /** 在当前分类或类别下全局唯一的 key */\n  key: string\n  /**\n   * 当前的分类名称\n   *\n   * @description 仅在分类子项目页面中可用\n   */\n  name?: string\n}
\n

所以你可以直接调用 useBlogCategory()useBlogType(),结果将是当前路由绑定的类别或类型。

\n

此外,你可以通过传递所需的 key 作为参数,来将获得绑定到该 key 的信息。

\n

对于上方的 Node 配置而言,你可以在客户端通过如下方式获取 tag 和 star 的信息:

\n

TagMap 布局:

\n
<script setup lang=\"ts\">\nimport { useBlogCategory } from '@vuepress/plugin-blog'\nimport { RouteLink } from 'vuepress/client'\n\nconst categoryMap = useBlogCategory('tag')\n</script>\n<template>\n  <div>\n    <h1>Tag page</h1>\n    <ul>\n      <li v-for=\"({ items, path }, name) in categoryMap.map\" :key=\"path\">\n        <RouteLink :key=\"name\" :to=\"path\" class=\"category\">\n          {{ name }}\n          <span class=\"category-num\">\n            {{ items.length }}\n          </span>\n        </RouteLink>\n      </li>\n    </ul>\n  </div>\n</template>
\n

TagList 布局:

\n
<script setup lang=\"ts\">\nimport { useBlogCategory } from '@vuepress/plugin-blog'\nimport { RouteLink } from 'vuepress/client'\n\nconst categoryMap = useBlogCategory('tag')\n</script>\n<template>\n  <div>\n    <h1>Tag page</h1>\n    <div class=\"category-wrapper\">\n      <RouteLink\n        v-for=\"({ items, path }, name) in categoryMap.map\"\n        :key=\"name\"\n        :to=\"path\"\n        class=\"category\"\n      >\n        {{ name }}\n        <span class=\"category-num\">\n          {{ items.length }}\n        </span>\n      </RouteLink>\n    </div>\n    <div v-if=\"categoryMap.currentItems\" class=\"article-wrapper\">\n      <div v-if=\"!categoryMap.currentItems.length\">Nothing in here.</div>\n      <article\n        v-for=\"{ info, path } in categoryMap.currentItems\"\n        :key=\"path\"\n        class=\"article\"\n        @click=\"$router.push(path)\"\n      >\n        <header class=\"title\">\n          {{ info.title }}\n        </header>\n        <hr />\n        <div class=\"article-info\">\n          <span v-if=\"info.author\" class=\"author\"\n            >Author: {{ info.author }}</span\n          >\n          <span v-if=\"info.date\" class=\"date\"\n            >Date: {{ new Date(info.date).toLocaleDateString() }}</span\n          >\n          <span v-if=\"info.category\" class=\"category\"\n            >Category: {{ info.category.join(', ') }}</span\n          >\n          <span v-if=\"info.tag\" class=\"tag\"\n            >Tag: {{ info.tag.join(', ') }}</span\n          >\n        </div>\n        <div v-if=\"info.excerpt\" class=\"excerpt\" v-html=\"info.excerpt\" />\n      </article>\n    </div>\n  </div>\n</template>
\n

StarList 布局:

\n
<script setup lang=\"ts\">\nimport { useBlogType } from '@vuepress/plugin-blog/client'\n\nimport ParentLayout from '@vuepress/theme-default/layouts/Layout.vue'\nimport ArticleList from '../components/ArticleList.vue'\n\nconst stars = useBlogType('star')\n</script>\n<template>\n  <div v-if=\"stars.items?.length\" class=\"article-wrapper\">\n    <article\n      v-for=\"{ info, path } in stars.items\"\n      :key=\"path\"\n      class=\"article\"\n      @click=\"$router.push(path)\"\n    >\n      <header class=\"title\">\n        {{ info.title }}\n      </header>\n      <hr />\n      <div class=\"article-info\">\n        <span v-if=\"info.author\" class=\"author\">Author: {{ info.author }}</span>\n        <span v-if=\"info.date\" class=\"date\"\n          >Date: {{ new Date(info.date).toLocaleDateString() }}</span\n        >\n        <span v-if=\"info.category\" class=\"category\"\n          >Category: {{ info.category.join(', ') }}</span\n        >\n        <span v-if=\"info.tag\" class=\"tag\">Tag: {{ info.tag.join(', ') }}</span>\n      </div>\n      <div v-if=\"info.excerpt\" class=\"excerpt\" v-html=\"info.excerpt\" />\n    </article>\n  </div>\n  <div v-else>Nothing in here.</div>\n</template>
\n

有关返回类型,请参阅 Composition API 返回类型

\n

多语言支持

\n

该插件添加了原生多语言支持,因此你的设置将自动应用于每种语言。

\n

例如,如果用户进行了以下 locales 配置,并且你正在设置上面的“star”示例:

\n
export default {\n  locales: {\n    '/': {\n      lang: 'en-US',\n    },\n    '/zh/': {\n      lang: 'zh-CN',\n    },\n  },\n}
\n

那么 /zh/star//star/ 都将可用,并且只会显示对应语言下的文章。

\n

摘要生成

\n

这个插件提供了一个内置的摘要生成器,可以通过将 excerpt 选项设置为 true 来启用。

\n
\n

摘要介绍

\n

摘要是一个 HTML 片段,被用于在博客列表中显示文章的简短描述,所以摘要有如下限制:

\n
    \n
  • 摘要不支持任何未知标签以及 Vue 语法,所以此类内容会在生成时被移除。如果你有自定义组件 (非 Vue 组件),请配置 isCustomElement 选项。
  • \n
  • 由于摘要是一个 HTML 片段,所以你将无法通过相对路径或别名引入任何图片,这些图片会被直接移除。如果你想要保留图片,请使用基于 .vuepress/public 的绝对路径或完整路径以确保它们可以在其他地址被访问。
  • \n
\n
\n

摘要生成器将尝试从 Frontmatter 内容中找到有效的摘要分隔符,如果找到,它将使用分隔符之前的内容,分隔符默认为 <!-- more -->,并且你可以通过 excerptSeparator 选项来自定义它。

\n

如果找不到有效的分隔符,它将从 Markdown 文件的开头开始解析内容,直到长度达到预设值时停止。该值默认为 300,你可以通过设置 excerptLength 选项来自定义它。

\n

要选择哪个页面应该生成摘要,你可以使用 excerptFilter 选项。

\n
\n

示例

\n

通常,如果用户设置了 frontmatter.description,你可能希望使用它们,因此如果 frontmatter.description 不为空,你可以让过滤器函数返回 false

\n
\n", + "date_modified": "2025-01-10T18:07:54.000Z", + "authors": [], + "tags": [] + }, + { + "title": "comment", + "url": "https://ecosystem.vuejs.press/ecosystem/zh/plugins/blog/comment/", + "id": "https://ecosystem.vuejs.press/ecosystem/zh/plugins/blog/comment/", + "summary": "comment", + "content_html": "\n", + "date_modified": "2024-05-29T05:40:07.000Z", + "authors": [], + "tags": [] + }, + { + "title": "指南", + "url": "https://ecosystem.vuejs.press/ecosystem/zh/plugins/blog/comment/guide.html", + "id": "https://ecosystem.vuejs.press/ecosystem/zh/plugins/blog/comment/guide.html", + "summary": "指南 设置选项 你既可以在 Node.js 一侧使用插件选项设置选项,也可以通过客户端配置文件在浏览器一侧设置选项。 通过插件选项 通过客户端配置文件 有以下你需要注意的限制: provider、多语言设置和其他资源相关选项必须在插件选项中设置。 为确保 tree-shaking 有效,我们必须在 Node 一侧优化入口,以便打包器可以了解最终打包中应...", + "content_html": "\n

设置选项

\n

你既可以在 Node.js 一侧使用插件选项设置选项,也可以通过客户端配置文件在浏览器一侧设置选项。

\n

通过插件选项

\n
import { commentPlugin } from '@vuepress/plugin-comment'\n\n// .vuepress/config.ts\nexport default {\n  plugins: [\n    commentPlugin({\n      provider: 'Artalk', // Artalk | Giscus | Waline | Twikoo\n\n      // 在这里放置其他选项\n      // ...\n    }),\n  ],\n}
\n

通过客户端配置文件

\n
import {\n  defineArtalkConfig,\n  // defineGiscusConfig,\n  // defineTwikooConfig,\n  // defineWalineConfig,\n} from '@vuepress/plugin-comment/client'\nimport { defineClientConfig } from 'vuepress/client'\n\ndefineArtalkConfig({\n  // 选项\n})
\n

有以下你需要注意的限制:

\n
    \n
  • \n

    provider、多语言设置和其他资源相关选项必须在插件选项中设置。

    \n

    为确保 tree-shaking 有效,我们必须在 Node 一侧优化入口,以便打包器可以了解最终打包中应包含哪些资源。

    \n

    这些选项将在配置参考中用

    \n
  • \n
  • \n

    不能序列化为 JSON 的选项必须在客户端配置中设置。

    \n

    接收复杂值的选项(例如:函数)不能在插件选项中设置,因为插件运行在 Node.js 环境下,所以我们无法将这些值和它们的上下文传递给浏览器。

    \n

    这些选项将在配置参考中用

    \n
  • \n
\n

添加评论

\n

该插件全局注册了一个组件 <CommentService />

\n
    \n
  • 如果你是用户,你应该使用 alias 和布局槽来插入组件。 我们建议你在 <PageNav /> 组件之后插入评论组件 (<CommentService />),本页可作为一个 Demo 作为参考。
  • \n
  • 如果你是主题开发者,你应该将这个组件插入到你的主题布局中。
  • \n
\n

默认情况下,<CommentService /> 组件是全局启用的,你可以在插件选项和页面 frontmatter 中使用 comment 选项来控制它。

\n
    \n
  • 你可以通过在页面 frontmatter 中设置 comment: false 在本地禁用它。
  • \n
  • 要使其全局禁用,请在插件选项中将 comment 设置为 false。 然后你可以在页面 frontmatter 中设置 comment: true 以在局部启用它。
  • \n
\n

你可以在页面 frontmatter 中设置 commentID 选项来自定义评论 ID,该 ID 用于标识要用于页面的评论存储项。默认情况下,它将是页面的 path ,这意味着如果你将站点部署到多个位置,站点间具有相同内容的页面将共享相同的评论数据。

\n

可用的评论服务

\n

目前你可以选择 GiscusWalineArtalkTwikoo

\n
\n

推荐的评论服务

\n
    \n
  • 面向程序员和开发人员: Giscus
  • \n
  • 面向公众: Waline
  • \n
\n
\n

通用选项

\n

provider

\n
    \n
  • 类型: \"Artalk\" | \"Giscus\" | \"Twikoo\" | \"Waline\" | \"None\"
  • \n
  • 默认值: \"None\"
  • \n
  • 详情:评论服务提供者。
  • \n
\n

comment

\n
    \n
  • 类型: boolean
  • \n
  • 默认值: true
  • \n
  • 详情:是否默认启用评论功能。
  • \n
\n", + "date_modified": "2025-01-10T18:07:54.000Z", + "authors": [], + "tags": [] + }, + { + "title": "feed", + "url": "https://ecosystem.vuejs.press/ecosystem/zh/plugins/blog/feed/", + "id": "https://ecosystem.vuejs.press/ecosystem/zh/plugins/blog/feed/", + "summary": "feed", + "content_html": "\n", + "date_modified": "2025-01-10T18:07:54.000Z", + "authors": [], + "tags": [] + }, + { + "title": "频道设置", + "url": "https://ecosystem.vuejs.press/ecosystem/zh/plugins/blog/feed/channel.html", + "id": "https://ecosystem.vuejs.press/ecosystem/zh/plugins/blog/feed/channel.html", + "summary": "频道设置 channel 插件选项用于配置 feed 的频道。 channel.title 类型:string 默认值:SiteConfig.title 频道的标题 channel.link 类型:string 默认值:部署的网址 (通过 options.hostname 和 context.base 生成) 频道地址 channel.descript...", + "content_html": "\n

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

\n

channel.title

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

频道的标题

\n

channel.link

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

频道地址

\n

channel.description

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

频道描述信息

\n

channel.language

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

频道使用的语言

\n

channel.copyright

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

频道版权信息

\n

channel.pubDate

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

频道内容的发布时间

\n

channel.lastUpdated

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

频道内容的上次更新时间

\n

channel.ttl

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

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

\n

channel.image

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

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

\n

channel.icon

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

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

\n

channel.author

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

频道的作者。

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

channel.hub

\n
    \n
  • 类型:string
  • \n
\n

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

\n
\n

WebSub

\n

有关信息,详见 Websub

\n
\n", + "date_modified": "2025-01-10T18:07:54.000Z", + "authors": [], + "tags": [] + }, + { + "title": "插件配置", + "url": "https://ecosystem.vuejs.press/ecosystem/zh/plugins/blog/feed/config.html", + "id": "https://ecosystem.vuejs.press/ecosystem/zh/plugins/blog/feed/config.html", + "summary": "插件配置 hostname 类型:string 必填:是 部署网站的域名。 atom 类型:boolean 默认值:false 是否启用 Atom 格式输出。 json 类型:boolean 默认值:false 是否启用 JSON 格式输出。 rss 类型:boolean 默认值:false 是否启用 RSS 格式输出。 image 类型:string...", + "content_html": "\n

hostname

\n
    \n
  • 类型:string
  • \n
  • 必填:是
  • \n
\n

部署网站的域名。

\n

atom

\n
    \n
  • 类型:boolean
  • \n
  • 默认值:false
  • \n
\n

是否启用 Atom 格式输出。

\n

json

\n
    \n
  • 类型:boolean
  • \n
  • 默认值:false
  • \n
\n

是否启用 JSON 格式输出。

\n

rss

\n
    \n
  • 类型:boolean
  • \n
  • 默认值:false
  • \n
\n

是否启用 RSS 格式输出。

\n

image

\n
    \n
  • 类型:string
  • \n
\n

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

\n

icon

\n
    \n
  • 类型:string
  • \n
\n

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

\n

count

\n
    \n
  • 类型:number
  • \n
  • 默认值:100
  • \n
\n

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

\n

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

\n

preservedElements

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

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

\n
\n

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

\n
\n

filter

\n
    \n
  • \n

    类型:(page: Page)=> boolean

    \n
  • \n
  • \n

    默认值:

    \n
    ;({ frontmatter, filePathRelative }) =>\n  Boolean(frontmatter.feed ?? (filePathRelative && !frontmatter.home))
    \n
  • \n
\n

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

\n

sorter

\n
    \n
  • \n

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

    \n
  • \n
  • \n

    默认值:

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

Feed 项目的排序器。

\n

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

\n
\n

提示

\n

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

\n
\n

channel

\n

channel 选项用于配置 Feed 频道。

\n

可用选项详见 配置 → 频道设置

\n

devServer

\n
    \n
  • 类型:boolean
  • \n
  • 默认值:false
  • \n
\n

是否在开发服务器中启用

\n
\n

提示

\n

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

\n
\n

devHostname

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

开发服务器使用的主机名

\n

atomOutputFilename

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

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

\n

atomXslTemplate

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

Atom xsl 模板文件没人陪美国

\n

atomXslFilename

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

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

\n

jsonOutputFilename

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

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

\n

rssOutputFilename

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

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

\n

rssXslTemplate

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

RSS xsl 模板文件内容。

\n

rssXslFilename

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

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

\n

getter

\n

Feed 生成控制器,详见 Feed 生成器

\n
\n

此插件内置了生成器,只有当你想完全控制 feed 生成时才需要设置此选项。

\n
\n

locales

\n
    \n
  • 类型:Record<string, BaseFeedOptions>
  • \n
\n

你可以将它用于每个语言环境的特定选项。

\n

hostname 外,上述任何选项均受支持。

\n", + "date_modified": "2025-01-10T18:07:54.000Z", + "authors": [], + "tags": [] + }, + { + "title": "Frontmatter 配置", + "url": "https://ecosystem.vuejs.press/ecosystem/zh/plugins/blog/feed/frontmatter.html", + "id": "https://ecosystem.vuejs.press/ecosystem/zh/plugins/blog/feed/frontmatter.html", + "summary": "Frontmatter 配置 你可以通过配置每个页面的 Frontmatter,来对每个 Feed 项目生成进行单独的控制。 添加与移除 默认情况下,所有文章均会被添加至 feed 流。如果你想在 feed 中移除特定页面,你可以在 frontmatter 中设置 feed: false。 读取的 Frontmatter 信息 title 类型:str...", + "content_html": "\n

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

\n

添加与移除

\n

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

\n

读取的 Frontmatter 信息

\n

title

\n
    \n
  • 类型:string
  • \n
\n

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

\n

description

\n
    \n
  • 类型:string
  • \n
\n

页面描述

\n

date

\n
    \n
  • 类型:Date
  • \n
\n

页面的发布日期

\n

article

\n
    \n
  • 类型:boolean
  • \n
\n

该页面是否是文章

\n
\n

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

\n
\n

copyright

\n
    \n
  • 类型:string
  • \n
\n

页面版权信息

\n

cover / image / banner

\n
    \n
  • 类型:string
  • \n
\n

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

\n

Frontmatter 选项

\n

feed.title

\n
    \n
  • 类型:string
  • \n
\n

Feed 项目的标题

\n

feed.description

\n
    \n
  • 类型:string
  • \n
\n

Feed 项目的描述

\n

feed.content

\n
    \n
  • 类型:string
  • \n
\n

Feed 项目的内容

\n

feed.author

\n
    \n
  • 类型:FeedAuthor[] | FeedAuthor
  • \n
\n

Feed 项目的作者

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

feed.contributor

\n
    \n
  • 类型:FeedContributor[] | FeedContributor
  • \n
\n

Feed 项目的贡献者

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

feed.guid

\n
    \n
  • 类型:string
  • \n
\n

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

\n
\n

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

\n
\n", + "date_modified": "2025-01-10T18:07:54.000Z", + "authors": [], + "tags": [] + }, + { + "title": "Feed 获取器", + "url": "https://ecosystem.vuejs.press/ecosystem/zh/plugins/blog/feed/getter.html", + "id": "https://ecosystem.vuejs.press/ecosystem/zh/plugins/blog/feed/getter.html", + "summary": "Feed 获取器 你可以通过控制插件选项中的 getter 来完全控制 Feed 项目的生成。 getter.title 类型:(page: Page, app: App) => string 项目标题获取器 getter.link 类型:(page: Page, app: App) => string 项目链接获取器 getter.descripti...", + "content_html": "\n

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

\n

getter.title

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

项目标题获取器

\n

getter.link

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

项目链接获取器

\n

getter.description

\n
    \n
  • 类型:(page: Page, app: App) => string | undefined
  • \n
\n

项目描述获取器

\n
\n

提示

\n

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

\n
\n

getter.content

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

项目内容获取器

\n

getter.author

\n
    \n
  • 类型:(page: Page, app: App) => FeedAuthor[]
  • \n
\n

项目作者获取器。

\n
\n

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

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

getter.category

\n
    \n
  • 类型:(page: Page, app: App) => FeedCategory[] | undefined
  • \n
\n

项目分类获取器。

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

getter.enclosure

\n
    \n
  • 类型:(page: Page, app: App) => FeedEnclosure | undefined
  • \n
\n

项目附件获取器。

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

getter.publishDate

\n
    \n
  • 类型:(page: Page, app: App) => Date | undefined
  • \n
\n

项目发布日期获取器

\n

getter.lastUpdateDate

\n
    \n
  • 类型:(page: Page, app: App) => Date
  • \n
\n

项目最后更新日期获取器

\n

getter.image

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

项目图片获取器

\n
\n

确保返回一个完整的 URL。

\n
\n

getter.contributor

\n
    \n
  • 类型:(page: Page, app: App) => FeedContributor[]
  • \n
\n

项目贡献者获取器

\n
\n

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

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

getter.copyright

\n
    \n
  • 类型:(page: Page, app: App) => string | undefined
  • \n
\n

项目版权获取器

\n", + "date_modified": "2025-01-10T18:07:54.000Z", + "authors": [], + "tags": [] + }, + { + "title": "指南", + "url": "https://ecosystem.vuejs.press/ecosystem/zh/plugins/blog/feed/guide.html", + "id": "https://ecosystem.vuejs.press/ecosystem/zh/plugins/blog/feed/guide.html", + "summary": "指南 使用 插件可为你生成以下三种格式的 feed 文件: Atom 1.0 JSON 1.1 RSS 2.0 请按照需要生成的格式,在插件选项中设置 atom, json 或 rss 为 true。 为了正确生成 Feed 链接,你需要在插件选项中设置 hostname。 可读的预览 当你在浏览器中打开 Feed 文件时,我们会通过 xsl 模板将 ...", + "content_html": "\n

使用

\n

插件可为你生成以下三种格式的 feed 文件:

\n
    \n
  • Atom 1.0
  • \n
  • JSON 1.1
  • \n
  • RSS 2.0
  • \n
\n

请按照需要生成的格式,在插件选项中设置 atom, jsonrsstrue

\n

为了正确生成 Feed 链接,你需要在插件选项中设置 hostname

\n

可读的预览

\n

当你在浏览器中打开 Feed 文件时,我们会通过 xsl 模板将 atom 和 rss feed xml 魔法般地转换为可读的 html。你可以查看本站的 atomrss feed 作为案例!

\n

如果你想在开发服务器中预览 Feed,你需要在插件选项中设置 devServer: true。如果你没有使用默认的 http://localhost:{port},你还需要设置 devHostname

\n

频道设置

\n

你可以通过设置 channel 选项来自自定义 Feed 频道的各项信息。

\n

我们推荐进行如下设置:

\n
    \n
  • 将建立 Feed 的日期转换为 ISOString 写入到 channel.pubDate
  • \n
  • 通过 channel.ttl 中设置内容的更新周期(单位: 分钟)
  • \n
  • 通过 channel.copyright 设置版权信息
  • \n
  • 通过 channel.author 设置频道作者。
  • \n
\n

详细的选项及其默认值详见 配置 → 频道设置

\n

Feed 生成

\n

默认情况下,所有文章均会被添加至 feed 流。

\n

你可以在 frontmatter 中配置 feed 和其他选项控制每个页面的 Feed 项目内容,详见 Frontmatter 选项 了解它们如何被转换。

\n

你可以通过配置插件选项中的 getter 完全控制 Feed 项目的生成逻辑。 详细的选项及其默认值详见 配置 → Feed 获取器

\n

多语言配置

\n

插件会针对每个语言生成单独的 Feed。

\n

你可以通过插件选项中的 locales 分别对不同语言提供不同的默认设置。

\n", + "date_modified": "2025-01-10T18:07:54.000Z", + "authors": [], + "tags": [] + }, + { + "title": "sass-palette", + "url": "https://ecosystem.vuejs.press/ecosystem/zh/plugins/development/sass-palette/", + "id": "https://ecosystem.vuejs.press/ecosystem/zh/plugins/development/sass-palette/", + "summary": "sass-palette", + "content_html": "\n", + "date_modified": "2025-01-10T18:07:54.000Z", + "authors": [], + "tags": [] + }, + { + "title": "配置", + "url": "https://ecosystem.vuejs.press/ecosystem/zh/plugins/development/sass-palette/config.html", + "id": "https://ecosystem.vuejs.press/ecosystem/zh/plugins/development/sass-palette/config.html", + "summary": "配置 插件选项 id 类型: string 必填: 是 调色板的唯一 ID,用于避免重复注册。 config 类型: string 默认值: `.vuepress/styles/${id}-palette.scss` 用户配置文件路径,相对于源文件夹。 提示 这是用户设置样式变量的文件。 默认路径的文件名拥有上方的 ID 前缀。 defaultConf...", + "content_html": "\n

插件选项

\n

id

\n
    \n
  • 类型: string
  • \n
  • 必填: 是
  • \n
\n

调色板的唯一 ID,用于避免重复注册。

\n

config

\n
    \n
  • 类型: string
  • \n
  • 默认值: `.vuepress/styles/${id}-palette.scss`
  • \n
\n

用户配置文件路径,相对于源文件夹。

\n
\n

提示

\n

这是用户设置样式变量的文件。

\n

默认路径的文件名拥有上方的 ID 前缀。

\n
\n

defaultConfig

\n
    \n
  • 类型: string
  • \n
  • 默认值: \"@vuepress/plugin-sass-palette/styles/default/config.scss\"
  • \n
\n

默认的配置文件路径,应为绝对路径。

\n
\n

提示

\n

这是你应该通过 !default 来提供默认样式变量的文件。

\n
\n

palette

\n
    \n
  • 类型: string
  • \n
  • 默认值: `.vuepress/styles/${id}-palette.scss`
  • \n
\n

用户的调色板文件路径,相对于源文件夹。

\n
\n

提示

\n

这是用户控制注入 CSS 变量的文件。所有的变量会被转换为连字符格式然后被注入。

\n

默认路径的文件名拥有上方的 ID 前缀。

\n
\n

defaultPalette

\n
    \n
  • 类型: string
  • \n
  • 默认值: \"@vuepress/plugin-sass-palette/styles/default/palette.scss\"
  • \n
\n

默认的调色板文件路径,应为绝对路径。

\n
\n

提示

\n

这是你应该通过 !default 来提供默认调色板值的文件。所有的变量会被转换为连字符格式然后被注入。

\n
\n

generator

\n
    \n
  • 类型: string
  • \n
  • 必填: 否
  • \n
\n

自定义的生成器,用于生成调色板配置的衍生值。

\n

如: 你可能想要根据 $theme-color 的值提供一个 $theme-color-light

\n

style

\n
    \n
  • 类型: string
  • \n
  • 必填: 否
  • \n
\n

用户的样式文件路径,相对于源文件夹。.

\n

别名

\n

可用的别名如下:

\n
    \n
  • 配置: @sass-palette/${id}-config (基于 id)
  • \n
  • 调色板: @sass-palette/${id}-palette (基于 id)
  • \n
  • 样式: @sass-palette/${id}-style (仅在设置了 style 选项时可用)
  • \n
  • 助手: @sass-palette/helper
  • \n
\n", + "date_modified": "2025-01-10T18:07:54.000Z", + "authors": [], + "tags": [] + }, + { + "title": "指南", + "url": "https://ecosystem.vuejs.press/ecosystem/zh/plugins/development/sass-palette/guide.html", + "id": "https://ecosystem.vuejs.press/ecosystem/zh/plugins/development/sass-palette/guide.html", + "summary": "指南 相比于 插件,本插件允许你: 基于用户配置派生相关样式 在插件中调用并提供和主题类似的样式自定义 跨越多个插件或主题通过 id 选项分组应用 在使用插件前,你需要了解 id 选项,以及三个样式概念: 配置、调色板和派生器。 ID 选项 首先,你应该了解此插件的设计目标是提供跨越插件和主题的支持 (而并不像官方插件仅面向主题)。 我们提供了 id ...", + "content_html": "\n

相比于 @vuepress/plugin-palette 插件,本插件允许你:

\n
    \n
  • 基于用户配置派生相关样式
  • \n
  • 在插件中调用并提供和主题类似的样式自定义
  • \n
  • 跨越多个插件或主题通过 id 选项分组应用
  • \n
\n

在使用插件前,你需要了解 id 选项,以及三个样式概念: 配置、调色板和派生器。

\n

ID 选项

\n

首先,你应该了解此插件的设计目标是提供跨越插件和主题的支持 (而并不像官方插件仅面向主题)。

\n

我们提供了 id 选项来完成此目标,它将允许你:

\n
    \n
  • \n

    在插件 (或主题) 间共享同一个样式系统。

    \n

    所有别名和模块名称都具有 ID 前缀,这意味着你可以在你的插件 (或主题) 中使用一套样式变量来统一你的样式,而不会受到其他插件 (或主题) 的影响。

    \n

    用户可以在同一个文件中配置所有颜色变量、断点和其他样式配置,并自动应用在具有相同 ID 的主题和插件上。

    \n
    \n

    示例

    \n

    vuepress-theme-hope 及其它的相关插件都使用相同 ID hope 调用插件,因此用户在主题中配置的样式会自动在所有插件中生效。

    \n
    \n
  • \n
  • \n

    设置不同的 ID 时,插件们和主题之间互相完全独立。我们建议你使用你的插件名称设置 id 变量。

    \n

    使用默认设置,用户将在 .vuepress/styles 文件夹下设置你的插件样式,其中 Sass 文件以你的 ID 前缀开头。你可以使用 ${id}-config${id}-palette 访问所需的变量。

    \n
    \n

    示例

    \n

    vuepress-theme-hope 正在使用 ID \"hope\",而假设 vuepress-plugin-abc 正在使用 \"abc\"。他们可以分别使用 hope-config hope-paletteabc-config abc-palette 模块名称获取自己的变量。

    \n
    \n
  • \n
  • \n

    通过相同 ID 调用插件不会有任何副作用。

    \n
    \n

    示例

    \n

    vuepress-theme-hope 及其它的相关插件都使用相同 ID hope 调用插件。

    \n
    \n
  • \n
\n

配置

\n

配置文件仅用于提供 Sass 变量。它所包含 Sass 变量可以在其他文件中通过 ${id}-config 使用。

\n

你可以指定一个文件作为用户配置文件。这样你可以稍后在插件 Sass 文件中访问包含每个变量的模块。此外,你还可以提供默认配置文件,你可以在其中使用 !default 为变量设置默认值。

\n
一个例子\n

假设,你正在 vuepress-plugin-abc 中这样调用插件:

\n
useSassPalette(app, {\n  id: 'abc',\n  defaultConfig: 'vuepress-plugin-abc/styles/config.scss',\n})
\n

用户配置:

\n
$navbar-height: 3.5rem;
\n

默认配置:

\n
$navbar-height: 2rem !default;\n$sidebar-width: 18rem !default;
\n

你可以在插件 Sass 文件中获取到如下变量:

\n
// Vue 单文件组件的 <style lang=\"scss\"> 块或脚本中直接导入的 Scss 文件中\n@debug abc-config.$navbar-height; // 3.5rem\n@debug abc-config.$sidebar-width; // 18rem
\n
\n

限制

\n

我们利用 additionalData 选项让 ${id}-config 模块在你的样式中可用,但这有一些限制。

\n

additionalData 仅适用于 SASS 入口,因此 ${id}-config 仅适用于:

\n
    \n
  • Vue 单文件组件的 <style lang=\"scss\">
  • \n
  • 脚本中直接导入的 scss 文件 (例如: 客户端应用程序增强文件中的 import \"./a-scss-file.scss\") 。
  • \n
\n

如果 scss 文件不是直接导入的,而是通过 @use@import API 导入的,模块将不可用。因此,在这种情况下,你应该通过 @use \"@sass-palette/${id}-config\"; 手动导入模块。

\n

调色板

\n

调色板文件用于 CSS 变量注入,其中每个变量将被注入到 root 中,变量名称转换为 kebab-name 格式。

\n

你可以指定一个文件作为用户调色板文件,默认文件名是 ${id}-palette.scss。 此外,你还可以提供一个默认的调色板文件,你可以在其中使用 !default 为变量设置默认值。

\n
一个例子\n

假设,你正在 vuepress-plugin-abc 中这样调用插件:

\n
useSassPalette(app, {\n  id: 'abc',\n  defaultPalette: 'vuepress-plugin-abc/styles/palette.scss',\n})
\n

用户调色板:

\n
$color-a: red;
\n

默认调色板:

\n
$color-a: blue !default;\n$color-b: green !default;
\n

那么 root 选择器将会拥有下列 CSS 变量:

\n
:root {\n  --color-a: red;\n  --color-b: green;\n}
\n
\n

和配置文件一样,调色板文件提供了一个 ${$id}-palette 模块 (也包含生成器的值),它也受 additionalData 选项的限制,因此如果你想在其他 Sass 文件中使用它,你应该手动导入模块。

\n

颜色设置

\n

由于默认主题支持深色模式,因此你可能希望在浅色模式和深色模式下使用不同的颜色。

\n

为此,你应该使用包含 lightdark 键的映射设置颜色变量。 稍后,此插件将为你生成不同的颜色。

\n
一个例子\n
// 用户调色板\n$text-color: (\n  light: #222,\n  dark: #999,\n);\n\n// 默认调色板\n$text-color: (\n  light: #2c3e50,\n  dark: #9e9e9e,\n) !default;\n$bg-color: (\n  light: #fff,\n  dark: #1e1e1e,\n) !default;
\n

然后你会得到:

\n
:root {\n  --text-color: #222;\n  --bg-color: #fff;\n}\n\n[data-theme='dark'] {\n  --text-color: #999;\n  --bg-color: #1e1e1e;\n}
\n
\n

允许的变量类型

\n

调色板中只允许使用颜色 (或深浅模式颜色对象)、长度和字符串。任何其他类型都将被删除。

\n
\n

为什么除了字符串只允许颜色和长度

\n

在常见情况下,你可能只想计算颜色和长度。所以放弃其他类型支持是相当安全的,因为你想要的任何其他值都可以转换为字符串。

\n
示例\n

如果你想要一个 --move-transitionwidth 0.3s ease,你可以使用字符串:

\n
// 这将被 sass 视为一个类型为 (length, time, function) 的列表\n// 并会触发警告并被插件删除\n$moveTransition: width 0.3s ease;\n\n// 这会得到你想要的\n// :root {\n//   --move-transition: width 0.3s ease\n// }\n$moveTransition: 'width 0.3s ease';
\n
\n
\n

辅助模块

\n

我们公开了 @vuepress/plugin-sass-palette 使用的内部函数,作为辅助模块。

\n

你可以通过 @sass-palette/helper 别名使用此辅助模块,并调用其函数来自己实现类似的功能。

\n

生成器

\n

生成器文件面向开发人员使用配置或调色板文件变量生成衍生值。

\n

你可以在此文件中直接获取调色板的变量值,并生成基于它们的新值。

\n

生成器变量也将像调色板一样作为 CSS 变量注入,它们也在调色板模块中可用。

\n
示例\n

你可能想要一个基于 $theme-color$theme-color-light。所以你可以这样写一个生成器:

\n
@use 'sass:color';\n@use '@sass-palette/helper';\n\n$theme-color-light: (\n  light: color.scale(helper.get-color($theme-color), $lightness: 10%),\n  dark: color.scale(helper.get-dark-color($theme-color), $lightness: 10%),\n) !default;
\n

你也可以通过导入配置文件来基于配置文件提供的变量生成值:

\n
// id 为 \"abc\" 的生成器\n@use 'sass:color';\n@use '@sass-palette/abc-config';\n@use '@sass-palette/helper';\n\n$code-c-bg: abc-config.$highlighter == 'shiki'? #fff: #f8f8f8;
\n
\n

用户样式

\n

如果你是主题开发人员,你可能希望为你的用户提供一种自定义主题或网站的方法。

\n

在这种情况下,你应该在使用此插件时将 style 选项设置为用户样式文件。

\n

稍后,你应该通过在你的主题样式之后导入 @sass-palette/${id}-style 来手动包含用户样式文件。

\n
\n

提示

\n

@sass-palette/${id}-style 是用户样式文件的别名,你可以在 JS/TS/SASS 中导入它。

\n
\n", + "date_modified": "2025-01-10T18:07:54.000Z", + "authors": [], + "tags": [] + }, + { + "title": "revealjs", + "url": "https://ecosystem.vuejs.press/ecosystem/zh/plugins/markdown/revealjs/", + "id": "https://ecosystem.vuejs.press/ecosystem/zh/plugins/markdown/revealjs/", + "summary": "revealjs", + "content_html": "\n", + "date_modified": "2025-01-10T18:07:54.000Z", + "authors": [], + "tags": [] + }, + { + "title": "幻灯片演示", + "url": "https://ecosystem.vuejs.press/ecosystem/zh/plugins/markdown/revealjs/demo.html", + "id": "https://ecosystem.vuejs.press/ecosystem/zh/plugins/markdown/revealjs/demo.html", + "content_html": "", + "image": "https://theme-hope-assets.vuejs.press/logo.svg", + "date_modified": "2025-01-10T18:07:54.000Z", + "authors": [], + "tags": [] + }, + { + "title": "幻灯片主题", + "url": "https://ecosystem.vuejs.press/ecosystem/zh/plugins/markdown/revealjs/themes.html", + "id": "https://ecosystem.vuejs.press/ecosystem/zh/plugins/markdown/revealjs/themes.html", + "summary": "幻灯片主题 auto 基于主题模式 black white league beige sky night serif simple solarized blood moon", + "content_html": "\n

auto

\n
\n

基于主题模式

\n
\n", + "date_modified": "2025-01-10T18:07:54.000Z", + "authors": [], + "tags": [] + }, + { + "title": "pwa", + "url": "https://ecosystem.vuejs.press/ecosystem/zh/plugins/pwa/pwa/", + "id": "https://ecosystem.vuejs.press/ecosystem/zh/plugins/pwa/pwa/", + "summary": "pwa", + "content_html": "\n", + "date_modified": "2025-01-10T18:07:54.000Z", + "authors": [], + "tags": [] + }, + { + "title": "配置", + "url": "https://ecosystem.vuejs.press/ecosystem/zh/plugins/pwa/pwa/config.html", + "id": "https://ecosystem.vuejs.press/ecosystem/zh/plugins/pwa/pwa/config.html", + "summary": "配置 选项 showInstall 类型:boolean 默认值:false 详情: 是否在 Service Worker 首次成功注册时显示 PWA 安装按钮 manifest 类型:ManifestOption 详情: 填充一个将被解析为 manifest.webmanifest 的对象。 提示 如果未设置某些选项,它们会回退到插件预设值。 nam...", + "content_html": "\n

选项

\n

showInstall

\n
    \n
  • \n

    类型:boolean

    \n
  • \n
  • \n

    默认值:false

    \n
  • \n
  • \n

    详情:

    \n

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

    \n
  • \n
\n

manifest

\n
    \n
  • \n

    类型:ManifestOption

    \n
  • \n
  • \n

    详情:

    \n

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

    \n
    \n

    提示

    \n

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

    \n
      \n
    • name: siteConfig.title || siteConfig.locales['/'].title || \"Site\"
    • \n
    • short_name: siteConfig.title || siteConfig.locales['/'].title || \"Site\"
    • \n
    • description: siteConfig.description || siteConfig.locales['/'].description || \"A site built with vuepress\"
    • \n
    • lang: siteConfig.locales['/'].lang || \"en-US\"
    • \n
    • start_url: context.base
    • \n
    • scope: context.base
    • \n
    • display: \"standalone\"
    • \n
    • theme_color: \"#46bd87\"
    • \n
    • background_color: \"#ffffff\"
    • \n
    • orientation: \"portrait-primary\"
    • \n
    • prefer_related_applications: false
    • \n
    \n
    \n
  • \n
  • \n

    参考:

    \n\n
  • \n
\n

favicon

\n
    \n
  • \n

    类型:string

    \n
  • \n
  • \n

    详情:

    \n

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

    \n
    \n

    注意

    \n

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

    \n
    \n
  • \n
\n

themeColor

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

cacheHTML

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

cacheImage

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

maxSize

\n
    \n
  • \n

    类型:number

    \n
  • \n
  • \n

    默认值:2048

    \n
  • \n
  • \n

    详情:

    \n

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

    \n
    \n

    注意

    \n

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

    \n

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

    \n
    \n
  • \n
\n

maxImageSize

\n
    \n
  • \n

    类型:number

    \n
  • \n
  • \n

    默认值:1024

    \n
  • \n
  • \n

    详情:

    \n

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

    \n
    \n

    该选项不能大于 maxSize 选项

    \n
    \n
  • \n
\n

update

\n
    \n
  • \n

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

    \n
  • \n
  • \n

    默认值:\"available\"

    \n
  • \n
  • \n

    详情:

    \n

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

    \n
      \n
    • \n

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

      \n
    • \n
    • \n

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

      \n
    • \n
    • \n

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

      \n

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

      \n
      \n

      提示

      \n

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

      \n
      \n
    • \n
    • \n

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

      \n
      \n

      警告

      \n

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

      \n
      \n
    • \n
    \n
    \n

    提示

    \n

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

    \n
    \n
  • \n
\n

apple

\n

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

\n

apple.icon

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

apple.maskIcon

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

apple.statusBarColor

\n
    \n
  • 类型:'black-translucent' | 'black' | 'default
  • \n
  • 详情:Safari 状态栏颜色
  • \n
\n

foundComponent

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

readyComponent

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

appendBase

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

generateSwConfig

\n
    \n
  • \n

    详情:

    \n

    传递给 workbox-build 的选项,具体详情,请见 Workbox 文档

    \n
  • \n
\n

locales

\n
    \n
  • \n

    类型:PwaPluginLocaleConfig

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

    详情:

    \n

    PWA 插件的国际化配置。

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

组合式 API

\n

usePwaEvent

\n
    \n
  • \n

    详情:

    \n

    返回此插件的事件派发器。

    \n

    你可以添加监听器函数到 register-service-worker 提供的事件。

    \n
  • \n
  • \n

    示例:

    \n
  • \n
\n
import { usePwaEvent } from '@vuepress/plugin-pwa/client'\n\nexport default {\n  setup(): void {\n    const event = usePwaEvent()\n    event.on('ready', (registration) => {\n      console.log('Service worker is active.')\n    })\n  },\n}
\n

工具函数

\n

forceUpdate

\n
    \n
  • \n

    详情:

    \n

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

    \n
  • \n
  • \n

    示例:

    \n
  • \n
\n
import { forceUpdate } from '@vuepress/plugin-pwa/client'\nimport { onMounted } from 'vue'\n\nexport default {\n  setup(): void {\n    onMounted(() => {\n      forceUpdate()\n    })\n  },\n}
\n

registerSW

\n
    \n
  • \n

    详情:

    \n

    手动注册 Service Worker。

    \n
  • \n
  • \n

    参数:

    \n
  • \n
\n

| 参数 | 类型 | 描述 |
\n|

\n", + "date_modified": "2025-01-10T18:07:54.000Z", + "authors": [], + "tags": [] + }, + { + "title": "指南", + "url": "https://ecosystem.vuejs.press/ecosystem/zh/plugins/pwa/pwa/guide.html", + "id": "https://ecosystem.vuejs.press/ecosystem/zh/plugins/pwa/pwa/guide.html", + "summary": "指南 介绍 将你的 VuePress 站点变成渐进式网络应用程序 (PWA)[1]。 此插件使用 workbox-build 生成 Service Worker 文件,并使用 register-service-worker 注册 Service Worker。 注意 如果你启用过该插件,并想要禁用它,你可能需要 来移除现有的 Service Worke...", + "content_html": "\n

介绍

\n

将你的 VuePress 站点变成渐进式网络应用程序 (PWA)[1]

\n

此插件使用 workbox-build 生成 Service Worker 文件,并使用 register-service-worker 注册 Service Worker。

\n
\n

注意

\n

如果你启用过该插件,并想要禁用它,你可能需要 `@vuepress/plugin-remove-pwa 来移除现有的 Service Worker 。

\n
\n

A PWA uses a Service Worker [2] (SW for short) to cache and proxy site content.

\n

一个 PWA 使用 Service Worker [2:1] (简称 SW) 来获取并托管网站内容。

\n

网络 App 清单

\n

为了使你的网站符合 PWA 的要求,一个网络 App 清单[3]文件是必要的,并且你的 PWA 应满足可安装性[4]要求。

\n

你可以通过设置 manifest 选项来自定义 manifest 文件,或者在 public 文件夹中提供 manifest.webmanifestmanifest.json。前者优先级更高。

\n

插件会自动为你生成 manifest.webmanifest,并在每个页面的 <head> 中添加清单链接声明,但是 你至少应该通过 manifest.icons 或 PWA 插件中的其他选项设置一个有效的图标。

\n
\n

此外,该插件默认不处理清单中的任何内容,而是按原样输出。 这意味着,如果你计划部署到子目录,则应自行将 URL 前缀附加到自己的清单 Urls 中。如果你需要的所有东西都在 base 文件夹下,你可以在插件选项中设置 appendBase: true 让插件将 base 自动附加到任何地址。

\n

缓存控制

\n

为了更好的控制 Service Worker 可以预缓存的内容,插件提供了相关的缓存控制选项。

\n

默认缓存

\n

默认情况下插件会预缓存所有的 JS 和 CSS 文件,但仅缓存主页和 404 页面的 HTML。插件同时还会缓存字体文件 (woff, woff2, eot, ttf, otf) 和 SVG 图标。

\n

图片缓存

\n

如果你的站点只有少量重要图片,并希望它们在离线模式下显示,你可以通过设置 cacheImage 选项为 true 来缓存站点图片。

\n

我们通过文件后缀名识别图片,任何以 .png, .jpg, .jpeg, .gif, .bmp, .webp 结尾的文件都会视为图片。

\n

HTML 缓存

\n

当你网站体积不大,并且希望文档完全离线可用时,你可以通过设置 cacheHTMLtrue 来缓存所有 HTML 页面。

\n\n

大小控制

\n

为了防止在预缓存列表中包含大文件,任何 > 2 MB 的文件或 > 1 MB 的图片都将被忽略。 你可以通过 maxSizemaxImageSize 来自定义大小限制 (单位为 KB)。

\n

更新控制

\n

我们提供 update 选项控制用户如何接收更新。

\n

update 选项的默认值是 \"available\",这意味着当网站内容更新后,新的 SW 会在后台静默安装,并在安装结束后弹窗提示用户新内容就绪。用户可以自主选择是否立即刷新查看新内容。这意味在新 SW 就绪前用户会访问旧版本网站。

\n

如果你的文档仍在建设期,希望尽早提示用户他可能在阅读已过时的内容,你可以将其设置为 \"hint\"。这样用户在进入文档后数秒内就可以收到新内容已发布的通知。但这样做的负面效果是如果用户在新 SW 就绪前选择更新,那么他将在新 SW 安装并接管页面前,需要从互联网获取页面的全部资源。

\n

如果你的文档很稳定,或者你在托管博客,不太关心用户立即接收到最新版本,你可以将其设置为 \"disable\",这意味着新的 SW 将在后台完全静默安装并在安装后等待,当旧版本 SW 控制的页面全部关闭后,新 SW 将再下次访问接管并提供用户新内容。此设置可以避免用户在访中被弹窗打扰。

\n

如果你希望通过 SW 来加速用户在弱网或无网条件下的访问,但同时希望用户时刻访问新内容,你可以将此选项设置为 \"force\"。这意味着检测到新 SW 后旧 SW 将会被立刻销毁并且页面会被刷新以确保用户浏览最新内容。最大的缺点就是致新 SW 发布后,用户在重新进入网站后的几秒内会遇到预期之外的突然刷新,并且他们将必须通过互联网访问文档并完全重新安装最新的 SW。

\n

更新提示弹窗

\n

当检测到新内容 (检测到新的 SW) 时,更新提示弹窗将会出现;当新内容就绪时,更新就绪弹窗将会出现。

\n

如果你对默认的弹窗不满意,你可以自行编写组件更换。从 @vuepress/plugin-pwa/client 中导入 PwaFoundPopupPwaReadyPopup 并使用其 slot 来自定义弹窗内容,然后将组件路径传递给 foundComponentreadyComponent 选项。

\n
<script setup lang=\"ts\">\nimport { PwaFoundPopup } from '@vuepress/plugin-pwa/client'\n</script>\n<template>\n  <PwaFoundPopup v-slot=\"{ found, refresh }\">\n    <div v-if=\"found\">\n      已找到新内容\n      <button type=\"button\" @click=\"refresh\">刷新</button>\n    </div>\n  </PwaFoundPopup>\n</template>
\n
<script setup lang=\"ts\">\nimport { PwaReadyPopup } from '@vuepress/plugin-pwa/client'\n</script>\n<template>\n  <PwaReadyPopup v-slot=\"{ isReady, reload }\">\n    <div v-if=\"isReady\">\n      新内容已就绪\n      <button type=\"button\" @click=\"reload\">应用</button>\n    </div>\n  </PwaReadyPopup>\n</template>
\n

其他选项

\n

插件还提供了其他 PWA 相关选项,比如微软磁贴图标与颜色设置,苹果图标等。 如果你是一个高级用户,你也可以设置 generateSwConfig 来配置 workbox-build。查看 插件选项 了解更多细节。

\n

相关阅读

\n

更多内容,请详见:

\n\n
\n
\n
    \n
  1. PWA 介绍

    \n

    PWA 全称 Progressive Web app,即渐进式网络应用程序,标准由 W3C 规定。

    \n

    它允许网站通过支持该特性的浏览器将网站作为 App 安装在对应平台上。

    \n

    访问 https://developer.mozilla.org/zh-CN/docs/Web/Progressive_web_apps 查看详情。 ↩︎

    \n
  2. \n
  3. Service Worker 简要介绍

    \n
      \n
    1. \n

      Service Worker 会在注册过程中获取注册在其中的所有文件并缓存它们。

      \n
    2. \n
    3. \n

      注册成功后,Service Worker 激活,并开始代理并控制你的全部请求。

      \n
    4. \n
    5. \n

      每当你想要通过浏览器发起访问请求后,Service Worker 将会查看其是否存在与自身缓存列表中,若存在则直接返回缓存好的结果,否则调用自身的 fetch 方法进行获取。你可以通过自定义 fetch 方法,来完全控制网页内资源获取请求的结果,比如在离线时提供一个 fallback 的网页。

      \n
    6. \n
    7. \n

      每次用户重新打开网站时,Service Worker 会向自身注册时的地址发出校验命令,如果检测到新版本的 Service Worker,则会更新自身,并开始缓存注册在新 Service Worker 中的资源列表。成功获取内容更新后,Service Worker 将会触发 update 事件。可以通过此事件提示用户,比如将在右下角显示一个弹出窗口,提示用户新内容可用并允许用户触发更新。

      \n
    8. \n
    \n ↩︎ ↩︎
  4. \n
  5. 清单文件

    \n

    清单文件使用 JSON 格式,负责声明 PWA 各项信息,如名称、描述、图标、快捷动作等。

    \n

    为了使你的站点能够被注册为 PWA,你需要满足 manifest 基本的规范,才能使浏览器认为该网站为一个可安装的 PWA 并允许用户安装它。

    \n
    \n

    提示

    \n

    Manifest 的标准与规范,请详见 MDN 网络 App 清单W3C Manifest

    \n
    \n ↩︎
  6. \n
  7. 可安装性

    \n

    想要让网站可以注册为 PWA,网站需要自行成功注册有效的 Service Worker,同时拥有合法的 manifest 清单文件并在网站中声明它。

    \n

    清单文件应至少包含 name(或 short_name) icons start_url

    \n

    在 Safari 中,SW 的最大缓存空间为 50 MB。 ↩︎ ↩︎

    \n
  8. \n
  9. SSG: Static Site Generating,静态站点生成。 ↩︎

    \n
  10. \n
  11. SEO: Search Engine Optimization,搜索引擎增强,

    \n

    详见 SEO 介绍 ↩︎

    \n
  12. \n
  13. SPA: Single Page Application, 单页应用

    \n

    大多只有主页,并使用 history mode 处理路由,而不是真的在页面之间导航。 ↩︎

    \n
  14. \n
\n
\n", + "date_modified": "2025-01-10T18:07:54.000Z", + "authors": [], + "tags": [] + }, + { + "title": "seo", + "url": "https://ecosystem.vuejs.press/ecosystem/zh/plugins/seo/seo/", + "id": "https://ecosystem.vuejs.press/ecosystem/zh/plugins/seo/seo/", + "summary": "seo", + "content_html": "\n", + "date_modified": "2025-01-10T18:07:54.000Z", + "authors": [], + "tags": [] + }, + { + "title": "选项", + "url": "https://ecosystem.vuejs.press/ecosystem/zh/plugins/seo/seo/config.html", + "id": "https://ecosystem.vuejs.press/ecosystem/zh/plugins/seo/seo/config.html", + "summary": "选项 hostname 类型:string 必填:是 详情: 部署域名 author 类型:Author 详情: 默认作者 autoDescription 类型:boolean 默认值:true 详情: 是否自动生成描述 canonical 类型:string | ((page: Page) => string | null) 详情: 首选链接 fal...", + "content_html": "\n

hostname

\n
    \n
  • \n

    类型:string

    \n
  • \n
  • \n

    必填:是

    \n
  • \n
  • \n

    详情:

    \n

    部署域名

    \n
  • \n
\n

author

\n
    \n
  • \n

    类型:Author

    \n
    type AuthorName = string\n\ninterface AuthorInfo {\n  /**\n   * 作者姓名\n   */\n  name: string\n\n  /**\n   * 作者网站\n   */\n  url?: string\n\n  /**\n   * 作者 Email\n   */\n  email?: string\n}\n\ntype Author = AuthorInfo | AuthorInfo[] | AuthorName | AuthorName[]
    \n
  • \n
  • \n

    详情:

    \n

    默认作者

    \n
  • \n
\n

autoDescription

\n
    \n
  • \n

    类型:boolean

    \n
  • \n
  • \n

    默认值:true

    \n
  • \n
  • \n

    详情:

    \n

    是否自动生成描述

    \n
  • \n
\n

canonical

\n
    \n
  • \n

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

    \n
  • \n
  • \n

    详情:

    \n

    首选链接

    \n
  • \n
\n

fallBackImage

\n
    \n
  • \n

    类型:string

    \n
  • \n
  • \n

    详情:

    \n

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

    \n
  • \n
\n

restrictions

\n
    \n
  • \n

    类型:string

    \n
  • \n
  • \n

    详情:

    \n

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

    \n
  • \n
\n

twitterID

\n
    \n
  • \n

    类型:string

    \n
  • \n
  • \n

    详情:

    \n

    你的 twitter 用户名

    \n
  • \n
\n

isArticle

\n
    \n
  • \n

    类型:(page: Page) => boolean

    \n
  • \n
  • \n

    详情:

    \n

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

    \n
  • \n
\n

ogp

\n
    \n
  • \n

    类型:

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

    详情:

    \n

    自定义 OGP 生成器

    \n

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

    \n
  • \n
\n

jsonLd

\n
    \n
  • \n

    类型:

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

    详情:

    \n

    自定义 JSON-LD 生成器

    \n

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

    \n
  • \n
\n

customHead

\n
    \n
  • \n

    类型:

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

    详情:

    \n

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

    \n
  • \n
\n", + "date_modified": "2025-01-10T18:07:54.000Z", + "authors": [], + "tags": [] + }, + { + "title": "指南", + "url": "https://ecosystem.vuejs.press/ecosystem/zh/plugins/seo/seo/guide.html", + "id": "https://ecosystem.vuejs.press/ecosystem/zh/plugins/seo/seo/guide.html", + "summary": "指南 本插件会通过向网站 注入标签,让你的网站完全支持 开放内容协议 OGP 和 JSON-LD 1.1,以全面增强站点的搜索引擎优化性。 开箱即用 插件开箱即用,在不做任何配置的情况下,会尽可能通过页面内容,提取对应的信息补全 OGP 与 JSON-LD 所需的必要标签。 默认情况下,插件会读取站点配置、主题配置与页面的 frontma...", + "content_html": "\n

本插件会通过向网站 <head> 注入标签,让你的网站完全支持 开放内容协议 OGPJSON-LD 1.1,以全面增强站点的搜索引擎优化性。

\n\n

开箱即用

\n

插件开箱即用,在不做任何配置的情况下,会尽可能通过页面内容,提取对应的信息补全 OGP 与 JSON-LD 所需的必要标签。

\n

默认情况下,插件会读取站点配置、主题配置与页面的 frontmatter 来尽可能自动生成。诸如站点名称,页面标题,页面类型,写作日期,最后更新日期,文章标签均会自动生成。

\n

默认的 OGP 生成逻辑

\n

| 属性名称 | 值 |
\n| :

\n", + "date_modified": "2025-01-10T18:07:54.000Z", + "authors": [], + "tags": [] + }, + { + "title": "sitemap", + "url": "https://ecosystem.vuejs.press/ecosystem/zh/plugins/seo/sitemap/", + "id": "https://ecosystem.vuejs.press/ecosystem/zh/plugins/seo/sitemap/", + "summary": "sitemap", + "content_html": "\n", + "date_modified": "2025-01-10T18:07:54.000Z", + "authors": [], + "tags": [] + }, + { + "title": "配置", + "url": "https://ecosystem.vuejs.press/ecosystem/zh/plugins/seo/sitemap/config.html", + "id": "https://ecosystem.vuejs.press/ecosystem/zh/plugins/seo/sitemap/config.html", + "summary": "配置 hostname 类型:string 必填:是 详情: 当前网站部署到的域名,插件需要此选项才能工作。 extraUrls 类型:string[] 详情: 需要额外包含的网址。 如果你有一些不包含在 VuePress 路由中的链接 (如: 存放在 public 文件夹下的页面或其他插件或工具直接生成的页面),你可能需要设置此项。 示例:['/ab...", + "content_html": "\n

hostname

\n
    \n
  • \n

    类型:string

    \n
  • \n
  • \n

    必填:是

    \n
  • \n
  • \n

    详情:

    \n

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

    \n
  • \n
\n

extraUrls

\n
    \n
  • \n

    类型:string[]

    \n
  • \n
  • \n

    详情:

    \n

    需要额外包含的网址。

    \n

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

    \n
  • \n
  • \n

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

    \n
  • \n
\n

excludePaths

\n
    \n
  • \n

    类型:string[]

    \n
  • \n
  • \n

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

    \n
  • \n
  • \n

    详情:

    \n

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

    \n

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

    \n
  • \n
\n

devServer

\n
    \n
  • \n

    类型:boolean

    \n
  • \n
  • \n

    默认值:false

    \n
  • \n
  • \n

    详情:

    \n

    是否在开发服务器中启用

    \n
  • \n
\n
\n

提示

\n

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

\n
\n

devHostname

\n
    \n
  • \n

    类型:string

    \n
  • \n
  • \n

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

    \n
  • \n
  • \n

    详情:

    \n

    开发服务器使用的主机名

    \n
  • \n
\n

sitemapFilename

\n
    \n
  • \n

    类型:string

    \n
  • \n
  • \n

    默认值:\"sitemap.xml\"

    \n
  • \n
  • \n

    详情:

    \n

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

    \n
  • \n
\n

sitemapXSLFilename

\n
    \n
  • \n

    类型:string

    \n
  • \n
  • \n

    默认值:\"sitemap.xsl\"

    \n
  • \n
  • \n

    详情:

    \n

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

    \n
  • \n
\n

sitemapXSLTemplate

\n
    \n
  • \n

    类型:string

    \n
  • \n
  • \n

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

    \n
  • \n
  • \n

    详情:

    \n

    用作模板的 XSL 文件内容

    \n
  • \n
\n

changefreq

\n
    \n
  • \n

    类型:\"always\" | \"hourly\" | \"daily\" | \"weekly\" | \"monthly\" | \"yearly\" | \"never\"

    \n
  • \n
  • \n

    默认值:\"daily\"

    \n
  • \n
  • \n

    详情:

    \n

    页面默认更新频率,会被 Frontmatter 中的 changefreq 选项覆盖。

    \n
  • \n
\n

priority

\n
    \n
  • \n

    类型:number

    \n
  • \n
  • \n

    默认值:0.5

    \n
  • \n
  • \n

    详情:

    \n

    页面优先级,范围 01

    \n
  • \n
\n

modifyTimeGetter

\n
    \n
  • \n

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

    \n
  • \n
  • \n

    详情:

    \n

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

    \n
  • \n
\n", + "date_modified": "2025-01-10T18:07:54.000Z", + "authors": [], + "tags": [] + }, + { + "title": "Frontmatter", + "url": "https://ecosystem.vuejs.press/ecosystem/zh/plugins/seo/sitemap/frontmatter.html", + "id": "https://ecosystem.vuejs.press/ecosystem/zh/plugins/seo/sitemap/frontmatter.html", + "summary": "Frontmatter sitemap 类型:SitemapFrontmatterOptions | false 详情: false 表示将页面排除在 sitemap 之外。 sitemap.changefreq 类型:\"always\" | \"hourly\" | \"daily\" | \"weekly\" | \"monthly\" | \"yearly\" | \"...", + "content_html": "\n

sitemap

\n
    \n
  • \n

    类型:SitemapFrontmatterOptions | false

    \n
  • \n
  • \n

    详情:

    \n

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

    \n
  • \n
\n

sitemap.changefreq

\n
    \n
  • \n

    类型:\"always\" | \"hourly\" | \"daily\" | \"weekly\" | \"monthly\" | \"yearly\" | \"never\"

    \n
  • \n
  • \n

    默认值:\"daily\"

    \n
  • \n
  • \n

    详情:

    \n

    页面默认更新频率。它会覆盖插件选项中的 changefreq 选项。

    \n
  • \n
\n

sitemap.priority

\n
    \n
  • \n

    类型:number

    \n
  • \n
  • \n

    默认值:0.5

    \n
  • \n
  • \n

    详情:

    \n

    页面优先级,范围 01

    \n
  • \n
\n", + "date_modified": "2025-01-10T18:07:54.000Z", + "authors": [], + "tags": [] + }, + { + "title": "指南", + "url": "https://ecosystem.vuejs.press/ecosystem/zh/plugins/seo/sitemap/guide.html", + "id": "https://ecosystem.vuejs.press/ecosystem/zh/plugins/seo/sitemap/guide.html", + "summary": "指南 本插件会为你的网站自动生成 Sitemap。为了使插件正常工作,你需要将部署的域名传递给插件的 hostname 选项。如果你想在开发服务器中预览,请配置 devServer 选项。 插件会自动根据页面的 Git 的时间戳生成页面的最后更新时间,同时会根据站点的多语言配置声明页面的其他语言版本替代地址。 控制 Sitemap 链接 默认情况下,所...", + "content_html": "\n

本插件会为你的网站自动生成 Sitemap。为了使插件正常工作,你需要将部署的域名传递给插件的 hostname 选项。如果你想在开发服务器中预览,请配置 devServer 选项。

\n

插件会自动根据页面的 Git 的时间戳生成页面的最后更新时间,同时会根据站点的多语言配置声明页面的其他语言版本替代地址。

\n

控制 Sitemap 链接

\n

默认情况下,所有除 404 页面以外的网站链接均会被添加进 Sitemap。

\n

如果你希望在 VuePress 项目页面之外,添加其他页面链接到 Sitemap,请将它们变成数组传入插件的 extraUrls 选项。

\n

如果你需要排除一些页面路径,你可以将它们变成数组传入到插件的 excludePaths 选项。你也可以在对应页面的 frontmatter 中,设置 sitemapfalse

\n

输出位置

\n

你还可以通过插件的 sitemapFilename 选项控制输出的地址,此地址相对于输出目录,默认为 sitemap.xml

\n

更新周期

\n

页面默认的更新周期是 daily (每天),如果你希望修改全部的页面周期,请在插件选项中设置 changefreq 。你也可以在页面的 frontmatter 中设置 sitemap.changefreq,页面具有更高的优先级。

\n

合法的频率有:

\n
    \n
  • \"always\"
  • \n
  • \"hourly\"
  • \n
  • \"daily\"
  • \n
  • \"weekly\"
  • \n
  • \"monthly\"
  • \n
  • \"yearly\"
  • \n
  • \"never\"
  • \n
\n

优先级

\n

你可以在插件中设置 priority 以提供一个默认值。同时你可以通过 frontmatter 中的 sitemap.priority 来为每个页面设置优先级。可接受的值为 01 的浮点数。

\n

修改时间获取

\n

你可以通过插件的 modifyTimeGetter 来返回一个 ISO 字符串格式的时间,默认会通过 Git 插件生成。

\n

以下是一个基于文件最后修改时间的例子。

\n
// 基于文件最后修改时间\n;({\n  modifyTimeGetter: (page, app) =>\n    fs.statSync(app.dir.source(page.filePathRelative)).mtime.toISOString(),\n})
\n

Sitemap 介绍

\n

网站地图 (Sitemap) 提供搜索引擎优化 (SEO):

\n
    \n
  • 为搜索引擎爬虫提供可以浏览整个网站的链接;
  • \n
  • 为搜索引擎爬虫提供一些链接,指向动态页面或者采用其他方法比较难以到达的页面;
  • \n
  • 如果访问者试图访问网站所在域内并不存在的 URL,那么这个访问者就会被转到“无法找到文件”的错误页面,而网站地图可以作为导航页。
  • \n
\n

网站地图通过使所有页面可被找到来增强搜索引擎优化的效果。

\n

大部分搜索引擎只跟踪页面内有限数量的链接,因此当网站非常大的时候,网站地图对于使搜索引擎和访问者可以访问网站中的所有内容就变得必不可少了。

\n

Sitemaps 是站点管理员向搜索引擎爬虫公布站点可被抓取页面的协议,sitemap 文件内容必须遵循 XML 格式的定义。每个 URL 可以包含更新的周期和时间、URL 在整个站点中的优先级。这样可以让搜索引擎更佳有效的抓取网站内容。

\n
\n

同步配置 robots.txt

\n

由于 Sitemap 面向搜索引擎,配合此插件使用时,你最好保证你在 .vuepress/public 文件夹下放置了有效的 robots.txt,以允许搜索引擎收录。一个最简单的 robots.txt 如下 (允许所有搜索引擎访问所有路径)

\n
User-agent: *\n\nAllow: /
\n
\n", + "date_modified": "2025-01-10T18:07:54.000Z", + "authors": [], + "tags": [] + }, + { + "title": "打包器相关", + "url": "https://ecosystem.vuejs.press/ecosystem/zh/tools/helper/node/bundler.html", + "id": "https://ecosystem.vuejs.press/ecosystem/zh/tools/helper/node/bundler.html", + "summary": "打包器相关 打包器函数用于在主题和插件中追加或修改打包器选项。 这些函数仅在 @vuepress/helper 中可用。 提示 所有函数都应在 extendsBundlerOptions 生命周期挂钩中调用。 我们在示例中省略了它。 实际代码应该是这样的: 通用方法 getBundlerName 获取当前打包器的名称。 示例 addCustomElem...", + "content_html": "\n

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

\n

这些函数仅在 @vuepress/helper 中可用。

\n
\n

提示

\n

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

\n

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

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

通用方法

\n

getBundlerName

\n

获取当前打包器的名称。

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

addCustomElement

\n

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

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

customizeDevServer

\n

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

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

Vite 相关

\n
    \n
  • \n

    addViteOptimizeDepsInclude

    \n

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

    \n
    \n

    提示

    \n

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

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

    addViteOptimizeDepsExclude

    \n

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

    \n
    \n

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

    \n
    \n
  • \n
  • \n

    addViteSsrExternal

    \n

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

    \n
    \n

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

    \n
    \n
  • \n
  • \n

    addViteSsrNoExternal

    \n

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

    \n
    \n

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

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

    addViteConfig

    \n

    A function for you to add vite config

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

    mergeViteConfig

    \n

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

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

    注意

    \n

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

    \n
    \n
    示例\n
    import { mergeViteConfig } from '@vuepress/helper'\n\nconfig.viteOptions = mergeViteConfig(config.viteOptions, {\n  build: {\n    charset: 'utf8',\n  },\n})
    \n
    \n
  • \n
\n

Webpack 相关

\n
    \n
  • \n

    chainWebpack

    \n

    链式修改 webpack 配置.

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

    configWebpack

    \n

    配置 Webpack

    \n
    export const configWebpack: (\n  bundlerOptions: unknown,\n  app: App,\n  configureWebpack: (\n    config: WebpackConfiguration,\n    isServer: boolean,\n    isBuild: boolean,\n  ) => void,\n) => void
    \n
    实例\n
    import { configWebpack } from '@vuepress/helper'\n\nconfigWebpack(bundlerOptions, app, (config, isServer, isBuild) => {\n  // do some customize here\n})
    \n
    \n
  • \n
\n", + "date_modified": "2025-01-10T18:07:54.000Z", + "authors": [], + "tags": [] + }, + { + "title": "多语言相关", + "url": "https://ecosystem.vuejs.press/ecosystem/zh/tools/helper/node/locales.html", + "id": "https://ecosystem.vuejs.press/ecosystem/zh/tools/helper/node/locales.html", + "summary": "多语言相关 这些函数仅在 @vuepress/helper 中可用。 getFullLocaleConfig 一个从内置的 locale 信息和用户配置中获取完整 locale 配置的辅助函数。 app 参数是 VuePress Node app 实例。 default 参数是默认的 locale 信息,应该是一个 locale 信息设置的数组。 每个...", + "content_html": "\n

这些函数仅在 @vuepress/helper 中可用。

\n

getFullLocaleConfig

\n

一个从内置的 locale 信息和用户配置中获取完整 locale 配置的辅助函数。

\n
export interface GetLocaleConfigOption<T extends LocaleData> {\n  app: App\n  default: DefaultLocaleInfo<T>\n  config?: LocaleConfig<T> | undefined\n  name?: string\n}\n\nexport const getFullLocaleConfig: <T extends LocaleData>(\n  options: GetLocaleConfigOption<T>,\n) => ExactLocaleConfig<T>
\n
    \n
  • \n

    app 参数是 VuePress Node app 实例。

    \n
  • \n
  • \n

    default 参数是默认的 locale 信息,应该是一个 locale 信息设置的数组。

    \n

    每个 locale 信息设置应该是一个包含两个元素的元组:

    \n
      \n
    • 第一个元素是 locale 信息设置所属的语言代码数组。
    • \n
    • 第二个元素是 locale 信息设置。
    • \n
    \n

    default 参数的示例:

    \n
    const defaultLocaleInfo = [\n  [\n    ['en'],\n    { title: 'VuePress', description: 'Vue-powered Static Site Generator' },\n  ],\n  [\n    ['zh', 'zh-CN'],\n    { title: 'VuePress', description: 'Vue 驱动的静态网站生成器' },\n  ],\n  [['zh-TW'], { title: 'VuePress', description: 'Vue 驅動的靜態網站生成器' }],\n]
    \n
  • \n
  • \n

    config 参数是用户 locale 配置,是可选的。

    \n

    它应该是一个以 localePath 为键,以部分 locale 信息设置为值的对象。

    \n

    config 参数的示例:

    \n
    const userLocaleConfig = {\n  '/zh/': { description: '由 Vue 驱动的静态网站生成器' },\n  '/zh-TW/': { description: '由 Vue 驅動的靜態網站生成器' },\n}
    \n
  • \n
  • \n

    name 参数是插件名称,是可选的,仅用于日志记录。

    \n
  • \n
\n

函数将自动合并默认的 locale 信息和用户 locale 配置,并返回最终的 locale 配置,其中用户 locale 配置将覆盖默认的 locale 信息。

\n

默认的 locale 信息将根据站点配置中每个 locale 的当前语言选择,当 locale 的语言代码在默认的 locale 信息中找不到时,它将回退到以下存在的第一个:

\n
    \n
  • en-US 的 locale 信息
  • \n
  • en 的 locale 信息
  • \n
  • 默认 locale 信息的第一个元素
  • \n
\n", + "date_modified": "2025-01-10T18:07:54.000Z", + "authors": [], + "tags": [] + }, + { + "title": "页面相关", + "url": "https://ecosystem.vuejs.press/ecosystem/zh/tools/helper/node/page.html", + "id": "https://ecosystem.vuejs.press/ecosystem/zh/tools/helper/node/page.html", + "summary": "页面相关 页面常见信息生成器。 这些函数仅在 @vuepress/helper 中可用。 getPageExcerpt 获取页面摘要。 getPageText 获取页面纯文本。", + "content_html": "\n

页面常见信息生成器。

\n

这些函数仅在 @vuepress/helper 中可用。

\n

getPageExcerpt

\n

获取页面摘要。

\n
export interface PageExcerptOptions {\n  /**\n   * 摘要分隔符\n   *\n   * @default \"<!-- more -->\"\n   */\n  separator?: string\n\n  /**\n   * 摘要的长度\n   *\n   * @description 摘要的长度会尽可能的接近这个值\n   *\n   * @default 300\n   */\n  length?: number\n\n  /**\n   * 被认为是自定义元素的标签\n   *\n   * @description 用于判断一个标签是否是自定义元素,因为在摘要中,所有的未知标签都会被移除。\n   */\n  isCustomElement?: (tagName: string) => boolean\n\n  /**\n   * 是否保留页面标题 (第一个 h1)\n   *\n   * @default false\n   */\n  keepPageTitle?: boolean\n\n  /**\n   * 是否保留代码块的标签,诸如行号和高亮行\n   *\n   * @default false\n   */\n  keepFenceDom?: boolean\n}\n\nexport const getPageExcerpt: (\n  app: App,\n  page: Page,\n  options?: PageExcerptOptions,\n) => string
\n

getPageText

\n

获取页面纯文本。

\n
export interface PageTextOptions {\n  /**\n   * 是否将文字转换成单行内容\n   *\n   * @default false\n   */\n  singleLine?: boolean\n\n  /**\n   * 文字的长度\n   *\n   * @description 文字的长度会尽可能的接近这个值\n   *\n   * @default 300\n   */\n  length?: number\n\n  /**\n   * 需要移除的标签\n   *\n   * @description 默认情况下表格和代码块会被移除\n   *\n   * @default ['table', 'pre']\n   */\n  removedTags?: string[]\n}\n\nexport const getPageText: (\n  app: App,\n  page: Page,\n  options?: PageTextOptions,\n) => string
\n
", + "date_modified": "2025-01-10T18:07:54.000Z", + "authors": [], + "tags": [] + }, + { + "title": "Artalk", + "url": "https://ecosystem.vuejs.press/ecosystem/zh/plugins/blog/comment/artalk/", + "id": "https://ecosystem.vuejs.press/ecosystem/zh/plugins/blog/comment/artalk/", + "summary": "Artalk Artalk 是一款简洁的自托管评论系统,你可以在服务器上轻松部署并置入前端页面中。 来到你的博客,或是任意位置,放置 Artalk 评论框,让页面具备丰富的社会化功能。 安装 部署 Artalk 服务端 请参见 Artalk 文档。 配置 请配置 provider: \"Artalk\" 并将你的服务端地址传入插件选项中的 server。 ...", + "content_html": "\n

Artalk 是一款简洁的自托管评论系统,你可以在服务器上轻松部署并置入前端页面中。

\n

来到你的博客,或是任意位置,放置 Artalk 评论框,让页面具备丰富的社会化功能。

\n\n

安装

\n
npm i -D artalk
\n

部署 Artalk 服务端

\n

请参见 Artalk 文档

\n

配置

\n

请配置 provider: \"Artalk\" 并将你的服务端地址传入插件选项中的 server

\n

其他的配置项详见 Artalk 配置

\n
\n

提示

\n

插件保留 el 选项在页面自行插入 Artalk。同时插件会自动根据 VuePress 信息为你自动设置 pageTitle, pageKeysite 选项。

\n
\n

夜间模式

\n

为了能使 Artalk 应用正确的主题,你需要通过 darkmode 属性向 <CommentService /> 传入一个布尔值,代表当前是否开启夜间模式。

\n", + "date_modified": "2025-01-10T18:07:54.000Z", + "authors": [], + "tags": [] + }, + { + "title": "Artalk 选项", + "url": "https://ecosystem.vuejs.press/ecosystem/zh/plugins/blog/comment/artalk/config.html", + "id": "https://ecosystem.vuejs.press/ecosystem/zh/plugins/blog/comment/artalk/config.html", + "summary": "Artalk 选项 配置 详见 Artalk 配置。 el pageTitle, pageKey 和 site 选项为插件的保留选项,将从 VuePress 配置中自动推断,不可设置。 imgUploader 和 avatarURLBuilder 这两个函数选项只能在客户端配置。 插件配置 你可以直接在插件选项中配置可序列化的选项: 客户端配置 你可以...", + "content_html": "\n

配置

\n

详见 Artalk 配置

\n
    \n
  • \n

    el pageTitle, pageKeysite 选项为插件的保留选项,将从 VuePress 配置中自动推断,不可设置。

    \n
  • \n
  • \n

    imgUploaderavatarURLBuilder 这两个函数选项只能在客户端配置。

    \n
  • \n
\n

插件配置

\n

你可以直接在插件选项中配置可序列化的选项:

\n
import { commentPlugin } from '@vuepress/plugin-comment'\nimport { defineUserConfig } from 'vuepress'\n\nexport default defineUserConfig({\n  plugins: [\n    commentPlugin({\n      provider: 'Artalk',\n      // 其他选项\n      // ...\n    }),\n  ],\n})
\n

客户端配置

\n

你可以使用 defineArtalkConfig 函数来配置 Artalk。

\n
import { defineArtalkConfig } from '@vuepress/plugin-comment/client'\nimport { defineClientConfig } from 'vuepress/client'\n\ndefineArtalkConfig({\n  // Artalk 选项\n})
\n
", + "date_modified": "2024-08-20T15:23:47.000Z", + "authors": [], + "tags": [] + }, + { + "title": "Giscus", + "url": "https://ecosystem.vuejs.press/ecosystem/zh/plugins/blog/comment/giscus/", + "id": "https://ecosystem.vuejs.press/ecosystem/zh/plugins/blog/comment/giscus/", + "summary": "Giscus Giscus 是一个基于 GitHub Discussion 的评论系统,启用简便。 准备工作 你需要创建一个公开仓库,并开启评论区,以作为评论存放的地点 你需要安装 Giscus App,使其有权限访问对应仓库。 在完成以上步骤后,请前往 Giscus 页面 获得你的设置。 你只需要填写仓库和 Discussion 分类,之后滚动到页面...", + "content_html": "\n

Giscus 是一个基于 GitHub Discussion 的评论系统,启用简便。

\n\n

准备工作

\n
    \n
  1. \n

    你需要创建一个公开仓库,并开启评论区,以作为评论存放的地点

    \n
  2. \n
  3. \n

    你需要安装 Giscus App,使其有权限访问对应仓库。

    \n
  4. \n
  5. \n

    在完成以上步骤后,请前往 Giscus 页面 获得你的设置。

    \n

    你只需要填写仓库和 Discussion 分类,之后滚动到页面下部的 “启用 giscus” 部分,获取 data-repo, data-repo-id, data-categorydata-category-id 这四个属性。

    \n
  6. \n
\n

配置

\n

请配置 provider: \"Giscus\" 并将 data-repo, data-repo-id, data-categorydata-category-id 作为插件选项传入 repo, repoId, category categoryId

\n

其他的配置项详见 Giscus 配置

\n

主题

\n

默认情况下,Giscus 使用 lightdark 主题 (基于夜间模式状态)。

\n
\n

夜间模式

\n

为了能使 Giscus 应用正确的主题,你需要为 <CommentService /> 通过 darkmode 属性传入一个布尔值,代表当前是否开启夜间模式。

\n
\n

如果你想在日间模式和夜间模式下自定义主题,你可以设置 lightThemedarkTheme 选项,使用内置主题关键字或以 https:// 开头的自定义 css 链接。

\n", + "date_modified": "2025-01-10T18:07:54.000Z", + "authors": [], + "tags": [] + } + ] +} \ No newline at end of file diff --git a/zh/index.html b/zh/index.html new file mode 100644 index 0000000000..e4512d9732 --- /dev/null +++ b/zh/index.html @@ -0,0 +1,43 @@ + + + + + + + + + 首页 | VuePress 生态系统 + + + + + + + + + diff --git a/zh/plugins/analytics/baidu-analytics.html b/zh/plugins/analytics/baidu-analytics.html new file mode 100644 index 0000000000..480df91f91 --- /dev/null +++ b/zh/plugins/analytics/baidu-analytics.html @@ -0,0 +1,51 @@ + + + + + + + + + baidu-analytics | VuePress 生态系统 + + + + + +
+ + + diff --git a/zh/plugins/analytics/google-analytics.html b/zh/plugins/analytics/google-analytics.html new file mode 100644 index 0000000000..d83f57090e --- /dev/null +++ b/zh/plugins/analytics/google-analytics.html @@ -0,0 +1,64 @@ + + + + + + + + + google-analytics | VuePress 生态系统 + + + + + +
+ + + diff --git a/zh/plugins/analytics/index.html b/zh/plugins/analytics/index.html new file mode 100644 index 0000000000..054e9e9718 --- /dev/null +++ b/zh/plugins/analytics/index.html @@ -0,0 +1,43 @@ + + + + + + + + + 统计分析插件 | VuePress 生态系统 + + + + + + + + + diff --git a/zh/plugins/analytics/umami-analytics.html b/zh/plugins/analytics/umami-analytics.html new file mode 100644 index 0000000000..684d6be673 --- /dev/null +++ b/zh/plugins/analytics/umami-analytics.html @@ -0,0 +1,51 @@ + + + + + + + + + umami-analytics | VuePress 生态系统 + + + + + +
+ + + diff --git a/zh/plugins/blog/blog/config.html b/zh/plugins/blog/blog/config.html new file mode 100644 index 0000000000..79f1aaaedf --- /dev/null +++ b/zh/plugins/blog/blog/config.html @@ -0,0 +1,180 @@ + + + + + + + + + 配置 | VuePress 生态系统 + + + + + +
+ + + diff --git a/zh/plugins/blog/blog/guide.html b/zh/plugins/blog/blog/guide.html new file mode 100644 index 0000000000..a4c105cff9 --- /dev/null +++ b/zh/plugins/blog/blog/guide.html @@ -0,0 +1,244 @@ + + + + + + + + + 指南 | VuePress 生态系统 + + + + + +
+ + + diff --git a/zh/plugins/blog/blog/index.html b/zh/plugins/blog/blog/index.html new file mode 100644 index 0000000000..3290f7ed11 --- /dev/null +++ b/zh/plugins/blog/blog/index.html @@ -0,0 +1,51 @@ + + + + + + + + + blog | VuePress 生态系统 + + + + + + + + + diff --git a/zh/plugins/blog/comment/artalk/config.html b/zh/plugins/blog/comment/artalk/config.html new file mode 100644 index 0000000000..386494f392 --- /dev/null +++ b/zh/plugins/blog/comment/artalk/config.html @@ -0,0 +1,59 @@ + + + + + + + + + Artalk 选项 | VuePress 生态系统 + + + + + +
+ + + diff --git a/zh/plugins/blog/comment/artalk/index.html b/zh/plugins/blog/comment/artalk/index.html new file mode 100644 index 0000000000..dfdc8ef0aa --- /dev/null +++ b/zh/plugins/blog/comment/artalk/index.html @@ -0,0 +1,43 @@ + + + + + + + + + Artalk | VuePress 生态系统 + + + + + +
+ + + diff --git a/zh/plugins/blog/comment/giscus/config.html b/zh/plugins/blog/comment/giscus/config.html new file mode 100644 index 0000000000..2400b5eb66 --- /dev/null +++ b/zh/plugins/blog/comment/giscus/config.html @@ -0,0 +1,79 @@ + + + + + + + + + Giscus 选项 | VuePress 生态系统 + + + + + +
+ + + diff --git a/zh/plugins/blog/comment/giscus/index.html b/zh/plugins/blog/comment/giscus/index.html new file mode 100644 index 0000000000..2a453264c9 --- /dev/null +++ b/zh/plugins/blog/comment/giscus/index.html @@ -0,0 +1,43 @@ + + + + + + + + + Giscus | VuePress 生态系统 + + + + + +
+ + + diff --git a/zh/plugins/blog/comment/guide.html b/zh/plugins/blog/comment/guide.html new file mode 100644 index 0000000000..b5f4c6272c --- /dev/null +++ b/zh/plugins/blog/comment/guide.html @@ -0,0 +1,65 @@ + + + + + + + + + 指南 | VuePress 生态系统 + + + + + +
+ + + diff --git a/zh/plugins/blog/comment/index.html b/zh/plugins/blog/comment/index.html new file mode 100644 index 0000000000..c206ad5981 --- /dev/null +++ b/zh/plugins/blog/comment/index.html @@ -0,0 +1,51 @@ + + + + + + + + + comment | VuePress 生态系统 + + + + + + + + + diff --git a/zh/plugins/blog/comment/twikoo/config.html b/zh/plugins/blog/comment/twikoo/config.html new file mode 100644 index 0000000000..bf75033ead --- /dev/null +++ b/zh/plugins/blog/comment/twikoo/config.html @@ -0,0 +1,59 @@ + + + + + + + + + Twikoo 选项 | VuePress 生态系统 + + + + + +
+ + + diff --git a/zh/plugins/blog/comment/twikoo/index.html b/zh/plugins/blog/comment/twikoo/index.html new file mode 100644 index 0000000000..ffd5c6083d --- /dev/null +++ b/zh/plugins/blog/comment/twikoo/index.html @@ -0,0 +1,45 @@ + + + + + + + + + Twikoo | VuePress 生态系统 + + + + + +
+ + + diff --git a/zh/plugins/blog/comment/waline/config.html b/zh/plugins/blog/comment/waline/config.html new file mode 100644 index 0000000000..db24578aba --- /dev/null +++ b/zh/plugins/blog/comment/waline/config.html @@ -0,0 +1,134 @@ + + + + + + + + + Waline 选项 | VuePress 生态系统 + + + + + +
+ + + diff --git a/zh/plugins/blog/comment/waline/index.html b/zh/plugins/blog/comment/waline/index.html new file mode 100644 index 0000000000..1a007a25de --- /dev/null +++ b/zh/plugins/blog/comment/waline/index.html @@ -0,0 +1,43 @@ + + + + + + + + + Waline | VuePress 生态系统 + + + + + +
+ + + diff --git a/zh/plugins/blog/feed/channel.html b/zh/plugins/blog/feed/channel.html new file mode 100644 index 0000000000..e5a10a5429 --- /dev/null +++ b/zh/plugins/blog/feed/channel.html @@ -0,0 +1,56 @@ + + + + + + + + + 频道设置 | VuePress 生态系统 + + + + + +
+ + + diff --git a/zh/plugins/blog/feed/config.html b/zh/plugins/blog/feed/config.html new file mode 100644 index 0000000000..1cc6c50c0e --- /dev/null +++ b/zh/plugins/blog/feed/config.html @@ -0,0 +1,53 @@ + + + + + + + + + 插件配置 | VuePress 生态系统 + + + + + +
+ + + diff --git a/zh/plugins/blog/feed/frontmatter.html b/zh/plugins/blog/feed/frontmatter.html new file mode 100644 index 0000000000..3f47aeb4bc --- /dev/null +++ b/zh/plugins/blog/feed/frontmatter.html @@ -0,0 +1,91 @@ + + + + + + + + + Frontmatter 配置 | VuePress 生态系统 + + + + + +
+ + + diff --git a/zh/plugins/blog/feed/getter.html b/zh/plugins/blog/feed/getter.html new file mode 100644 index 0000000000..30653c1045 --- /dev/null +++ b/zh/plugins/blog/feed/getter.html @@ -0,0 +1,129 @@ + + + + + + + + + Feed 获取器 | VuePress 生态系统 + + + + + +
+ + + diff --git a/zh/plugins/blog/feed/guide.html b/zh/plugins/blog/feed/guide.html new file mode 100644 index 0000000000..5636ffd39c --- /dev/null +++ b/zh/plugins/blog/feed/guide.html @@ -0,0 +1,43 @@ + + + + + + + + + 指南 | VuePress 生态系统 + + + + + +
+ + + diff --git a/zh/plugins/blog/feed/index.html b/zh/plugins/blog/feed/index.html new file mode 100644 index 0000000000..7f88f8dba0 --- /dev/null +++ b/zh/plugins/blog/feed/index.html @@ -0,0 +1,51 @@ + + + + + + + + + feed | VuePress 生态系统 + + + + + + + + + diff --git a/zh/plugins/blog/index.html b/zh/plugins/blog/index.html new file mode 100644 index 0000000000..d23bfa6ca0 --- /dev/null +++ b/zh/plugins/blog/index.html @@ -0,0 +1,43 @@ + + + + + + + + + 博客插件 | VuePress 生态系统 + + + + + + + + + diff --git a/zh/plugins/development/active-header-links.html b/zh/plugins/development/active-header-links.html new file mode 100644 index 0000000000..8821f93b21 --- /dev/null +++ b/zh/plugins/development/active-header-links.html @@ -0,0 +1,51 @@ + + + + + + + + + active-header-links | VuePress 生态系统 + + + + + +
+ + + diff --git a/zh/plugins/development/git.html b/zh/plugins/development/git.html new file mode 100644 index 0000000000..98a1e9539d --- /dev/null +++ b/zh/plugins/development/git.html @@ -0,0 +1,195 @@ + + + + + + + + + git | VuePress 生态系统 + + + + + +
+ + + diff --git a/zh/plugins/development/index.html b/zh/plugins/development/index.html new file mode 100644 index 0000000000..4dec505e9e --- /dev/null +++ b/zh/plugins/development/index.html @@ -0,0 +1,43 @@ + + + + + + + + + 开发插件 | VuePress 生态系统 + + + + + + + + + diff --git a/zh/plugins/development/palette.html b/zh/plugins/development/palette.html new file mode 100644 index 0000000000..c5aa3ce7af --- /dev/null +++ b/zh/plugins/development/palette.html @@ -0,0 +1,74 @@ + + + + + + + + + palette | VuePress 生态系统 + + + + + +
+ + + diff --git a/zh/plugins/development/reading-time.html b/zh/plugins/development/reading-time.html new file mode 100644 index 0000000000..1900e374fd --- /dev/null +++ b/zh/plugins/development/reading-time.html @@ -0,0 +1,127 @@ + + + + + + + + + reading-time | VuePress 生态系统 + + + + + +
+ + + diff --git a/zh/plugins/development/rtl.html b/zh/plugins/development/rtl.html new file mode 100644 index 0000000000..633d2f7090 --- /dev/null +++ b/zh/plugins/development/rtl.html @@ -0,0 +1,56 @@ + + + + + + + + + rtl | VuePress 生态系统 + + + + + +
+ + + diff --git a/zh/plugins/development/sass-palette/config.html b/zh/plugins/development/sass-palette/config.html new file mode 100644 index 0000000000..9844d494f9 --- /dev/null +++ b/zh/plugins/development/sass-palette/config.html @@ -0,0 +1,43 @@ + + + + + + + + + 配置 | VuePress 生态系统 + + + + + +
+ + + diff --git a/zh/plugins/development/sass-palette/guide.html b/zh/plugins/development/sass-palette/guide.html new file mode 100644 index 0000000000..c13a29f86a --- /dev/null +++ b/zh/plugins/development/sass-palette/guide.html @@ -0,0 +1,97 @@ + + + + + + + + + 指南 | VuePress 生态系统 + + + + + +
+ + + diff --git a/zh/plugins/development/sass-palette/index.html b/zh/plugins/development/sass-palette/index.html new file mode 100644 index 0000000000..65198e615b --- /dev/null +++ b/zh/plugins/development/sass-palette/index.html @@ -0,0 +1,53 @@ + + + + + + + + + sass-palette | VuePress 生态系统 + + + + + +
+ + + diff --git a/zh/plugins/development/theme-data.html b/zh/plugins/development/theme-data.html new file mode 100644 index 0000000000..004665c804 --- /dev/null +++ b/zh/plugins/development/theme-data.html @@ -0,0 +1,88 @@ + + + + + + + + + theme-data | VuePress 生态系统 + + + + + +
+ + + diff --git a/zh/plugins/development/toc.html b/zh/plugins/development/toc.html new file mode 100644 index 0000000000..110cbf0612 --- /dev/null +++ b/zh/plugins/development/toc.html @@ -0,0 +1,108 @@ + + + + + + + + + toc | VuePress 生态系统 + + + + + +
+ + + diff --git a/zh/plugins/features/back-to-top.html b/zh/plugins/features/back-to-top.html new file mode 100644 index 0000000000..05c9848646 --- /dev/null +++ b/zh/plugins/features/back-to-top.html @@ -0,0 +1,55 @@ + + + + + + + + + back-to-top | VuePress 生态系统 + + + + + +
+ + + diff --git a/zh/plugins/features/catalog.html b/zh/plugins/features/catalog.html new file mode 100644 index 0000000000..8274cd46a5 --- /dev/null +++ b/zh/plugins/features/catalog.html @@ -0,0 +1,123 @@ + + + + + + + + + catalog | VuePress 生态系统 + + + + + +
+ + + diff --git a/zh/plugins/features/copy-code.html b/zh/plugins/features/copy-code.html new file mode 100644 index 0000000000..81f004eaa4 --- /dev/null +++ b/zh/plugins/features/copy-code.html @@ -0,0 +1,112 @@ + + + + + + + + + copy-code | VuePress 生态系统 + + + + + +
+ + + diff --git a/zh/plugins/features/copyright.html b/zh/plugins/features/copyright.html new file mode 100644 index 0000000000..7f868bfbf8 --- /dev/null +++ b/zh/plugins/features/copyright.html @@ -0,0 +1,104 @@ + + + + + + + + + copyright | VuePress 生态系统 + + + + + +
+ + + diff --git a/zh/plugins/features/icon.html b/zh/plugins/features/icon.html new file mode 100644 index 0000000000..bbdf07152e --- /dev/null +++ b/zh/plugins/features/icon.html @@ -0,0 +1,86 @@ + + + + + + + + + icon | VuePress 生态系统 + + + + + +
+ + + diff --git a/zh/plugins/features/index.html b/zh/plugins/features/index.html new file mode 100644 index 0000000000..362b1b259e --- /dev/null +++ b/zh/plugins/features/index.html @@ -0,0 +1,43 @@ + + + + + + + + + 功能插件 | VuePress 生态系统 + + + + + + + + + diff --git a/zh/plugins/features/medium-zoom.html b/zh/plugins/features/medium-zoom.html new file mode 100644 index 0000000000..1f96fa76a5 --- /dev/null +++ b/zh/plugins/features/medium-zoom.html @@ -0,0 +1,69 @@ + + + + + + + + + medium-zoom | VuePress 生态系统 + + + + + +
+ + + diff --git a/zh/plugins/features/notice.html b/zh/plugins/features/notice.html new file mode 100644 index 0000000000..66eb851c40 --- /dev/null +++ b/zh/plugins/features/notice.html @@ -0,0 +1,145 @@ + + + + + + + + + notice | VuePress 生态系统 + + + + + +
+ + + diff --git a/zh/plugins/features/nprogress.html b/zh/plugins/features/nprogress.html new file mode 100644 index 0000000000..d12868f1e5 --- /dev/null +++ b/zh/plugins/features/nprogress.html @@ -0,0 +1,50 @@ + + + + + + + + + nprogress | VuePress 生态系统 + + + + + +
+ + + diff --git a/zh/plugins/features/photo-swipe.html b/zh/plugins/features/photo-swipe.html new file mode 100644 index 0000000000..917a65e2db --- /dev/null +++ b/zh/plugins/features/photo-swipe.html @@ -0,0 +1,161 @@ + + + + + + + + + photo-swipe | VuePress 生态系统 + + + + + +
+ + + diff --git a/zh/plugins/features/watermark.html b/zh/plugins/features/watermark.html new file mode 100644 index 0000000000..fa6b45dea0 --- /dev/null +++ b/zh/plugins/features/watermark.html @@ -0,0 +1,76 @@ + + + + + + + + + watermark | VuePress 生态系统 + + + + + +
+ + + diff --git a/zh/plugins/index.html b/zh/plugins/index.html new file mode 100644 index 0000000000..3820e4e58b --- /dev/null +++ b/zh/plugins/index.html @@ -0,0 +1,43 @@ + + + + + + + + + 插件 | VuePress 生态系统 + + + + + + + + + diff --git a/zh/plugins/markdown/append-date.html b/zh/plugins/markdown/append-date.html new file mode 100644 index 0000000000..9372e0e836 --- /dev/null +++ b/zh/plugins/markdown/append-date.html @@ -0,0 +1,47 @@ + + + + + + + + + append-date | VuePress 生态系统 + + + + + +
+ + + diff --git a/zh/plugins/markdown/index.html b/zh/plugins/markdown/index.html new file mode 100644 index 0000000000..2d3fd55fb2 --- /dev/null +++ b/zh/plugins/markdown/index.html @@ -0,0 +1,43 @@ + + + + + + + + + Markdown 插件 | VuePress 生态系统 + + + + + + + + + diff --git a/zh/plugins/markdown/links-check.html b/zh/plugins/markdown/links-check.html new file mode 100644 index 0000000000..a378d09996 --- /dev/null +++ b/zh/plugins/markdown/links-check.html @@ -0,0 +1,66 @@ + + + + + + + + + links-check | VuePress 生态系统 + + + + + +
+ + + diff --git a/zh/plugins/markdown/markdown-container.html b/zh/plugins/markdown/markdown-container.html new file mode 100644 index 0000000000..0aacad9431 --- /dev/null +++ b/zh/plugins/markdown/markdown-container.html @@ -0,0 +1,74 @@ + + + + + + + + + markdown-container | VuePress 生态系统 + + + + + +
+ + + diff --git a/zh/plugins/markdown/markdown-ext.html b/zh/plugins/markdown/markdown-ext.html new file mode 100644 index 0000000000..6c5d58f920 --- /dev/null +++ b/zh/plugins/markdown/markdown-ext.html @@ -0,0 +1,98 @@ + + + + + + + + + markdown-ext | VuePress 生态系统 + + + + + +
+ + + diff --git a/zh/plugins/markdown/markdown-hint.html b/zh/plugins/markdown/markdown-hint.html new file mode 100644 index 0000000000..c90a17325a --- /dev/null +++ b/zh/plugins/markdown/markdown-hint.html @@ -0,0 +1,119 @@ + + + + + + + + + markdown-hint | VuePress 生态系统 + + + + + +
+ + + diff --git a/zh/plugins/markdown/markdown-image.html b/zh/plugins/markdown/markdown-image.html new file mode 100644 index 0000000000..20f12b75ca --- /dev/null +++ b/zh/plugins/markdown/markdown-image.html @@ -0,0 +1,82 @@ + + + + + + + + + markdown-image | VuePress 生态系统 + + + + + +
+ + + diff --git a/zh/plugins/markdown/markdown-include.html b/zh/plugins/markdown/markdown-include.html new file mode 100644 index 0000000000..74896a7950 --- /dev/null +++ b/zh/plugins/markdown/markdown-include.html @@ -0,0 +1,236 @@ + + + + + + + + + markdown-include | VuePress 生态系统 + + + + + +
+ + + diff --git a/zh/plugins/markdown/markdown-math.html b/zh/plugins/markdown/markdown-math.html new file mode 100644 index 0000000000..9a071e6193 --- /dev/null +++ b/zh/plugins/markdown/markdown-math.html @@ -0,0 +1,62 @@ + + + + + + + + + markdown-math | VuePress 生态系统 + + + + + +
+ + + diff --git a/zh/plugins/markdown/markdown-stylize.html b/zh/plugins/markdown/markdown-stylize.html new file mode 100644 index 0000000000..18a7d476a6 --- /dev/null +++ b/zh/plugins/markdown/markdown-stylize.html @@ -0,0 +1,111 @@ + + + + + + + + + markdown-stylize | VuePress 生态系统 + + + + + +
+ + + diff --git a/zh/plugins/markdown/markdown-tab.html b/zh/plugins/markdown/markdown-tab.html new file mode 100644 index 0000000000..f6ed897a63 --- /dev/null +++ b/zh/plugins/markdown/markdown-tab.html @@ -0,0 +1,226 @@ + + + + + + + + + markdown-tab | VuePress 生态系统 + + + + + +
+ + + diff --git a/zh/plugins/markdown/prismjs.html b/zh/plugins/markdown/prismjs.html new file mode 100644 index 0000000000..286b23399e --- /dev/null +++ b/zh/plugins/markdown/prismjs.html @@ -0,0 +1,342 @@ + + + + + + + + + prismjs | VuePress 生态系统 + + + + + +
+ + + diff --git a/zh/plugins/markdown/revealjs/demo.html b/zh/plugins/markdown/revealjs/demo.html new file mode 100644 index 0000000000..3a2eceb285 --- /dev/null +++ b/zh/plugins/markdown/revealjs/demo.html @@ -0,0 +1,43 @@ + + + + + + + + + 幻灯片演示 | VuePress 生态系统 + + + + + +
+ + + diff --git a/zh/plugins/markdown/revealjs/index.html b/zh/plugins/markdown/revealjs/index.html new file mode 100644 index 0000000000..cf08a22f7c --- /dev/null +++ b/zh/plugins/markdown/revealjs/index.html @@ -0,0 +1,111 @@ + + + + + + + + + revealjs | VuePress 生态系统 + + + + + +
+ + + diff --git a/zh/plugins/markdown/revealjs/themes.html b/zh/plugins/markdown/revealjs/themes.html new file mode 100644 index 0000000000..3db24bdd42 --- /dev/null +++ b/zh/plugins/markdown/revealjs/themes.html @@ -0,0 +1,43 @@ + + + + + + + + + 幻灯片主题 | VuePress 生态系统 + + + + + + + + + diff --git a/zh/plugins/markdown/shiki.html b/zh/plugins/markdown/shiki.html new file mode 100644 index 0000000000..7e781fa3d2 --- /dev/null +++ b/zh/plugins/markdown/shiki.html @@ -0,0 +1,343 @@ + + + + + + + + + shiki | VuePress 生态系统 + + + + + +
+ + + diff --git a/zh/plugins/pwa/index.html b/zh/plugins/pwa/index.html new file mode 100644 index 0000000000..2e2e8a94a0 --- /dev/null +++ b/zh/plugins/pwa/index.html @@ -0,0 +1,43 @@ + + + + + + + + + 渐进式应用插件 | VuePress 生态系统 + + + + + + + + + diff --git a/zh/plugins/pwa/pwa/config.html b/zh/plugins/pwa/pwa/config.html new file mode 100644 index 0000000000..699a72f807 --- /dev/null +++ b/zh/plugins/pwa/pwa/config.html @@ -0,0 +1,174 @@ + + + + + + + + + 配置 | VuePress 生态系统 + + + + + +
+ + + diff --git a/zh/plugins/pwa/pwa/guide.html b/zh/plugins/pwa/pwa/guide.html new file mode 100644 index 0000000000..89b1856811 --- /dev/null +++ b/zh/plugins/pwa/pwa/guide.html @@ -0,0 +1,63 @@ + + + + + + + + + 指南 | VuePress 生态系统 + + + + + +
+ + + diff --git a/zh/plugins/pwa/pwa/index.html b/zh/plugins/pwa/pwa/index.html new file mode 100644 index 0000000000..07ab50b36a --- /dev/null +++ b/zh/plugins/pwa/pwa/index.html @@ -0,0 +1,51 @@ + + + + + + + + + pwa | VuePress 生态系统 + + + + + + + + + diff --git a/zh/plugins/pwa/remove-pwa.html b/zh/plugins/pwa/remove-pwa.html new file mode 100644 index 0000000000..9a4f33d66d --- /dev/null +++ b/zh/plugins/pwa/remove-pwa.html @@ -0,0 +1,51 @@ + + + + + + + + + remove-pwa | VuePress 生态系统 + + + + + +
+ + + diff --git a/zh/plugins/search/docsearch.html b/zh/plugins/search/docsearch.html new file mode 100644 index 0000000000..c27452ec82 --- /dev/null +++ b/zh/plugins/search/docsearch.html @@ -0,0 +1,249 @@ + + + + + + + + + docsearch | VuePress 生态系统 + + + + + +
+ + + diff --git a/zh/plugins/search/guidelines.html b/zh/plugins/search/guidelines.html new file mode 100644 index 0000000000..c26618e55e --- /dev/null +++ b/zh/plugins/search/guidelines.html @@ -0,0 +1,43 @@ + + + + + + + + + 搜索插件指南 | VuePress 生态系统 + + + + + +
+ + + diff --git a/zh/plugins/search/index.html b/zh/plugins/search/index.html new file mode 100644 index 0000000000..d67dbf0f64 --- /dev/null +++ b/zh/plugins/search/index.html @@ -0,0 +1,43 @@ + + + + + + + + + 搜索插件 | VuePress 生态系统 + + + + + + + + + diff --git a/zh/plugins/search/search.html b/zh/plugins/search/search.html new file mode 100644 index 0000000000..d2673262f3 --- /dev/null +++ b/zh/plugins/search/search.html @@ -0,0 +1,113 @@ + + + + + + + + + search | VuePress 生态系统 + + + + + +
+ + + diff --git a/zh/plugins/search/slimsearch.html b/zh/plugins/search/slimsearch.html new file mode 100644 index 0000000000..e788d04d53 --- /dev/null +++ b/zh/plugins/search/slimsearch.html @@ -0,0 +1,336 @@ + + + + + + + + + slimsearch | VuePress 生态系统 + + + + + +
+ + + diff --git a/zh/plugins/seo/index.html b/zh/plugins/seo/index.html new file mode 100644 index 0000000000..13416205c1 --- /dev/null +++ b/zh/plugins/seo/index.html @@ -0,0 +1,43 @@ + + + + + + + + + 搜索引擎优化插件 | VuePress 生态系统 + + + + + + + + + diff --git a/zh/plugins/seo/seo/config.html b/zh/plugins/seo/seo/config.html new file mode 100644 index 0000000000..252989add2 --- /dev/null +++ b/zh/plugins/seo/seo/config.html @@ -0,0 +1,83 @@ + + + + + + + + + 选项 | VuePress 生态系统 + + + + + +
+ + + diff --git a/zh/plugins/seo/seo/guide.html b/zh/plugins/seo/seo/guide.html new file mode 100644 index 0000000000..875a4fefb0 --- /dev/null +++ b/zh/plugins/seo/seo/guide.html @@ -0,0 +1,74 @@ + + + + + + + + + 指南 | VuePress 生态系统 + + + + + +
+ + + diff --git a/zh/plugins/seo/seo/index.html b/zh/plugins/seo/seo/index.html new file mode 100644 index 0000000000..d221ff61cc --- /dev/null +++ b/zh/plugins/seo/seo/index.html @@ -0,0 +1,51 @@ + + + + + + + + + seo | VuePress 生态系统 + + + + + + + + + diff --git a/zh/plugins/seo/sitemap/config.html b/zh/plugins/seo/sitemap/config.html new file mode 100644 index 0000000000..155bbd7d1a --- /dev/null +++ b/zh/plugins/seo/sitemap/config.html @@ -0,0 +1,43 @@ + + + + + + + + + 配置 | VuePress 生态系统 + + + + + +
+ + + diff --git a/zh/plugins/seo/sitemap/frontmatter.html b/zh/plugins/seo/sitemap/frontmatter.html new file mode 100644 index 0000000000..b2dc7a65fc --- /dev/null +++ b/zh/plugins/seo/sitemap/frontmatter.html @@ -0,0 +1,43 @@ + + + + + + + + + Frontmatter | VuePress 生态系统 + + + + + +
+ + + diff --git a/zh/plugins/seo/sitemap/guide.html b/zh/plugins/seo/sitemap/guide.html new file mode 100644 index 0000000000..9d11d9c223 --- /dev/null +++ b/zh/plugins/seo/sitemap/guide.html @@ -0,0 +1,49 @@ + + + + + + + + + 指南 | VuePress 生态系统 + + + + + +
+ + + diff --git a/zh/plugins/seo/sitemap/index.html b/zh/plugins/seo/sitemap/index.html new file mode 100644 index 0000000000..ccb5529a63 --- /dev/null +++ b/zh/plugins/seo/sitemap/index.html @@ -0,0 +1,51 @@ + + + + + + + + + sitemap | VuePress 生态系统 + + + + + + + + + diff --git a/zh/plugins/tools/cache.html b/zh/plugins/tools/cache.html new file mode 100644 index 0000000000..c562212a7f --- /dev/null +++ b/zh/plugins/tools/cache.html @@ -0,0 +1,54 @@ + + + + + + + + + cache | VuePress 生态系统 + + + + + +
+ + + diff --git a/zh/plugins/tools/google-tag-manager.html b/zh/plugins/tools/google-tag-manager.html new file mode 100644 index 0000000000..c34ff22b1e --- /dev/null +++ b/zh/plugins/tools/google-tag-manager.html @@ -0,0 +1,66 @@ + + + + + + + + + google-tag-manager | VuePress 生态系统 + + + + + +
+ + + diff --git a/zh/plugins/tools/index.html b/zh/plugins/tools/index.html new file mode 100644 index 0000000000..76ce51a606 --- /dev/null +++ b/zh/plugins/tools/index.html @@ -0,0 +1,43 @@ + + + + + + + + + 工具插件 | VuePress 生态系统 + + + + + + + + + diff --git a/zh/plugins/tools/redirect.html b/zh/plugins/tools/redirect.html new file mode 100644 index 0000000000..5886ca8362 --- /dev/null +++ b/zh/plugins/tools/redirect.html @@ -0,0 +1,151 @@ + + + + + + + + + redirect | VuePress 生态系统 + + + + + +
+ + + diff --git a/zh/plugins/tools/register-components.html b/zh/plugins/tools/register-components.html new file mode 100644 index 0000000000..21e66d4ebb --- /dev/null +++ b/zh/plugins/tools/register-components.html @@ -0,0 +1,85 @@ + + + + + + + + + register-components | VuePress 生态系统 + + + + + +
+ + + diff --git a/zh/rss.xml b/zh/rss.xml new file mode 100644 index 0000000000..30ec91b1e0 --- /dev/null +++ b/zh/rss.xml @@ -0,0 +1,5170 @@ + + + + + VuePress 生态系统 + https://ecosystem.vuejs.press/ecosystem/zh/ + VuePress 官方主题和插件 + zh-CN + Fri, 10 Jan 2025 18:17:55 GMT + Fri, 10 Jan 2025 18:17:55 GMT + @vuepress/plugin-feed + https://validator.w3.org/feed/docs/rss2.html + + 插件 + https://ecosystem.vuejs.press/ecosystem/zh/plugins/ + https://ecosystem.vuejs.press/ecosystem/zh/plugins/ + 插件 + 插件 + + + + 主题 + https://ecosystem.vuejs.press/ecosystem/zh/themes/ + https://ecosystem.vuejs.press/ecosystem/zh/themes/ + 主题 + 主题 + + + + 主题指南 + https://ecosystem.vuejs.press/ecosystem/zh/themes/guidelines.html + https://ecosystem.vuejs.press/ecosystem/zh/themes/guidelines.html + 主题指南 + 主题指南 为了避免主题开发者和用户设置不必要的选项,我们制定了一套主题创建时应遵循的指南。 DOM 结构 一个主题必须实现以下 DOM 结构: 容器:一个包含整个主题的元素。此元素应该有一个 vp-container 属性。 内容:一个包含 markdown 渲染结果的元素。此元素应该有一个 vp-content 属性。 一个主题可以有以下可选元素: ... + 为了避免主题开发者和用户设置不必要的选项,我们制定了一套主题创建时应遵循的指南。

+

DOM 结构

+

一个主题必须实现以下 DOM 结构:

+
    +
  • 容器:一个包含整个主题的元素。此元素应该有一个 vp-container 属性。
  • +
  • 内容:一个包含 markdown 渲染结果的元素。此元素应该有一个 vp-content 属性。
  • +
+

一个主题可以有以下可选元素:

+
    +
  • 导航栏:站点的导航栏。此元素应该有一个 vp-navbar 属性。
  • +
  • 侧边栏:站点的侧边栏。此元素应该有一个 vp-sidebar 属性。
  • +
  • 大纲:主要内容的标题或大纲。此元素应该有一个 vp-outline 属性。
  • +
  • 评论:评论服务(评论框和评论列表)。此元素应该有一个 vp-comment 属性。
  • +
  • 页脚:站点的页脚。此元素应该有一个 vp-footer 属性。
  • +
+

一个主题必须:

+
    +
  • 在暗色模式下,将 html 元素的 data-theme 设置为 dark
  • +
  • 在亮色模式下,将 html 元素的 data-theme 设置为 light
  • +
+

如果主题只有一种颜色方案,主题仍然需要将 data-theme 设置为 lightdark,以指示默认颜色方案。

+

组件

+

为了支持搜索插件,主题应检查 <SearchBox /> 是否已全局注册,并在其自己的导航栏或侧边栏中呈现(如果可用)。

+

颜色变量

+

一个主题必须实现以下颜色变量:

+

文字

+
    +
  • --vp-c-text:默认文本颜色。
  • +
  • --vp-c-text-mute:用于静音文本的颜色,例如“非活动菜单”或“信息文本”。
  • +
  • --vp-c-text-subtle:用于细微文本的颜色,例如“占位符”或“插入符号”。
  • +
+

背景

+
    +
  • --vp-c-bg:用于主屏幕的背景颜色。
  • +
  • --vp-c-bg-alt:用于“侧边栏”或“代码块”等地方的备用背景颜色。
  • +
  • --vp-c-bg-elv:用于“浮动”部分的提升背景颜色,例如“对话框”。
  • +
+

阴影

+
    +
  • --vp-c-shadow:阴影颜色
  • +
+

强调

+

用于交互组件的强调颜色和品牌颜色。

+
    +
  • +

    --vp-c-accent:主要用于彩色文本的最实色。它必须满足与放在 --vp-c-accent-soft 顶部时的对比度。

    +
  • +
  • +

    --vp-c-accent-hover:用于悬停状态的颜色。

    +
  • +
  • +

    --vp-c-accent-bg:用于实色背景的颜色。它必须满足与放在其顶部的 --vp-c-accent-text 的对比度。

    +
  • +
  • +

    --vp-c-accent-text:用于 --vp-c-accent-bg 背景的文本颜色。它必须满足与 --vp-c-accent-bg 的对比度。

    +
  • +
  • +

    --vp-c-accent-soft:用于自定义容器或徽章等细微背景的颜色。当将 --vp-c-accent 颜色放在其顶部时,它必须满足对比度。

    +

    软色必须是半透明的 alpha 通道。这是至关重要的,因为它允许将多个“软”颜色叠加在一起以创建强调,例如在自定义容器内部有内联代码块时。

    +
  • +
+

边框

+
    +
  • --vp-c-border:交互组件的边框颜色。例如,这应该用于按钮轮廓。
  • +
  • --vp-c-border-hard:较暗的边框颜色,用于紧贴文本的“硬”边框,例如表格和 kbd。
  • +
  • --vp-c-divider:分隔符的颜色,用于在同一组件内分隔部分,例如在“h2”标题上放置分隔符。
  • +
+

控件

+
    +
  • --vp-c-control:用于交互控件(例如按钮或复选框)的背景颜色。
  • +
  • --vp-c-control-hover:用于交互控件悬停状态的背景颜色。
  • +
  • --vp-c-control-disabled:用于交互控件禁用状态的颜色。
  • +
+

过渡时间

+
    +
  • --vp-t-color:颜色过渡时间。
  • +
  • --vp-t-transform:变换过渡时间。
  • +
+

案例

+]]>
+
+ + 工具包 + https://ecosystem.vuejs.press/ecosystem/zh/tools/ + https://ecosystem.vuejs.press/ecosystem/zh/tools/ + 工具包 + 工具包 + + + + 统计分析插件 + https://ecosystem.vuejs.press/ecosystem/zh/plugins/analytics/ + https://ecosystem.vuejs.press/ecosystem/zh/plugins/analytics/ + 统计分析插件 + 统计分析插件 + + + + baidu-analytics + https://ecosystem.vuejs.press/ecosystem/zh/plugins/analytics/baidu-analytics.html + https://ecosystem.vuejs.press/ecosystem/zh/plugins/analytics/baidu-analytics.html + baidu-analytics + baidu-analytics + + + + google-analytics + https://ecosystem.vuejs.press/ecosystem/zh/plugins/analytics/google-analytics.html + https://ecosystem.vuejs.press/ecosystem/zh/plugins/analytics/google-analytics.html + google-analytics + google-analytics + + + + umami-analytics + https://ecosystem.vuejs.press/ecosystem/zh/plugins/analytics/umami-analytics.html + https://ecosystem.vuejs.press/ecosystem/zh/plugins/analytics/umami-analytics.html + umami-analytics + umami-analytics + + + + 博客插件 + https://ecosystem.vuejs.press/ecosystem/zh/plugins/blog/ + https://ecosystem.vuejs.press/ecosystem/zh/plugins/blog/ + 博客插件 + 博客插件 + + + + 开发插件 + https://ecosystem.vuejs.press/ecosystem/zh/plugins/development/ + https://ecosystem.vuejs.press/ecosystem/zh/plugins/development/ + 开发插件 + 开发插件 + + + + active-header-links + https://ecosystem.vuejs.press/ecosystem/zh/plugins/development/active-header-links.html + https://ecosystem.vuejs.press/ecosystem/zh/plugins/development/active-header-links.html + active-header-links + active-header-links + + + + git + https://ecosystem.vuejs.press/ecosystem/zh/plugins/development/git.html + https://ecosystem.vuejs.press/ecosystem/zh/plugins/development/git.html + git + git + + + + palette + https://ecosystem.vuejs.press/ecosystem/zh/plugins/development/palette.html + https://ecosystem.vuejs.press/ecosystem/zh/plugins/development/palette.html + palette + palette + + + + reading-time + https://ecosystem.vuejs.press/ecosystem/zh/plugins/development/reading-time.html + https://ecosystem.vuejs.press/ecosystem/zh/plugins/development/reading-time.html + reading-time + reading-time + + + + rtl + https://ecosystem.vuejs.press/ecosystem/zh/plugins/development/rtl.html + https://ecosystem.vuejs.press/ecosystem/zh/plugins/development/rtl.html + rtl + rtl + + + + theme-data + https://ecosystem.vuejs.press/ecosystem/zh/plugins/development/theme-data.html + https://ecosystem.vuejs.press/ecosystem/zh/plugins/development/theme-data.html + theme-data + theme-data + + + + toc + https://ecosystem.vuejs.press/ecosystem/zh/plugins/development/toc.html + https://ecosystem.vuejs.press/ecosystem/zh/plugins/development/toc.html + toc + + + + 功能插件 + https://ecosystem.vuejs.press/ecosystem/zh/plugins/features/ + https://ecosystem.vuejs.press/ecosystem/zh/plugins/features/ + 功能插件 + 功能插件 + + + + back-to-top + https://ecosystem.vuejs.press/ecosystem/zh/plugins/features/back-to-top.html + https://ecosystem.vuejs.press/ecosystem/zh/plugins/features/back-to-top.html + back-to-top + back-to-top + + + + catalog + https://ecosystem.vuejs.press/ecosystem/zh/plugins/features/catalog.html + https://ecosystem.vuejs.press/ecosystem/zh/plugins/features/catalog.html + catalog + catalog + + + + copy-code + https://ecosystem.vuejs.press/ecosystem/zh/plugins/features/copy-code.html + https://ecosystem.vuejs.press/ecosystem/zh/plugins/features/copy-code.html + copy-code + copy-code + + + + copyright + https://ecosystem.vuejs.press/ecosystem/zh/plugins/features/copyright.html + https://ecosystem.vuejs.press/ecosystem/zh/plugins/features/copyright.html + copyright + copyright + + + + icon + https://ecosystem.vuejs.press/ecosystem/zh/plugins/features/icon.html + https://ecosystem.vuejs.press/ecosystem/zh/plugins/features/icon.html + icon + icon + + + + medium-zoom + https://ecosystem.vuejs.press/ecosystem/zh/plugins/features/medium-zoom.html + https://ecosystem.vuejs.press/ecosystem/zh/plugins/features/medium-zoom.html + medium-zoom + medium-zoom + + + + notice + https://ecosystem.vuejs.press/ecosystem/zh/plugins/features/notice.html + https://ecosystem.vuejs.press/ecosystem/zh/plugins/features/notice.html + notice + notice + + + + nprogress + https://ecosystem.vuejs.press/ecosystem/zh/plugins/features/nprogress.html + https://ecosystem.vuejs.press/ecosystem/zh/plugins/features/nprogress.html + nprogress + nprogress + + + + photo-swipe + https://ecosystem.vuejs.press/ecosystem/zh/plugins/features/photo-swipe.html + https://ecosystem.vuejs.press/ecosystem/zh/plugins/features/photo-swipe.html + photo-swipe + photo-swipe + + + + watermark + https://ecosystem.vuejs.press/ecosystem/zh/plugins/features/watermark.html + https://ecosystem.vuejs.press/ecosystem/zh/plugins/features/watermark.html + watermark + watermark + + + + Markdown 插件 + https://ecosystem.vuejs.press/ecosystem/zh/plugins/markdown/ + https://ecosystem.vuejs.press/ecosystem/zh/plugins/markdown/ + Markdown 插件 + Markdown 插件 + + + + append-date + https://ecosystem.vuejs.press/ecosystem/zh/plugins/markdown/append-date.html + https://ecosystem.vuejs.press/ecosystem/zh/plugins/markdown/append-date.html + append-date + append-date + + + + links-check + https://ecosystem.vuejs.press/ecosystem/zh/plugins/markdown/links-check.html + https://ecosystem.vuejs.press/ecosystem/zh/plugins/markdown/links-check.html + links-check + links-check + + + + markdown-container + https://ecosystem.vuejs.press/ecosystem/zh/plugins/markdown/markdown-container.html + https://ecosystem.vuejs.press/ecosystem/zh/plugins/markdown/markdown-container.html + markdown-container + markdown-container + + + + markdown-ext + https://ecosystem.vuejs.press/ecosystem/zh/plugins/markdown/markdown-ext.html + https://ecosystem.vuejs.press/ecosystem/zh/plugins/markdown/markdown-ext.html + markdown-ext + markdown-ext + + + + markdown-hint + https://ecosystem.vuejs.press/ecosystem/zh/plugins/markdown/markdown-hint.html + https://ecosystem.vuejs.press/ecosystem/zh/plugins/markdown/markdown-hint.html + markdown-hint + markdown-hint + + + + markdown-image + https://ecosystem.vuejs.press/ecosystem/zh/plugins/markdown/markdown-image.html + https://ecosystem.vuejs.press/ecosystem/zh/plugins/markdown/markdown-image.html + markdown-image + markdown-image + + + + + markdown-include + https://ecosystem.vuejs.press/ecosystem/zh/plugins/markdown/markdown-include.html + https://ecosystem.vuejs.press/ecosystem/zh/plugins/markdown/markdown-include.html + markdown-include + markdown-include + + + + markdown-math + https://ecosystem.vuejs.press/ecosystem/zh/plugins/markdown/markdown-math.html + https://ecosystem.vuejs.press/ecosystem/zh/plugins/markdown/markdown-math.html + markdown-math + markdown-math + + + + markdown-stylize + https://ecosystem.vuejs.press/ecosystem/zh/plugins/markdown/markdown-stylize.html + https://ecosystem.vuejs.press/ecosystem/zh/plugins/markdown/markdown-stylize.html + markdown-stylize + markdown-stylize + + + + markdown-tab + https://ecosystem.vuejs.press/ecosystem/zh/plugins/markdown/markdown-tab.html + https://ecosystem.vuejs.press/ecosystem/zh/plugins/markdown/markdown-tab.html + markdown-tab + markdown-tab + + + + prismjs + https://ecosystem.vuejs.press/ecosystem/zh/plugins/markdown/prismjs.html + https://ecosystem.vuejs.press/ecosystem/zh/plugins/markdown/prismjs.html + prismjs + prismjs + + + + shiki + https://ecosystem.vuejs.press/ecosystem/zh/plugins/markdown/shiki.html + https://ecosystem.vuejs.press/ecosystem/zh/plugins/markdown/shiki.html + shiki + shiki + + + + 渐进式应用插件 + https://ecosystem.vuejs.press/ecosystem/zh/plugins/pwa/ + https://ecosystem.vuejs.press/ecosystem/zh/plugins/pwa/ + 渐进式应用插件 + 渐进式应用插件 + + + + remove-pwa + https://ecosystem.vuejs.press/ecosystem/zh/plugins/pwa/remove-pwa.html + https://ecosystem.vuejs.press/ecosystem/zh/plugins/pwa/remove-pwa.html + remove-pwa + remove-pwa + + + + 搜索插件 + https://ecosystem.vuejs.press/ecosystem/zh/plugins/search/ + https://ecosystem.vuejs.press/ecosystem/zh/plugins/search/ + 搜索插件 + 搜索插件 + + + + docsearch + https://ecosystem.vuejs.press/ecosystem/zh/plugins/search/docsearch.html + https://ecosystem.vuejs.press/ecosystem/zh/plugins/search/docsearch.html + docsearch + docsearch + + + + 搜索插件指南 + https://ecosystem.vuejs.press/ecosystem/zh/plugins/search/guidelines.html + https://ecosystem.vuejs.press/ecosystem/zh/plugins/search/guidelines.html + 搜索插件指南 + 搜索插件指南 为了使 VuePress 主题开箱即用地支持搜索插件,我们有一套创建搜索插件时应遵循的指南。 组件名称 如果搜索插件提供了适合在导航栏或侧边栏中显示的搜索框,则应将其命名为 &lt;SearchBox /&gt; 并进行全局注册。 如果搜索插件提供了适合在单个页面中显示的复杂搜索结果组件(包含输入和结果列表),则应将其命名为 &lt;SearchPanel... + 为了使 VuePress 主题开箱即用地支持搜索插件,我们有一套创建搜索插件时应遵循的指南。

+

组件名称

+
    +
  • +

    如果搜索插件提供了适合在导航栏或侧边栏中显示的搜索框,则应将其命名为 <SearchBox /> 并进行全局注册。

    +
  • +
  • +

    如果搜索插件提供了适合在单个页面中显示的复杂搜索结果组件(包含输入和结果列表),则应将其命名为 <SearchPanel /> 并进行全局注册。

    +

    搜索插件应在每个语言环境中自动生成一个包含 <SearchPanel /> 组件的 /search.html 页面,但不得覆盖任何现有页面。

    +
  • +
+]]>
+
+ + search + https://ecosystem.vuejs.press/ecosystem/zh/plugins/search/search.html + https://ecosystem.vuejs.press/ecosystem/zh/plugins/search/search.html + search + search + + + + slimsearch + https://ecosystem.vuejs.press/ecosystem/zh/plugins/search/slimsearch.html + https://ecosystem.vuejs.press/ecosystem/zh/plugins/search/slimsearch.html + slimsearch + slimsearch + + + + 搜索引擎优化插件 + https://ecosystem.vuejs.press/ecosystem/zh/plugins/seo/ + https://ecosystem.vuejs.press/ecosystem/zh/plugins/seo/ + 搜索引擎优化插件 + 搜索引擎优化插件 + + + + 工具插件 + https://ecosystem.vuejs.press/ecosystem/zh/plugins/tools/ + https://ecosystem.vuejs.press/ecosystem/zh/plugins/tools/ + 工具插件 + 工具插件 + + + + cache + https://ecosystem.vuejs.press/ecosystem/zh/plugins/tools/cache.html + https://ecosystem.vuejs.press/ecosystem/zh/plugins/tools/cache.html + cache + cache + + + + google-tag-manager + https://ecosystem.vuejs.press/ecosystem/zh/plugins/tools/google-tag-manager.html + https://ecosystem.vuejs.press/ecosystem/zh/plugins/tools/google-tag-manager.html + google-tag-manager + google-tag-manager + + + + redirect + https://ecosystem.vuejs.press/ecosystem/zh/plugins/tools/redirect.html + https://ecosystem.vuejs.press/ecosystem/zh/plugins/tools/redirect.html + redirect + redirect + + + + register-components + https://ecosystem.vuejs.press/ecosystem/zh/plugins/tools/register-components.html + https://ecosystem.vuejs.press/ecosystem/zh/plugins/tools/register-components.html + register-components + register-components + + + + 默认主题 + https://ecosystem.vuejs.press/ecosystem/zh/themes/default/ + https://ecosystem.vuejs.press/ecosystem/zh/themes/default/ + 默认主题 + 默认主题 + + + + 内置组件 + https://ecosystem.vuejs.press/ecosystem/zh/themes/default/components.html + https://ecosystem.vuejs.press/ecosystem/zh/themes/default/components.html + 内置组件 + 内置组件 + + + + 配置 + https://ecosystem.vuejs.press/ecosystem/zh/themes/default/config.html + https://ecosystem.vuejs.press/ecosystem/zh/themes/default/config.html + 配置 + 配置 基础配置 hostname 类型: string 详情: 部署的域名,例如 https://example.com locales 类型: { [path: string]: Partial&lt;DefaultThemeLocaleData&gt; } 默认值: {} 详情: 多语言支持的各个语言 locales 。 所有在 Locale 配置 章节内的配... + 基础配置 +

hostname

+
    +
  • +

    类型: string

    +
  • +
  • +

    详情:

    +

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

    +
  • +
+

locales

+
    +
  • +

    类型: { [path: string]: Partial<DefaultThemeLocaleData> }

    +
  • +
  • +

    默认值: {}

    +
  • +
  • +

    详情:

    +

    多语言支持的各个语言 locales 。

    +

    所有在 Locale 配置 章节内的配置项都可以在 locales 中使用。

    +

    该配置项仅能在默认主题内生效,注意不要和 站点配置 中的 locales 混淆。

    +
  • +
  • +

    参考:

    + +
  • +
+

Locale 配置

+

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

+

colorMode

+ +

colorModeSwitch

+ +

externalLinkIcon

+
    +
  • +

    类型: boolean

    +
  • +
  • +

    默认值: true

    +
  • +
  • +

    详情:

    +

    是否在外部链接上显示外部链接图标。

    +
  • +
+

home

+
    +
  • +

    类型: string

    +
  • +
  • +

    默认值: /

    +
  • +
  • +

    详情:

    +

    首页的路径。

    +

    它将被用于:

    +
      +
    • 导航栏中 Logo 的链接
    • +
    • 404 页面的 返回首页 链接
    • +
    +
  • +
+

navbar

+
    +
  • +

    类型: false | NavbarOptions

    +
  • +
  • +

    默认值: []

    +
  • +
  • +

    详情:

    +

    导航栏配置。

    +

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

    +

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

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

    示例 1:

    +
  • +
+
export default {
+  theme: defaultTheme({
+    navbar: [
+      // NavbarLink
+      {
+        text: 'Foo',
+        link: '/foo/',
+      },
+      // NavbarGroup
+      {
+        text: 'Group',
+        prefix: '/group/',
+        children: ['foo.md', 'bar.md'],
+      },
+      // 字符串 - 页面文件路径
+      '/bar/README.md',
+    ],
+  }),
+}
+
    +
  • 示例 2:
  • +
+
export default {
+  theme: defaultTheme({
+    navbar: [
+      // 嵌套 Group - 最大深度为 2
+      {
+        text: 'Group',
+        prefix: '/group/',
+        children: [
+          {
+            text: 'SubGroup1',
+            prefix: 'sub1/',
+            children: [
+              'foo.md', // 解析为 `/guide/group/sub1/bar.md`
+              'bar.md', // 解析为 `/guide/group/sub1/bar.md`
+
+              // 一个外部链接
+              {
+                text: 'Example',
+                link: 'https://example.com',
+              },
+            ],
+          },
+          {
+            text: 'SubGroup2',
+            prefix: 'sub2/',
+            // 项目内链接的 .md 或 .html 后缀是可以省略的
+            children: [
+              'foo', // 解析为 `/guide/group/sub2/foo.md`
+              'bar', // 解析为 `/guide/group/sub2/bar.md`
+
+              // 不在 SubGroup2 内的链接
+              '/baz/', // 解析为 `/baz/README.md`
+            ],
+          },
+        ],
+      },
+      // 控制元素何时被激活
+      {
+        text: 'Group 2',
+        children: [
+          {
+            text: 'Always active',
+            link: '/',
+            // 该元素将一直处于激活状态
+            activeMatch: '/',
+          },
+          {
+            text: 'Active on /foo/',
+            link: '/not-foo/',
+            // 该元素在当前路由路径是 /foo/ 开头时激活
+            // 支持正则表达式
+            activeMatch: '^/foo/',
+          },
+        ],
+      },
+    ],
+  }),
+}
+

logo

+
    +
  • +

    类型: 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',
+  }),
+}
+

sidebar

+
    +
  • +

    类型: false | SidebarOptions

    +
  • +
  • +

    默认值: 'heading'

    +
  • +
  • +

    详情:

    +

    侧边栏配置。

    +

    你可以通过页面的 sidebar frontmatter 来覆盖这个全局配置。

    +

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

    +

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

    +

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

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

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

    +
      +
    • Key 为路径前缀。
    • +
    • Value 为 侧边栏数组"heading" 以自动为相应路径生成基于标题的侧边栏。
    • +
    +
  • +
  • +

    示例 1:

    +
  • +
+
export default {
+  theme: defaultTheme({
+    // 侧边栏数组
+    // 所有页面会使用相同的侧边栏
+    sidebar: [
+      // SidebarItem
+      {
+        text: 'Foo',
+        prefix: '/foo/',
+        link: '/foo/',
+        children: [
+          // SidebarItem
+          {
+            text: 'github',
+            link: 'https://github.com',
+            children: [],
+          },
+          // 字符串 - 页面文件路径
+          'bar.md', // 解析为 `/foo/bar.md`
+          '/ray.md', // 解析为 `/ray.md`
+        ],
+      },
+      // 字符串 - 页面文件路径
+      '/bar/README.md',
+    ],
+  }),
+}
+
    +
  • 示例 2:
  • +
+
export default {
+  theme: defaultTheme({
+    // 侧边栏对象
+    // 不同子路径下的页面会使用不同的侧边栏
+    sidebar: {
+      '/guide/': [
+        {
+          text: 'Guide',
+          // 相对路径会自动追加子路径前缀
+          children: [
+            'introduction.md', // 解析为 `/guide/introduction.md`
+            'getting-started.md', // 解析为 `/guide/getting-started.md`
+          ],
+        },
+      ],
+      '/reference/': 'heading',
+    },
+  }),
+}
+
    +
  • 示例 3:
  • +
+
export default {
+  theme: defaultTheme({
+    // 可折叠的侧边栏
+    sidebar: {
+      '/reference/': [
+        {
+          text: 'VuePress Reference',
+          collapsible: true,
+          // 基于项目路径的 .md 或 .html 后缀是可以省略的
+          children: ['cli', 'config'],
+        },
+        {
+          text: 'Bundlers Reference',
+          collapsible: true,
+          // 前缀可以是相对路径,等同于 `prefix: /reference/bundler/`
+          prefix: 'bundler/',
+          children: ['vite', 'webpack'],
+        },
+      ],
+    },
+  }),
+}
+

sidebarDepth

+
    +
  • +

    类型: number

    +
  • +
  • +

    默认值: 2

    +
  • +
  • +

    详情:

    +

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

    +
      +
    • 设为 0 来禁用所有级别的页面标题。
    • +
    • 设为 1 来包含 <h2> 标题。
    • +
    • 设为 2 来包含 <h2><h3> 标题。
    • +
    • ...
    • +
    +

    你可以通过页面的 sidebarDepth frontmatter 来覆盖这个全局配置。

    +
  • +
+

editLink

+
    +
  • +

    类型: boolean

    +
  • +
  • +

    默认值: true

    +
  • +
  • +

    详情:

    +

    是否启用 编辑此页 链接。

    +

    你可以通过页面的 editLink frontmatter 来覆盖这个全局配置。

    +
  • +
+

editLinkPattern

+
    +
  • +

    类型: string

    +
  • +
  • +

    详情:

    +

    编辑此页 链接的 Pattern 。

    +

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

    +

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

    +
  • +
  • +

    用法:

    +

    | Pattern | 描述 |
    +|

    +
  • +
+]]>
+
+ + 继承 + https://ecosystem.vuejs.press/ecosystem/zh/themes/default/extending.html + https://ecosystem.vuejs.press/ecosystem/zh/themes/default/extending.html + 继承 + 继承 VuePress 默认主题有着大量的用户,因此我们对它进行了一些便于继承的设计,以便用户轻松进行定制化。 布局插槽 默认主题的 Layout 布局提供了一些插槽: navbar navbar-before navbar-after sidebar sidebar-top sidebar-bottom page page-top page-bott... + 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
extending-a-theme
+

组件替换

+

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

+

默认主题将所有 非全局的组件 都注册了一个带 @theme 前缀的 alias 。例如,VPHomeFooter.vue 的别名是 @theme/VPHomeFooter.vue

+

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

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

修改行为

+

默认主题的核心行为大多都被抽离成可组合式 API 或工具函数,并同样提供了 @theme 前缀的 alias

+

比如,如果你想为默认主题的主题数据添加一些默认值,你可以通过覆盖 @theme/useThemeDatauseThemeData 函数来实现。

+

开发一个子主题

+

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

+
import type { DefaultThemeOptions } from '@vuepress/theme-default'
+import { defaultTheme } from '@vuepress/theme-default'
+import type { Theme } from 'vuepress/core'
+import { getDirname, path } from 'vuepress/utils'
+
+const __dirname = import.meta.dirname || getDirname(import.meta.url)
+
+export const childTheme = (options: DefaultThemeOptions): Theme => ({
+  name: 'vuepress-theme-child',
+  extends: defaultTheme(options),
+
+  // 在子主题的客户端配置文件中覆盖布局
+  // 注意,你在发布到 NPM 之前会将 TS 构建为 JS ,因此这里需要设置为 JS 文件的路径
+  clientConfigFile: path.resolve(__dirname, './client.js'),
+
+  // 覆盖组件别名
+  alias: {
+    '@theme/VPHomeFooter.vue': path.resolve(
+      __dirname,
+      './components/MyHomeFooter.vue',
+    ),
+  },
+})
+
]]>
+ +
+ + Frontmatter + https://ecosystem.vuejs.press/ecosystem/zh/themes/default/frontmatter.html + https://ecosystem.vuejs.press/ecosystem/zh/themes/default/frontmatter.html + Frontmatter + Frontmatter + + + + 语言配置 + https://ecosystem.vuejs.press/ecosystem/zh/themes/default/locale.html + https://ecosystem.vuejs.press/ecosystem/zh/themes/default/locale.html + 语言配置 + 语言配置 这些选项用于配置与语言相关的文本。 如果你的站点是以英语以外的其他语言提供服务的,你应该为每个语言设置这些选项来提供翻译。 repoLabel 类型: string 详情: 项目仓库的标签。 它将被用作 仓库链接 的文字。仓库链接 将会显示为导航栏的最后一个元素。 如果你不明确指定该配置项,它将会根据 配置项自动推断。 selectLangu... + 这些选项用于配置与语言相关的文本。

+

如果你的站点是以英语以外的其他语言提供服务的,你应该为每个语言设置这些选项来提供翻译。

+

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: '简体中文',
+      },
+    },
+  }),
+}
+

navbarLabel

+
    +
  • +

    类型:null | string

    +
  • +
  • +

    详情:

    +

    导航栏中主导航 aria-label 属性的值。

    +
  • +
+

pageNavbarLabel

+
    +
  • +

    类型: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 页面中 返回首页 链接的文字。

    +
  • +
+

toggleColorMode

+
    +
  • +

    类型: string

    +
  • +
  • +

    默认值: 'toggle color mode'

    +
  • +
  • +

    详情:

    +

    切换颜色模式按钮的标题文字。

    +

    它主要是为了站点的可访问性 (a11y) 。

    +
  • +
  • +

    参考:

    + +
  • +
+

toggleSidebar

+
    +
  • +

    类型: string

    +
  • +
  • +

    默认值: 'toggle sidebar'

    +
  • +
  • +

    详情:

    +

    切换侧边栏按钮的标题文字。

    +

    它主要是为了站点的可访问性 (a11y) 。

    +
  • +
+

prev

+
    +
  • +

    类型: string | false

    +
  • +
  • +

    默认值: 'Prev'

    +
  • +
  • +

    详情:

    +

    上一页按钮的文字。设置为 false 时,将隐藏上一页按钮。

    +
  • +
+

next

+
    +
  • +

    类型: string | false

    +
  • +
  • +

    默认值: 'Next'

    +
  • +
  • +

    详情:

    +

    下一页按钮的文字。设置为 false 时,将隐藏下一页按钮。

    +
  • +
+]]>
+
+ + Markdown + https://ecosystem.vuejs.press/ecosystem/zh/themes/default/markdown.html + https://ecosystem.vuejs.press/ecosystem/zh/themes/default/markdown.html + Markdown + Markdown + + + + 插件配置 + https://ecosystem.vuejs.press/ecosystem/zh/themes/default/plugin.html + https://ecosystem.vuejs.press/ecosystem/zh/themes/default/plugin.html + 插件配置 + 插件配置 你可以通过 themePlugins 设置默认主题使用的插件。 默认主题使用了一些插件,如果你确实不需要该插件,你可以选择禁用它。在禁用插件之前,请确保你已了解它的用途。 themePlugins.activeHeaderLinks 类型: boolean 默认值: true 详情: 是否启用 。 themePlugins.backToTop... + 你可以通过 themePlugins 设置默认主题使用的插件。

+

默认主题使用了一些插件,如果你确实不需要该插件,你可以选择禁用它。在禁用插件之前,请确保你已了解它的用途。

+
import { defaultTheme } from '@vuepress/theme-default'
+
+export default {
+  theme: defaultTheme({
+    themePlugins: {
+      // 在这里自定义主题插件
+    },
+  }),
+}
+

themePlugins.activeHeaderLinks

+ +

themePlugins.backToTop

+
    +
  • +

    类型: BackToTopPluginOptions | boolean

    +
  • +
  • +

    默认值: true

    +
  • +
  • +

    详情:

    +

    是否启用 @vuepress/plugin-back-to-top

    +

    支持对象格式以作为插件选项。

    +
  • +
+

themePlugins.container

+ +

themePlugins.copyCode

+
    +
  • +

    类型: CopyCodePluginOptions | boolean

    +
  • +
  • +

    默认值: true

    +
  • +
  • +

    详情:

    +

    是否启用 @vuepress/plugin-copy-code

    +

    支持对象格式以作为插件选项。

    +
  • +
+

themePlugins.git

+
    +
  • +

    类型: boolean

    +
  • +
  • +

    默认值: true

    +
  • +
  • +

    详情:

    +

    是否启用 @vuepress/plugin-git

    +
  • +
+

themePlugins.hint

+ +

themePlugins.links-check

+ +

themePlugins.mediumZoom

+ +

themePlugins.nprogress

+ +

themePlugins.prismjs

+ +

themePlugins.seo

+
    +
  • +

    类型: SeoPluginOptions | boolean

    +
  • +
  • +

    默认值: true

    +
  • +
  • +

    详情:

    +

    是否启用 @vuepress/plugin-seo

    +

    支持对象格式以作为插件选项。

    +
  • +
+

themePlugins.sitemap

+
    +
  • +

    类型: SitemapPluginOptions | boolean

    +
  • +
  • +

    默认值: true

    +
  • +
  • +

    详情:

    +

    是否启用 @vuepress/plugin-sitemap

    +

    支持对象格式以作为插件选项。

    +
  • +
+

themePlugins.tab

+ +]]>
+
+ + 样式 + https://ecosystem.vuejs.press/ecosystem/zh/themes/default/styles.html + https://ecosystem.vuejs.press/ecosystem/zh/themes/default/styles.html + 样式 + 样式 + + + + @vuepress/helper + https://ecosystem.vuejs.press/ecosystem/zh/tools/helper/ + https://ecosystem.vuejs.press/ecosystem/zh/tools/helper/ + @vuepress/helper + @vuepress/helper + + + + 客户端相关 + https://ecosystem.vuejs.press/ecosystem/zh/tools/helper/client.html + https://ecosystem.vuejs.press/ecosystem/zh/tools/helper/client.html + 客户端相关 + 客户端相关 这些函数仅在 @vuepress/helper/client 中可用。 可组合 API hasGlobalComponent 检查组件是否已全局注册。 提示 组件的局部导入不影响结果。 当在 setup 之外调用时,你需要将 app 实例作为第二个参数传递。 示例 useLocaleConfig 从语言环境设置中获取当前语言环境配置。 示例... + 这些函数仅在 @vuepress/helper/client 中可用。

+

可组合 API

+

hasGlobalComponent

+

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

+
+

提示

+
    +
  1. 组件的局部导入不影响结果。
  2. +
  3. 当在 setup 之外调用时,你需要将 app 实例作为第二个参数传递。
  4. +
+
+
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 // '标题'
+
+

工具

+

getHeaders

+

获取当前页面指定的 标题列表。

+
export const getHeaders: (options: GetHeadersOptions) => MenuItem[]
+

参数:

+
export interface GetHeadersOptions {
+  /**
+   * 页面标题选择器
+   *
+   * @default '[vp-content] h1, [vp-content] h2, [vp-content] h3, [vp-content] h4, [vp-content] h5, [vp-content] h6'
+   */
+  selector?: string
+  /**
+   * 忽略标题内的特定元素选择器
+   *
+   * 它将作为 `document.querySelectorAll` 的参数。
+   * 因此,你应该传入一个 `CSS 选择器` 字符串
+   *
+   * @default []
+   */
+  ignore?: string[]
+  /**
+   * 指定获取的标题层级
+   *
+   * `1` 至 `6` 表示 `<h1>` 至 `<h6>`
+   *
+   * - `false`: 不返回标题列表
+   * - `number`: 只获取指定的单个层级的标题。
+   * - `[number, number]: 标题层级元组,第一个数字应小于第二个数字。例如,`[2, 4]` 表示显示从 `<h2>` 到 `<h4>` 的所有标题。
+   * - `deep`: 等同于 `[2, 6]`, 表示获取从 `<h2>` 到 `<h6>` 的所有标题。
+   *
+   * @default 2
+   */
+  levels?: HeaderLevels
+}
+

返回结果:

+
export interface Header {
+  /**
+   * 当前标题的层级
+   *
+   * `1` 至 `6` 表示 `<h1>` 至 `<h6>`
+   */
+  level: number
+  /**
+   * 当前标题的内容
+   */
+  title: string
+  /**
+   * 标题的 标识
+   *
+   * 这通常是标题元素的 `id` 属性值
+   */
+  slug: string
+  /**
+   * 标题的链接
+   *
+   * 通常使用`#${slug}`作为锚点哈希
+   */
+  link: string
+  /**
+   * 标题的子标题列表
+   */
+  children: Header[]
+}
+
+export type HeaderLevels = number | 'deep' | false | [number, number]
+
+export type MenuItem = Omit<Header, 'children' | 'slug'> & {
+  element: HTMLHeadElement
+  children?: MenuItem[]
+}
+
Examples +
onMounted(() => {
+  const headers = getHeaders({
+    selector: '[vp-content] :where(h1,h2,h3,h4,h5,h6)',
+    levels: [2, 3], // 只有 h2 和 h3
+    ignore: ['.badge'], // 忽略标题内的 <Badge />
+  })
+  console.log(headers)
+})
+
+]]>
+
+ + 共享方法 + https://ecosystem.vuejs.press/ecosystem/zh/tools/helper/shared.html + https://ecosystem.vuejs.press/ecosystem/zh/tools/helper/shared.html + 共享方法 + 共享方法 以下函数在 Node.js 和客户端上均可用。 这些函数在 @vuepress/helper @vuepress/helper/client 和 @vuepress/helper/shared 中都可用。 数据相关 此方法在 MarkdownIt 插件中很有用。有些时候你可能需要在 Markdown 插件中生成组件,并将复杂的数据写入到组件属... + 以下函数在 Node.js 和客户端上均可用。

+

这些函数在 @vuepress/helper @vuepress/helper/client@vuepress/helper/shared 中都可用。

+

数据相关

+

此方法在 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。
  • +
  • isLinkRelative(x): x 是否不是外部 URL 或绝对 URL。
  • +
  • ensureEndingSlash(x): 确保 x 以斜杠结尾。
  • +
  • ensureLeadingSlash(x): 确保 x 以斜杠开头。
  • +
  • removeEndingSlash(x): 确保 x 不以斜杠结尾。
  • +
  • removeLeadingSlash(x): 确保 x 不以斜杠开头。
  • +
+]]>
+
+ + 样式 + https://ecosystem.vuejs.press/ecosystem/zh/tools/helper/style.html + https://ecosystem.vuejs.press/ecosystem/zh/tools/helper/style.html + 样式 + 样式 提供了如下样式文件。 规范化 @vuepress/helper/normalize.css 是一个 CSS 文件,用于规范化浏览器的默认样式。推荐在社区主题中引入它。 + 提供了如下样式文件。

+

规范化

+

@vuepress/helper/normalize.css 是一个 CSS 文件,用于规范化浏览器的默认样式。推荐在社区主题中引入它。

+]]>
+
+ + blog + https://ecosystem.vuejs.press/ecosystem/zh/plugins/blog/blog/ + https://ecosystem.vuejs.press/ecosystem/zh/plugins/blog/blog/ + blog + blog + + + + 配置 + https://ecosystem.vuejs.press/ecosystem/zh/plugins/blog/blog/config.html + https://ecosystem.vuejs.press/ecosystem/zh/plugins/blog/blog/config.html + 配置 + 配置 插件选项 getInfo 类型: (page: Page) =&gt; Record&lt;string, unknown&gt; 必填: 否 参考: 详情: 获取文章信息的函数。 获取到的信息会被稍后注入至路由元数据,以便你可以在客户端中通过组合式 API 获取。 filter 类型: (page: Page) =&gt; boolean 默认值: (page) =&gt;... + 插件选项 +

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 | false | ((name: string) => string)
+
+  /**
+   * 项目页面布局组件名称
+   *
+   * @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>[]
+}
+
]]>
+
+ + 指南 + https://ecosystem.vuejs.press/ecosystem/zh/plugins/blog/blog/guide.html + https://ecosystem.vuejs.press/ecosystem/zh/plugins/blog/blog/guide.html + 指南 + 指南 使用 @vuepress/plugin-blog,你可以轻松地将博客功能引入主题。 收集文章并生成信息 起步时,插件会首选过滤并选择那些需要作为文章的页面。这将剔除你不想要的页面,并在后续处理中排除它们。 默认情况下,所有从 Markdown 文件生成但不是主页的页面,都将被视作文章。 你可能需要设置 filter 选项来完全自定义要收集的页面。... + 使用 @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, unknown> = {
+          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 布局:

+
<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" :key="path">
+        <RouteLink :key="name" :to="path" class="category">
+          {{ name }}
+          <span class="category-num">
+            {{ items.length }}
+          </span>
+        </RouteLink>
+      </li>
+    </ul>
+  </div>
+</template>
+

TagList 布局:

+
<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>
+    <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 v-if="categoryMap.currentItems" class="article-wrapper">
+      <div v-if="!categoryMap.currentItems.length">Nothing in here.</div>
+      <article
+        v-for="{ info, path } in categoryMap.currentItems"
+        :key="path"
+        class="article"
+        @click="$router.push(path)"
+      >
+        <header class="title">
+          {{ info.title }}
+        </header>
+        <hr />
+        <div class="article-info">
+          <span v-if="info.author" class="author"
+            >Author: {{ info.author }}</span
+          >
+          <span v-if="info.date" 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 布局:

+
<script setup lang="ts">
+import { useBlogType } from '@vuepress/plugin-blog/client'
+
+import ParentLayout from '@vuepress/theme-default/layouts/Layout.vue'
+import ArticleList from '../components/ArticleList.vue'
+
+const stars = useBlogType('star')
+</script>
+<template>
+  <div v-if="stars.items?.length" class="article-wrapper">
+    <article
+      v-for="{ info, path } in stars.items"
+      :key="path"
+      class="article"
+      @click="$router.push(path)"
+    >
+      <header class="title">
+        {{ info.title }}
+      </header>
+      <hr />
+      <div class="article-info">
+        <span v-if="info.author" class="author">Author: {{ info.author }}</span>
+        <span v-if="info.date" 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 v-else>Nothing in here.</div>
+</template>
+

有关返回类型,请参阅 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

+
+]]>
+
+ + comment + https://ecosystem.vuejs.press/ecosystem/zh/plugins/blog/comment/ + https://ecosystem.vuejs.press/ecosystem/zh/plugins/blog/comment/ + comment + comment + + + + 指南 + https://ecosystem.vuejs.press/ecosystem/zh/plugins/blog/comment/guide.html + https://ecosystem.vuejs.press/ecosystem/zh/plugins/blog/comment/guide.html + 指南 + 指南 设置选项 你既可以在 Node.js 一侧使用插件选项设置选项,也可以通过客户端配置文件在浏览器一侧设置选项。 通过插件选项 通过客户端配置文件 有以下你需要注意的限制: provider、多语言设置和其他资源相关选项必须在插件选项中设置。 为确保 tree-shaking 有效,我们必须在 Node 一侧优化入口,以便打包器可以了解最终打包中应... + 设置选项 +

你既可以在 Node.js 一侧使用插件选项设置选项,也可以通过客户端配置文件在浏览器一侧设置选项。

+

通过插件选项

+
import { commentPlugin } from '@vuepress/plugin-comment'
+
+// .vuepress/config.ts
+export default {
+  plugins: [
+    commentPlugin({
+      provider: 'Artalk', // Artalk | Giscus | Waline | Twikoo
+
+      // 在这里放置其他选项
+      // ...
+    }),
+  ],
+}
+

通过客户端配置文件

+
import {
+  defineArtalkConfig,
+  // defineGiscusConfig,
+  // defineTwikooConfig,
+  // defineWalineConfig,
+} from '@vuepress/plugin-comment/client'
+import { defineClientConfig } from 'vuepress/client'
+
+defineArtalkConfig({
+  // 选项
+})
+

有以下你需要注意的限制:

+
    +
  • +

    provider、多语言设置和其他资源相关选项必须在插件选项中设置。

    +

    为确保 tree-shaking 有效,我们必须在 Node 一侧优化入口,以便打包器可以了解最终打包中应包含哪些资源。

    +

    这些选项将在配置参考中用

    +
  • +
  • +

    不能序列化为 JSON 的选项必须在客户端配置中设置。

    +

    接收复杂值的选项(例如:函数)不能在插件选项中设置,因为插件运行在 Node.js 环境下,所以我们无法将这些值和它们的上下文传递给浏览器。

    +

    这些选项将在配置参考中用

    +
  • +
+

添加评论

+

该插件全局注册了一个组件 <CommentService />

+
    +
  • 如果你是用户,你应该使用 alias 和布局槽来插入组件。 我们建议你在 <PageNav /> 组件之后插入评论组件 (<CommentService />),本页可作为一个 Demo 作为参考。
  • +
  • 如果你是主题开发者,你应该将这个组件插入到你的主题布局中。
  • +
+

默认情况下,<CommentService /> 组件是全局启用的,你可以在插件选项和页面 frontmatter 中使用 comment 选项来控制它。

+
    +
  • 你可以通过在页面 frontmatter 中设置 comment: false 在本地禁用它。
  • +
  • 要使其全局禁用,请在插件选项中将 comment 设置为 false。 然后你可以在页面 frontmatter 中设置 comment: true 以在局部启用它。
  • +
+

你可以在页面 frontmatter 中设置 commentID 选项来自定义评论 ID,该 ID 用于标识要用于页面的评论存储项。默认情况下,它将是页面的 path ,这意味着如果你将站点部署到多个位置,站点间具有相同内容的页面将共享相同的评论数据。

+

可用的评论服务

+

目前你可以选择 GiscusWalineArtalkTwikoo

+
+

推荐的评论服务

+
    +
  • 面向程序员和开发人员: Giscus
  • +
  • 面向公众: Waline
  • +
+
+

通用选项

+

provider

+
    +
  • 类型: "Artalk" | "Giscus" | "Twikoo" | "Waline" | "None"
  • +
  • 默认值: "None"
  • +
  • 详情:评论服务提供者。
  • +
+

comment

+
    +
  • 类型: boolean
  • +
  • 默认值: true
  • +
  • 详情:是否默认启用评论功能。
  • +
+]]>
+
+ + feed + https://ecosystem.vuejs.press/ecosystem/zh/plugins/blog/feed/ + https://ecosystem.vuejs.press/ecosystem/zh/plugins/blog/feed/ + feed + feed + + + + 频道设置 + https://ecosystem.vuejs.press/ecosystem/zh/plugins/blog/feed/channel.html + https://ecosystem.vuejs.press/ecosystem/zh/plugins/blog/feed/channel.html + 频道设置 + 频道设置 channel 插件选项用于配置 feed 的频道。 channel.title 类型:string 默认值:SiteConfig.title 频道的标题 channel.link 类型:string 默认值:部署的网址 (通过 options.hostname 和 context.base 生成) 频道地址 channel.descript... + channel 插件选项用于配置 feed 的频道。

+

channel.title

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

频道的标题

+

channel.link

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

频道地址

+

channel.description

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

频道描述信息

+

channel.language

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

频道使用的语言

+

channel.copyright

+
    +
  • 类型: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

+
+]]>
+
+ + 插件配置 + https://ecosystem.vuejs.press/ecosystem/zh/plugins/blog/feed/config.html + https://ecosystem.vuejs.press/ecosystem/zh/plugins/blog/feed/config.html + 插件配置 + 插件配置 hostname 类型:string 必填:是 部署网站的域名。 atom 类型:boolean 默认值:false 是否启用 Atom 格式输出。 json 类型:boolean 默认值:false 是否启用 JSON 格式输出。 rss 类型:boolean 默认值:false 是否启用 RSS 格式输出。 image 类型:string... + 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 }) =>
    +  Boolean(frontmatter.feed ?? (filePathRelative && !frontmatter.home))
    +
  • +
+

自定义的过滤函数,用于过滤哪些项目在 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 外,上述任何选项均受支持。

+]]>
+
+ + Frontmatter 配置 + https://ecosystem.vuejs.press/ecosystem/zh/plugins/blog/feed/frontmatter.html + https://ecosystem.vuejs.press/ecosystem/zh/plugins/blog/feed/frontmatter.html + Frontmatter 配置 + Frontmatter 配置 你可以通过配置每个页面的 Frontmatter,来对每个 Feed 项目生成进行单独的控制。 添加与移除 默认情况下,所有文章均会被添加至 feed 流。如果你想在 feed 中移除特定页面,你可以在 frontmatter 中设置 feed: false。 读取的 Frontmatter 信息 title 类型:str... + 你可以通过配置每个页面的 Frontmatter,来对每个 Feed 项目生成进行单独的控制。

+

添加与移除

+

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

+

读取的 Frontmatter 信息

+

title

+
    +
  • 类型:string
  • +
+

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

+

description

+
    +
  • 类型:string
  • +
+

页面描述

+

date

+
    +
  • 类型:Date
  • +
+

页面的发布日期

+

article

+
    +
  • 类型:boolean
  • +
+

该页面是否是文章

+
+

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

+
+

copyright

+
    +
  • 类型: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。

+
+]]>
+
+ + Feed 获取器 + https://ecosystem.vuejs.press/ecosystem/zh/plugins/blog/feed/getter.html + https://ecosystem.vuejs.press/ecosystem/zh/plugins/blog/feed/getter.html + Feed 获取器 + Feed 获取器 你可以通过控制插件选项中的 getter 来完全控制 Feed 项目的生成。 getter.title 类型:(page: Page, app: App) =&gt; string 项目标题获取器 getter.link 类型:(page: Page, app: App) =&gt; string 项目链接获取器 getter.descripti... + 你可以通过控制插件选项中的 getter 来完全控制 Feed 项目的生成。

+

getter.title

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

项目标题获取器

+

getter.link

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

项目链接获取器

+

getter.description

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

项目描述获取器

+
+

提示

+

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

+
+

getter.content

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

项目内容获取器

+

getter.author

+
    +
  • 类型:(page: Page, app: App) => FeedAuthor[]
  • +
+

项目作者获取器。

+
+

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

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

getter.category

+
    +
  • 类型:(page: Page, app: App) => 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, app: App) => 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, app: App) => Date | undefined
  • +
+

项目发布日期获取器

+

getter.lastUpdateDate

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

项目最后更新日期获取器

+

getter.image

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

项目图片获取器

+
+

确保返回一个完整的 URL。

+
+

getter.contributor

+
    +
  • 类型:(page: Page, app: App) => FeedContributor[]
  • +
+

项目贡献者获取器

+
+

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

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

getter.copyright

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

项目版权获取器

+]]>
+
+ + 指南 + https://ecosystem.vuejs.press/ecosystem/zh/plugins/blog/feed/guide.html + https://ecosystem.vuejs.press/ecosystem/zh/plugins/blog/feed/guide.html + 指南 + 指南 使用 插件可为你生成以下三种格式的 feed 文件: Atom 1.0 JSON 1.1 RSS 2.0 请按照需要生成的格式,在插件选项中设置 atom, json 或 rss 为 true。 为了正确生成 Feed 链接,你需要在插件选项中设置 hostname。 可读的预览 当你在浏览器中打开 Feed 文件时,我们会通过 xsl 模板将 ... + 使用 +

插件可为你生成以下三种格式的 feed 文件:

+
    +
  • Atom 1.0
  • +
  • JSON 1.1
  • +
  • RSS 2.0
  • +
+

请按照需要生成的格式,在插件选项中设置 atom, jsonrsstrue

+

为了正确生成 Feed 链接,你需要在插件选项中设置 hostname

+

可读的预览

+

当你在浏览器中打开 Feed 文件时,我们会通过 xsl 模板将 atom 和 rss feed xml 魔法般地转换为可读的 html。你可以查看本站的 atomrss 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 分别对不同语言提供不同的默认设置。

+]]>
+
+ + sass-palette + https://ecosystem.vuejs.press/ecosystem/zh/plugins/development/sass-palette/ + https://ecosystem.vuejs.press/ecosystem/zh/plugins/development/sass-palette/ + sass-palette + sass-palette + + + + 配置 + https://ecosystem.vuejs.press/ecosystem/zh/plugins/development/sass-palette/config.html + https://ecosystem.vuejs.press/ecosystem/zh/plugins/development/sass-palette/config.html + 配置 + 配置 插件选项 id 类型: string 必填: 是 调色板的唯一 ID,用于避免重复注册。 config 类型: string 默认值: `.vuepress/styles/${id}-palette.scss` 用户配置文件路径,相对于源文件夹。 提示 这是用户设置样式变量的文件。 默认路径的文件名拥有上方的 ID 前缀。 defaultConf... + 插件选项 +

id

+
    +
  • 类型: string
  • +
  • 必填: 是
  • +
+

调色板的唯一 ID,用于避免重复注册。

+

config

+
    +
  • 类型: string
  • +
  • 默认值: `.vuepress/styles/${id}-palette.scss`
  • +
+

用户配置文件路径,相对于源文件夹。

+
+

提示

+

这是用户设置样式变量的文件。

+

默认路径的文件名拥有上方的 ID 前缀。

+
+

defaultConfig

+
    +
  • 类型: string
  • +
  • 默认值: "@vuepress/plugin-sass-palette/styles/default/config.scss"
  • +
+

默认的配置文件路径,应为绝对路径。

+
+

提示

+

这是你应该通过 !default 来提供默认样式变量的文件。

+
+

palette

+
    +
  • 类型: string
  • +
  • 默认值: `.vuepress/styles/${id}-palette.scss`
  • +
+

用户的调色板文件路径,相对于源文件夹。

+
+

提示

+

这是用户控制注入 CSS 变量的文件。所有的变量会被转换为连字符格式然后被注入。

+

默认路径的文件名拥有上方的 ID 前缀。

+
+

defaultPalette

+
    +
  • 类型: string
  • +
  • 默认值: "@vuepress/plugin-sass-palette/styles/default/palette.scss"
  • +
+

默认的调色板文件路径,应为绝对路径。

+
+

提示

+

这是你应该通过 !default 来提供默认调色板值的文件。所有的变量会被转换为连字符格式然后被注入。

+
+

generator

+
    +
  • 类型: string
  • +
  • 必填: 否
  • +
+

自定义的生成器,用于生成调色板配置的衍生值。

+

如: 你可能想要根据 $theme-color 的值提供一个 $theme-color-light

+

style

+
    +
  • 类型: string
  • +
  • 必填: 否
  • +
+

用户的样式文件路径,相对于源文件夹。.

+

别名

+

可用的别名如下:

+
    +
  • 配置: @sass-palette/${id}-config (基于 id)
  • +
  • 调色板: @sass-palette/${id}-palette (基于 id)
  • +
  • 样式: @sass-palette/${id}-style (仅在设置了 style 选项时可用)
  • +
  • 助手: @sass-palette/helper
  • +
+]]>
+
+ + 指南 + https://ecosystem.vuejs.press/ecosystem/zh/plugins/development/sass-palette/guide.html + https://ecosystem.vuejs.press/ecosystem/zh/plugins/development/sass-palette/guide.html + 指南 + 指南 相比于 插件,本插件允许你: 基于用户配置派生相关样式 在插件中调用并提供和主题类似的样式自定义 跨越多个插件或主题通过 id 选项分组应用 在使用插件前,你需要了解 id 选项,以及三个样式概念: 配置、调色板和派生器。 ID 选项 首先,你应该了解此插件的设计目标是提供跨越插件和主题的支持 (而并不像官方插件仅面向主题)。 我们提供了 id ... + 相比于 @vuepress/plugin-palette 插件,本插件允许你:

+
    +
  • 基于用户配置派生相关样式
  • +
  • 在插件中调用并提供和主题类似的样式自定义
  • +
  • 跨越多个插件或主题通过 id 选项分组应用
  • +
+

在使用插件前,你需要了解 id 选项,以及三个样式概念: 配置、调色板和派生器。

+

ID 选项

+

首先,你应该了解此插件的设计目标是提供跨越插件和主题的支持 (而并不像官方插件仅面向主题)。

+

我们提供了 id 选项来完成此目标,它将允许你:

+
    +
  • +

    在插件 (或主题) 间共享同一个样式系统。

    +

    所有别名和模块名称都具有 ID 前缀,这意味着你可以在你的插件 (或主题) 中使用一套样式变量来统一你的样式,而不会受到其他插件 (或主题) 的影响。

    +

    用户可以在同一个文件中配置所有颜色变量、断点和其他样式配置,并自动应用在具有相同 ID 的主题和插件上。

    +
    +

    示例

    +

    vuepress-theme-hope 及其它的相关插件都使用相同 ID hope 调用插件,因此用户在主题中配置的样式会自动在所有插件中生效。

    +
    +
  • +
  • +

    设置不同的 ID 时,插件们和主题之间互相完全独立。我们建议你使用你的插件名称设置 id 变量。

    +

    使用默认设置,用户将在 .vuepress/styles 文件夹下设置你的插件样式,其中 Sass 文件以你的 ID 前缀开头。你可以使用 ${id}-config${id}-palette 访问所需的变量。

    +
    +

    示例

    +

    vuepress-theme-hope 正在使用 ID "hope",而假设 vuepress-plugin-abc 正在使用 "abc"。他们可以分别使用 hope-config hope-paletteabc-config abc-palette 模块名称获取自己的变量。

    +
    +
  • +
  • +

    通过相同 ID 调用插件不会有任何副作用。

    +
    +

    示例

    +

    vuepress-theme-hope 及其它的相关插件都使用相同 ID hope 调用插件。

    +
    +
  • +
+

配置

+

配置文件仅用于提供 Sass 变量。它所包含 Sass 变量可以在其他文件中通过 ${id}-config 使用。

+

你可以指定一个文件作为用户配置文件。这样你可以稍后在插件 Sass 文件中访问包含每个变量的模块。此外,你还可以提供默认配置文件,你可以在其中使用 !default 为变量设置默认值。

+
一个例子 +

假设,你正在 vuepress-plugin-abc 中这样调用插件:

+
useSassPalette(app, {
+  id: 'abc',
+  defaultConfig: 'vuepress-plugin-abc/styles/config.scss',
+})
+

用户配置:

+
$navbar-height: 3.5rem;
+

默认配置:

+
$navbar-height: 2rem !default;
+$sidebar-width: 18rem !default;
+

你可以在插件 Sass 文件中获取到如下变量:

+
// Vue 单文件组件的 <style lang="scss"> 块或脚本中直接导入的 Scss 文件中
+@debug abc-config.$navbar-height; // 3.5rem
+@debug abc-config.$sidebar-width; // 18rem
+
+

限制

+

我们利用 additionalData 选项让 ${id}-config 模块在你的样式中可用,但这有一些限制。

+

additionalData 仅适用于 SASS 入口,因此 ${id}-config 仅适用于:

+
    +
  • Vue 单文件组件的 <style lang="scss">
  • +
  • 脚本中直接导入的 scss 文件 (例如: 客户端应用程序增强文件中的 import "./a-scss-file.scss") 。
  • +
+

如果 scss 文件不是直接导入的,而是通过 @use@import API 导入的,模块将不可用。因此,在这种情况下,你应该通过 @use "@sass-palette/${id}-config"; 手动导入模块。

+

调色板

+

调色板文件用于 CSS 变量注入,其中每个变量将被注入到 root 中,变量名称转换为 kebab-name 格式。

+

你可以指定一个文件作为用户调色板文件,默认文件名是 ${id}-palette.scss。 此外,你还可以提供一个默认的调色板文件,你可以在其中使用 !default 为变量设置默认值。

+
一个例子 +

假设,你正在 vuepress-plugin-abc 中这样调用插件:

+
useSassPalette(app, {
+  id: 'abc',
+  defaultPalette: 'vuepress-plugin-abc/styles/palette.scss',
+})
+

用户调色板:

+
$color-a: red;
+

默认调色板:

+
$color-a: blue !default;
+$color-b: green !default;
+

那么 root 选择器将会拥有下列 CSS 变量:

+
:root {
+  --color-a: red;
+  --color-b: green;
+}
+
+

和配置文件一样,调色板文件提供了一个 ${$id}-palette 模块 (也包含生成器的值),它也受 additionalData 选项的限制,因此如果你想在其他 Sass 文件中使用它,你应该手动导入模块。

+

颜色设置

+

由于默认主题支持深色模式,因此你可能希望在浅色模式和深色模式下使用不同的颜色。

+

为此,你应该使用包含 lightdark 键的映射设置颜色变量。 稍后,此插件将为你生成不同的颜色。

+
一个例子 +
// 用户调色板
+$text-color: (
+  light: #222,
+  dark: #999,
+);
+
+// 默认调色板
+$text-color: (
+  light: #2c3e50,
+  dark: #9e9e9e,
+) !default;
+$bg-color: (
+  light: #fff,
+  dark: #1e1e1e,
+) !default;
+

然后你会得到:

+
:root {
+  --text-color: #222;
+  --bg-color: #fff;
+}
+
+[data-theme='dark'] {
+  --text-color: #999;
+  --bg-color: #1e1e1e;
+}
+
+

允许的变量类型

+

调色板中只允许使用颜色 (或深浅模式颜色对象)、长度和字符串。任何其他类型都将被删除。

+
+

为什么除了字符串只允许颜色和长度

+

在常见情况下,你可能只想计算颜色和长度。所以放弃其他类型支持是相当安全的,因为你想要的任何其他值都可以转换为字符串。

+
示例 +

如果你想要一个 --move-transitionwidth 0.3s ease,你可以使用字符串:

+
// 这将被 sass 视为一个类型为 (length, time, function) 的列表
+// 并会触发警告并被插件删除
+$moveTransition: width 0.3s ease;
+
+// 这会得到你想要的
+// :root {
+//   --move-transition: width 0.3s ease
+// }
+$moveTransition: 'width 0.3s ease';
+
+
+

辅助模块

+

我们公开了 @vuepress/plugin-sass-palette 使用的内部函数,作为辅助模块。

+

你可以通过 @sass-palette/helper 别名使用此辅助模块,并调用其函数来自己实现类似的功能。

+

生成器

+

生成器文件面向开发人员使用配置或调色板文件变量生成衍生值。

+

你可以在此文件中直接获取调色板的变量值,并生成基于它们的新值。

+

生成器变量也将像调色板一样作为 CSS 变量注入,它们也在调色板模块中可用。

+
示例 +

你可能想要一个基于 $theme-color$theme-color-light。所以你可以这样写一个生成器:

+
@use 'sass:color';
+@use '@sass-palette/helper';
+
+$theme-color-light: (
+  light: color.scale(helper.get-color($theme-color), $lightness: 10%),
+  dark: color.scale(helper.get-dark-color($theme-color), $lightness: 10%),
+) !default;
+

你也可以通过导入配置文件来基于配置文件提供的变量生成值:

+
// id 为 "abc" 的生成器
+@use 'sass:color';
+@use '@sass-palette/abc-config';
+@use '@sass-palette/helper';
+
+$code-c-bg: abc-config.$highlighter == 'shiki'? #fff: #f8f8f8;
+
+

用户样式

+

如果你是主题开发人员,你可能希望为你的用户提供一种自定义主题或网站的方法。

+

在这种情况下,你应该在使用此插件时将 style 选项设置为用户样式文件。

+

稍后,你应该通过在你的主题样式之后导入 @sass-palette/${id}-style 来手动包含用户样式文件。

+
+

提示

+

@sass-palette/${id}-style 是用户样式文件的别名,你可以在 JS/TS/SASS 中导入它。

+
+]]>
+
+ + revealjs + https://ecosystem.vuejs.press/ecosystem/zh/plugins/markdown/revealjs/ + https://ecosystem.vuejs.press/ecosystem/zh/plugins/markdown/revealjs/ + revealjs + revealjs + + + + 幻灯片演示 + https://ecosystem.vuejs.press/ecosystem/zh/plugins/markdown/revealjs/demo.html + https://ecosystem.vuejs.press/ecosystem/zh/plugins/markdown/revealjs/demo.html + 幻灯片演示 + + + + 幻灯片主题 + https://ecosystem.vuejs.press/ecosystem/zh/plugins/markdown/revealjs/themes.html + https://ecosystem.vuejs.press/ecosystem/zh/plugins/markdown/revealjs/themes.html + 幻灯片主题 + 幻灯片主题 auto 基于主题模式 black white league beige sky night serif simple solarized blood moon + auto +
+

基于主题模式

+
+]]>
+
+ + pwa + https://ecosystem.vuejs.press/ecosystem/zh/plugins/pwa/pwa/ + https://ecosystem.vuejs.press/ecosystem/zh/plugins/pwa/pwa/ + pwa + pwa + + + + 配置 + https://ecosystem.vuejs.press/ecosystem/zh/plugins/pwa/pwa/config.html + https://ecosystem.vuejs.press/ecosystem/zh/plugins/pwa/pwa/config.html + 配置 + 配置 选项 showInstall 类型:boolean 默认值:false 详情: 是否在 Service Worker 首次成功注册时显示 PWA 安装按钮 manifest 类型:ManifestOption 详情: 填充一个将被解析为 manifest.webmanifest 的对象。 提示 如果未设置某些选项,它们会回退到插件预设值。 nam... + 选项 +

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.maskIcon

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

apple.statusBarColor

+
    +
  • 类型:'black-translucent' | 'black' | 'default
  • +
  • 详情:Safari 状态栏颜色
  • +
+

foundComponent

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

readyComponent

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

appendBase

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

generateSwConfig

+
    +
  • +

    详情:

    +

    传递给 workbox-build 的选项,具体详情,请见 Workbox 文档

    +
  • +
+

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]: Partial<PwaPluginLocaleData>
    +}
    +
  • +
  • +

    详情:

    +

    PWA 插件的国际化配置。

    +
  • +
+
内置支持语言 +
    +
  • 简体中文 (zh-CN)
  • +
  • 繁体中文 (zh-TW)
  • +
  • 英文(美国) (en-US)
  • +
  • 德语 (de-DE)
  • +
  • 俄语 (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

+
    +
  • +

    详情:

    +

    返回此插件的事件派发器。

    +

    你可以添加监听器函数到 register-service-worker 提供的事件。

    +
  • +
  • +

    示例:

    +
  • +
+
import { usePwaEvent } from '@vuepress/plugin-pwa/client'
+
+export default {
+  setup(): void {
+    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(): void {
+    onMounted(() => {
+      forceUpdate()
+    })
+  },
+}
+

registerSW

+
    +
  • +

    详情:

    +

    手动注册 Service Worker。

    +
  • +
  • +

    参数:

    +
  • +
+

| 参数 | 类型 | 描述 |
+|

+]]>
+
+ + 指南 + https://ecosystem.vuejs.press/ecosystem/zh/plugins/pwa/pwa/guide.html + https://ecosystem.vuejs.press/ecosystem/zh/plugins/pwa/pwa/guide.html + 指南 + 指南 介绍 将你的 VuePress 站点变成渐进式网络应用程序 (PWA)[1]。 此插件使用 workbox-build 生成 Service Worker 文件,并使用 register-service-worker 注册 Service Worker。 注意 如果你启用过该插件,并想要禁用它,你可能需要 来移除现有的 Service Worke... + 介绍 +

将你的 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 清单[3]文件是必要的,并且你的 PWA 应满足可安装性[4]要求。

+

你可以通过设置 manifest 选项来自定义 manifest 文件,或者在 public 文件夹中提供 manifest.webmanifestmanifest.json。前者优先级更高。

+

插件会自动为你生成 manifest.webmanifest,并在每个页面的 <head> 中添加清单链接声明,但是 你至少应该通过 manifest.icons 或 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 页面。

+ +

大小控制

+

为了防止在预缓存列表中包含大文件,任何 > 2 MB 的文件或 > 1 MB 的图片都将被忽略。 你可以通过 maxSizemaxImageSize 来自定义大小限制 (单位为 KB)。

+

更新控制

+

我们提供 update 选项控制用户如何接收更新。

+

update 选项的默认值是 "available",这意味着当网站内容更新后,新的 SW 会在后台静默安装,并在安装结束后弹窗提示用户新内容就绪。用户可以自主选择是否立即刷新查看新内容。这意味在新 SW 就绪前用户会访问旧版本网站。

+

如果你的文档仍在建设期,希望尽早提示用户他可能在阅读已过时的内容,你可以将其设置为 "hint"。这样用户在进入文档后数秒内就可以收到新内容已发布的通知。但这样做的负面效果是如果用户在新 SW 就绪前选择更新,那么他将在新 SW 安装并接管页面前,需要从互联网获取页面的全部资源。

+

如果你的文档很稳定,或者你在托管博客,不太关心用户立即接收到最新版本,你可以将其设置为 "disable",这意味着新的 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 type="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 type="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. +
  3. Service Worker 简要介绍

    +
      +
    1. +

      Service Worker 会在注册过程中获取注册在其中的所有文件并缓存它们。

      +
    2. +
    3. +

      注册成功后,Service Worker 激活,并开始代理并控制你的全部请求。

      +
    4. +
    5. +

      每当你想要通过浏览器发起访问请求后,Service Worker 将会查看其是否存在与自身缓存列表中,若存在则直接返回缓存好的结果,否则调用自身的 fetch 方法进行获取。你可以通过自定义 fetch 方法,来完全控制网页内资源获取请求的结果,比如在离线时提供一个 fallback 的网页。

      +
    6. +
    7. +

      每次用户重新打开网站时,Service Worker 会向自身注册时的地址发出校验命令,如果检测到新版本的 Service Worker,则会更新自身,并开始缓存注册在新 Service Worker 中的资源列表。成功获取内容更新后,Service Worker 将会触发 update 事件。可以通过此事件提示用户,比如将在右下角显示一个弹出窗口,提示用户新内容可用并允许用户触发更新。

      +
    8. +
    + ↩︎ ↩︎
  4. +
  5. 清单文件

    +

    清单文件使用 JSON 格式,负责声明 PWA 各项信息,如名称、描述、图标、快捷动作等。

    +

    为了使你的站点能够被注册为 PWA,你需要满足 manifest 基本的规范,才能使浏览器认为该网站为一个可安装的 PWA 并允许用户安装它。

    +
    +

    提示

    +

    Manifest 的标准与规范,请详见 MDN 网络 App 清单W3C Manifest

    +
    + ↩︎
  6. +
  7. 可安装性

    +

    想要让网站可以注册为 PWA,网站需要自行成功注册有效的 Service Worker,同时拥有合法的 manifest 清单文件并在网站中声明它。

    +

    清单文件应至少包含 name(或 short_name) icons start_url

    +

    在 Safari 中,SW 的最大缓存空间为 50 MB。 ↩︎ ↩︎

    +
  8. +
  9. SSG: Static Site Generating,静态站点生成。 ↩︎

    +
  10. +
  11. SEO: Search Engine Optimization,搜索引擎增强,

    +

    详见 SEO 介绍 ↩︎

    +
  12. +
  13. SPA: Single Page Application, 单页应用

    +

    大多只有主页,并使用 history mode 处理路由,而不是真的在页面之间导航。 ↩︎

    +
  14. +
+
+]]>
+
+ + seo + https://ecosystem.vuejs.press/ecosystem/zh/plugins/seo/seo/ + https://ecosystem.vuejs.press/ecosystem/zh/plugins/seo/seo/ + seo + seo + + + + 选项 + https://ecosystem.vuejs.press/ecosystem/zh/plugins/seo/seo/config.html + https://ecosystem.vuejs.press/ecosystem/zh/plugins/seo/seo/config.html + 选项 + 选项 hostname 类型:string 必填:是 详情: 部署域名 author 类型:Author 详情: 默认作者 autoDescription 类型:boolean 默认值:true 详情: 是否自动生成描述 canonical 类型:string | ((page: Page) =&gt; string | null) 详情: 首选链接 fal... + hostname +
    +
  • +

    类型:string

    +
  • +
  • +

    必填:是

    +
  • +
  • +

    详情:

    +

    部署域名

    +
  • +
+

author

+
    +
  • +

    类型:Author

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

    详情:

    +

    默认作者

    +
  • +
+

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 信息 */
    +  ogpInfo: 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>

    +
  • +
+]]>
+
+ + 指南 + https://ecosystem.vuejs.press/ecosystem/zh/plugins/seo/seo/guide.html + https://ecosystem.vuejs.press/ecosystem/zh/plugins/seo/seo/guide.html + 指南 + 指南 本插件会通过向网站 &lt;head&gt; 注入标签,让你的网站完全支持 开放内容协议 OGP 和 JSON-LD 1.1,以全面增强站点的搜索引擎优化性。 开箱即用 插件开箱即用,在不做任何配置的情况下,会尽可能通过页面内容,提取对应的信息补全 OGP 与 JSON-LD 所需的必要标签。 默认情况下,插件会读取站点配置、主题配置与页面的 frontma... + 本插件会通过向网站 <head> 注入标签,让你的网站完全支持 开放内容协议 OGPJSON-LD 1.1,以全面增强站点的搜索引擎优化性。

+ +

开箱即用

+

插件开箱即用,在不做任何配置的情况下,会尽可能通过页面内容,提取对应的信息补全 OGP 与 JSON-LD 所需的必要标签。

+

默认情况下,插件会读取站点配置、主题配置与页面的 frontmatter 来尽可能自动生成。诸如站点名称,页面标题,页面类型,写作日期,最后更新日期,文章标签均会自动生成。

+

默认的 OGP 生成逻辑

+

| 属性名称 | 值 |
+| :

+]]>
+
+ + sitemap + https://ecosystem.vuejs.press/ecosystem/zh/plugins/seo/sitemap/ + https://ecosystem.vuejs.press/ecosystem/zh/plugins/seo/sitemap/ + sitemap + sitemap + + + + 配置 + https://ecosystem.vuejs.press/ecosystem/zh/plugins/seo/sitemap/config.html + https://ecosystem.vuejs.press/ecosystem/zh/plugins/seo/sitemap/config.html + 配置 + 配置 hostname 类型:string 必填:是 详情: 当前网站部署到的域名,插件需要此选项才能工作。 extraUrls 类型:string[] 详情: 需要额外包含的网址。 如果你有一些不包含在 VuePress 路由中的链接 (如: 存放在 public 文件夹下的页面或其他插件或工具直接生成的页面),你可能需要设置此项。 示例:[&apos;/ab... + 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 插件生成。

    +
  • +
+]]>
+
+ + Frontmatter + https://ecosystem.vuejs.press/ecosystem/zh/plugins/seo/sitemap/frontmatter.html + https://ecosystem.vuejs.press/ecosystem/zh/plugins/seo/sitemap/frontmatter.html + Frontmatter + Frontmatter sitemap 类型:SitemapFrontmatterOptions | false 详情: false 表示将页面排除在 sitemap 之外。 sitemap.changefreq 类型:&quot;always&quot; | &quot;hourly&quot; | &quot;daily&quot; | &quot;weekly&quot; | &quot;monthly&quot; | &quot;yearly&quot; | &quot;... + sitemap +
    +
  • +

    类型:SitemapFrontmatterOptions | false

    +
  • +
  • +

    详情:

    +

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

    +
  • +
+

sitemap.changefreq

+
    +
  • +

    类型:"always" | "hourly" | "daily" | "weekly" | "monthly" | "yearly" | "never"

    +
  • +
  • +

    默认值:"daily"

    +
  • +
  • +

    详情:

    +

    页面默认更新频率。它会覆盖插件选项中的 changefreq 选项。

    +
  • +
+

sitemap.priority

+
    +
  • +

    类型:number

    +
  • +
  • +

    默认值:0.5

    +
  • +
  • +

    详情:

    +

    页面优先级,范围 01

    +
  • +
+]]>
+
+ + 指南 + https://ecosystem.vuejs.press/ecosystem/zh/plugins/seo/sitemap/guide.html + https://ecosystem.vuejs.press/ecosystem/zh/plugins/seo/sitemap/guide.html + 指南 + 指南 本插件会为你的网站自动生成 Sitemap。为了使插件正常工作,你需要将部署的域名传递给插件的 hostname 选项。如果你想在开发服务器中预览,请配置 devServer 选项。 插件会自动根据页面的 Git 的时间戳生成页面的最后更新时间,同时会根据站点的多语言配置声明页面的其他语言版本替代地址。 控制 Sitemap 链接 默认情况下,所... + 本插件会为你的网站自动生成 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: /
+
+]]>
+
+ + 打包器相关 + https://ecosystem.vuejs.press/ecosystem/zh/tools/helper/node/bundler.html + https://ecosystem.vuejs.press/ecosystem/zh/tools/helper/node/bundler.html + 打包器相关 + 打包器相关 打包器函数用于在主题和插件中追加或修改打包器选项。 这些函数仅在 @vuepress/helper 中可用。 提示 所有函数都应在 extendsBundlerOptions 生命周期挂钩中调用。 我们在示例中省略了它。 实际代码应该是这样的: 通用方法 getBundlerName 获取当前打包器的名称。 示例 addCustomElem... + 打包器函数用于在主题和插件中追加或修改打包器选项。

+

这些函数仅在 @vuepress/helper 中可用。

+
+

提示

+

所有函数都应在 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: RegExp | string[] | string,
+) => 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<Buffer | string>
+
+  /**
+   * 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,
+    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 {
    +  addViteOptimizeDepsExclude,
    +  addViteOptimizeDepsInclude,
    +  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
    +})
    +
    +
  • +
+]]>
+
+ + 多语言相关 + https://ecosystem.vuejs.press/ecosystem/zh/tools/helper/node/locales.html + https://ecosystem.vuejs.press/ecosystem/zh/tools/helper/node/locales.html + 多语言相关 + 多语言相关 这些函数仅在 @vuepress/helper 中可用。 getFullLocaleConfig 一个从内置的 locale 信息和用户配置中获取完整 locale 配置的辅助函数。 app 参数是 VuePress Node app 实例。 default 参数是默认的 locale 信息,应该是一个 locale 信息设置的数组。 每个... + 这些函数仅在 @vuepress/helper 中可用。

+

getFullLocaleConfig

+

一个从内置的 locale 信息和用户配置中获取完整 locale 配置的辅助函数。

+
export interface GetLocaleConfigOption<T extends LocaleData> {
+  app: App
+  default: DefaultLocaleInfo<T>
+  config?: LocaleConfig<T> | undefined
+  name?: string
+}
+
+export const getFullLocaleConfig: <T extends LocaleData>(
+  options: GetLocaleConfigOption<T>,
+) => ExactLocaleConfig<T>
+
    +
  • +

    app 参数是 VuePress Node app 实例。

    +
  • +
  • +

    default 参数是默认的 locale 信息,应该是一个 locale 信息设置的数组。

    +

    每个 locale 信息设置应该是一个包含两个元素的元组:

    +
      +
    • 第一个元素是 locale 信息设置所属的语言代码数组。
    • +
    • 第二个元素是 locale 信息设置。
    • +
    +

    default 参数的示例:

    +
    const defaultLocaleInfo = [
    +  [
    +    ['en'],
    +    { title: 'VuePress', description: 'Vue-powered Static Site Generator' },
    +  ],
    +  [
    +    ['zh', 'zh-CN'],
    +    { title: 'VuePress', description: 'Vue 驱动的静态网站生成器' },
    +  ],
    +  [['zh-TW'], { title: 'VuePress', description: 'Vue 驅動的靜態網站生成器' }],
    +]
    +
  • +
  • +

    config 参数是用户 locale 配置,是可选的。

    +

    它应该是一个以 localePath 为键,以部分 locale 信息设置为值的对象。

    +

    config 参数的示例:

    +
    const userLocaleConfig = {
    +  '/zh/': { description: '由 Vue 驱动的静态网站生成器' },
    +  '/zh-TW/': { description: '由 Vue 驅動的靜態網站生成器' },
    +}
    +
  • +
  • +

    name 参数是插件名称,是可选的,仅用于日志记录。

    +
  • +
+

函数将自动合并默认的 locale 信息和用户 locale 配置,并返回最终的 locale 配置,其中用户 locale 配置将覆盖默认的 locale 信息。

+

默认的 locale 信息将根据站点配置中每个 locale 的当前语言选择,当 locale 的语言代码在默认的 locale 信息中找不到时,它将回退到以下存在的第一个:

+
    +
  • en-US 的 locale 信息
  • +
  • en 的 locale 信息
  • +
  • 默认 locale 信息的第一个元素
  • +
+]]>
+
+ + 页面相关 + https://ecosystem.vuejs.press/ecosystem/zh/tools/helper/node/page.html + https://ecosystem.vuejs.press/ecosystem/zh/tools/helper/node/page.html + 页面相关 + 页面相关 页面常见信息生成器。 这些函数仅在 @vuepress/helper 中可用。 getPageExcerpt 获取页面摘要。 getPageText 获取页面纯文本。 + 页面常见信息生成器。

+

这些函数仅在 @vuepress/helper 中可用。

+

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
+
]]>
+
+ + Artalk + https://ecosystem.vuejs.press/ecosystem/zh/plugins/blog/comment/artalk/ + https://ecosystem.vuejs.press/ecosystem/zh/plugins/blog/comment/artalk/ + Artalk + Artalk Artalk 是一款简洁的自托管评论系统,你可以在服务器上轻松部署并置入前端页面中。 来到你的博客,或是任意位置,放置 Artalk 评论框,让页面具备丰富的社会化功能。 安装 部署 Artalk 服务端 请参见 Artalk 文档。 配置 请配置 provider: &quot;Artalk&quot; 并将你的服务端地址传入插件选项中的 server。 ... + Artalk 是一款简洁的自托管评论系统,你可以在服务器上轻松部署并置入前端页面中。

+

来到你的博客,或是任意位置,放置 Artalk 评论框,让页面具备丰富的社会化功能。

+ +

安装

+
npm i -D artalk
+

部署 Artalk 服务端

+

请参见 Artalk 文档

+

配置

+

请配置 provider: "Artalk" 并将你的服务端地址传入插件选项中的 server

+

其他的配置项详见 Artalk 配置

+
+

提示

+

插件保留 el 选项在页面自行插入 Artalk。同时插件会自动根据 VuePress 信息为你自动设置 pageTitle, pageKeysite 选项。

+
+

夜间模式

+

为了能使 Artalk 应用正确的主题,你需要通过 darkmode 属性向 <CommentService /> 传入一个布尔值,代表当前是否开启夜间模式。

+]]>
+
+ + Artalk 选项 + https://ecosystem.vuejs.press/ecosystem/zh/plugins/blog/comment/artalk/config.html + https://ecosystem.vuejs.press/ecosystem/zh/plugins/blog/comment/artalk/config.html + Artalk 选项 + Artalk 选项 配置 详见 Artalk 配置。 el pageTitle, pageKey 和 site 选项为插件的保留选项,将从 VuePress 配置中自动推断,不可设置。 imgUploader 和 avatarURLBuilder 这两个函数选项只能在客户端配置。 插件配置 你可以直接在插件选项中配置可序列化的选项: 客户端配置 你可以... + 配置 +

详见 Artalk 配置

+
    +
  • +

    el pageTitle, pageKeysite 选项为插件的保留选项,将从 VuePress 配置中自动推断,不可设置。

    +
  • +
  • +

    imgUploaderavatarURLBuilder 这两个函数选项只能在客户端配置。

    +
  • +
+

插件配置

+

你可以直接在插件选项中配置可序列化的选项:

+
import { commentPlugin } from '@vuepress/plugin-comment'
+import { defineUserConfig } from 'vuepress'
+
+export default defineUserConfig({
+  plugins: [
+    commentPlugin({
+      provider: 'Artalk',
+      // 其他选项
+      // ...
+    }),
+  ],
+})
+

客户端配置

+

你可以使用 defineArtalkConfig 函数来配置 Artalk。

+
import { defineArtalkConfig } from '@vuepress/plugin-comment/client'
+import { defineClientConfig } from 'vuepress/client'
+
+defineArtalkConfig({
+  // Artalk 选项
+})
+
]]>
+
+ + Giscus + https://ecosystem.vuejs.press/ecosystem/zh/plugins/blog/comment/giscus/ + https://ecosystem.vuejs.press/ecosystem/zh/plugins/blog/comment/giscus/ + Giscus + Giscus Giscus 是一个基于 GitHub Discussion 的评论系统,启用简便。 准备工作 你需要创建一个公开仓库,并开启评论区,以作为评论存放的地点 你需要安装 Giscus App,使其有权限访问对应仓库。 在完成以上步骤后,请前往 Giscus 页面 获得你的设置。 你只需要填写仓库和 Discussion 分类,之后滚动到页面... + Giscus 是一个基于 GitHub Discussion 的评论系统,启用简便。

+ +

准备工作

+
    +
  1. +

    你需要创建一个公开仓库,并开启评论区,以作为评论存放的地点

    +
  2. +
  3. +

    你需要安装 Giscus App,使其有权限访问对应仓库。

    +
  4. +
  5. +

    在完成以上步骤后,请前往 Giscus 页面 获得你的设置。

    +

    你只需要填写仓库和 Discussion 分类,之后滚动到页面下部的 “启用 giscus” 部分,获取 data-repo, data-repo-id, data-categorydata-category-id 这四个属性。

    +
  6. +
+

配置

+

请配置 provider: "Giscus" 并将 data-repo, data-repo-id, data-categorydata-category-id 作为插件选项传入 repo, repoId, category categoryId

+

其他的配置项详见 Giscus 配置

+

主题

+

默认情况下,Giscus 使用 lightdark 主题 (基于夜间模式状态)。

+
+

夜间模式

+

为了能使 Giscus 应用正确的主题,你需要为 <CommentService /> 通过 darkmode 属性传入一个布尔值,代表当前是否开启夜间模式。

+
+

如果你想在日间模式和夜间模式下自定义主题,你可以设置 lightThemedarkTheme 选项,使用内置主题关键字或以 https:// 开头的自定义 css 链接。

+]]>
+
+
+
\ No newline at end of file diff --git a/zh/rss.xsl b/zh/rss.xsl new file mode 100644 index 0000000000..75170d8057 --- /dev/null +++ b/zh/rss.xsl @@ -0,0 +1,506 @@ + + + + + + + RSS Feed + + + + + + +
+ + + +

+ +

+

+ +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Language: + +
Published Date: + +
Last Build Date: + +
Copyright: + +
+ Catetory: + + + , + + +
+
+ +
+ + +
+
+
+ +
+
+ + + + + + + + + + + + + + , + + + + + + + + + + + +
+
+
+ +
+ +
+
+
+ + + +
+
diff --git a/zh/themes/default/components.html b/zh/themes/default/components.html new file mode 100644 index 0000000000..fcb4a41379 --- /dev/null +++ b/zh/themes/default/components.html @@ -0,0 +1,48 @@ + + + + + + + + + 内置组件 | VuePress 生态系统 + + + + + +
+ + + diff --git a/zh/themes/default/config.html b/zh/themes/default/config.html new file mode 100644 index 0000000000..715c0d50f6 --- /dev/null +++ b/zh/themes/default/config.html @@ -0,0 +1,205 @@ + + + + + + + + + 配置 | VuePress 生态系统 + + + + + +
+ + + diff --git a/zh/themes/default/extending.html b/zh/themes/default/extending.html new file mode 100644 index 0000000000..10b5d02494 --- /dev/null +++ b/zh/themes/default/extending.html @@ -0,0 +1,102 @@ + + + + + + + + + 继承 | VuePress 生态系统 + + + + + +
+ + + diff --git a/zh/themes/default/frontmatter.html b/zh/themes/default/frontmatter.html new file mode 100644 index 0000000000..cc84763393 --- /dev/null +++ b/zh/themes/default/frontmatter.html @@ -0,0 +1,93 @@ + + + + + + + + + Frontmatter | VuePress 生态系统 + + + + + +
+ + + diff --git a/zh/themes/default/index.html b/zh/themes/default/index.html new file mode 100644 index 0000000000..ac0bd96f88 --- /dev/null +++ b/zh/themes/default/index.html @@ -0,0 +1,49 @@ + + + + + + + + + 默认主题 | VuePress 生态系统 + + + + + + + + + diff --git a/zh/themes/default/locale.html b/zh/themes/default/locale.html new file mode 100644 index 0000000000..6a41619c9e --- /dev/null +++ b/zh/themes/default/locale.html @@ -0,0 +1,62 @@ + + + + + + + + + 语言配置 | VuePress 生态系统 + + + + + +
+ + + diff --git a/zh/themes/default/markdown.html b/zh/themes/default/markdown.html new file mode 100644 index 0000000000..9574c703bf --- /dev/null +++ b/zh/themes/default/markdown.html @@ -0,0 +1,117 @@ + + + + + + + + + Markdown | VuePress 生态系统 + + + + + +
+ + + diff --git a/zh/themes/default/plugin.html b/zh/themes/default/plugin.html new file mode 100644 index 0000000000..10b362ff17 --- /dev/null +++ b/zh/themes/default/plugin.html @@ -0,0 +1,51 @@ + + + + + + + + + 插件配置 | VuePress 生态系统 + + + + + +
+ + + diff --git a/zh/themes/default/styles.html b/zh/themes/default/styles.html new file mode 100644 index 0000000000..bae52e27e6 --- /dev/null +++ b/zh/themes/default/styles.html @@ -0,0 +1,157 @@ + + + + + + + + + 样式 | VuePress 生态系统 + + + + + +
+ + + diff --git a/zh/themes/guidelines.html b/zh/themes/guidelines.html new file mode 100644 index 0000000000..11f22be277 --- /dev/null +++ b/zh/themes/guidelines.html @@ -0,0 +1,43 @@ + + + + + + + + + 主题指南 | VuePress 生态系统 + + + + + +
+ + + diff --git a/zh/themes/index.html b/zh/themes/index.html new file mode 100644 index 0000000000..9fd717ce65 --- /dev/null +++ b/zh/themes/index.html @@ -0,0 +1,43 @@ + + + + + + + + + 主题 | VuePress 生态系统 + + + + + + + + + diff --git a/zh/tools/helper/client.html b/zh/tools/helper/client.html new file mode 100644 index 0000000000..120e7116c4 --- /dev/null +++ b/zh/tools/helper/client.html @@ -0,0 +1,130 @@ + + + + + + + + + 客户端相关 | VuePress 生态系统 + + + + + +
+ + + diff --git a/zh/tools/helper/index.html b/zh/tools/helper/index.html new file mode 100644 index 0000000000..9d00c23da7 --- /dev/null +++ b/zh/tools/helper/index.html @@ -0,0 +1,43 @@ + + + + + + + + + @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..fa4fd29417 --- /dev/null +++ b/zh/tools/helper/node/bundler.html @@ -0,0 +1,204 @@ + + + + + + + + + 打包器相关 | VuePress 生态系统 + + + + + +
+ + + diff --git a/zh/tools/helper/node/index.html b/zh/tools/helper/node/index.html new file mode 100644 index 0000000000..fa073179ea --- /dev/null +++ b/zh/tools/helper/node/index.html @@ -0,0 +1,43 @@ + + + + + + + + + Node | VuePress 生态系统 + + + + + + + + + diff --git a/zh/tools/helper/node/locales.html b/zh/tools/helper/node/locales.html new file mode 100644 index 0000000000..42db076e45 --- /dev/null +++ b/zh/tools/helper/node/locales.html @@ -0,0 +1,65 @@ + + + + + + + + + 多语言相关 | VuePress 生态系统 + + + + + +
+ + + diff --git a/zh/tools/helper/node/page.html b/zh/tools/helper/node/page.html new file mode 100644 index 0000000000..ffe99794c7 --- /dev/null +++ b/zh/tools/helper/node/page.html @@ -0,0 +1,117 @@ + + + + + + + + + 页面相关 | VuePress 生态系统 + + + + + +
+ + + diff --git a/zh/tools/helper/shared.html b/zh/tools/helper/shared.html new file mode 100644 index 0000000000..bd2753cef8 --- /dev/null +++ b/zh/tools/helper/shared.html @@ -0,0 +1,147 @@ + + + + + + + + + 共享方法 | VuePress 生态系统 + + + + + +
+ + + diff --git a/zh/tools/helper/style.html b/zh/tools/helper/style.html new file mode 100644 index 0000000000..a143c1cfc5 --- /dev/null +++ b/zh/tools/helper/style.html @@ -0,0 +1,43 @@ + + + + + + + + + 样式 | VuePress 生态系统 + + + + + + + + + diff --git a/zh/tools/index.html b/zh/tools/index.html new file mode 100644 index 0000000000..067b37eea2 --- /dev/null +++ b/zh/tools/index.html @@ -0,0 +1,43 @@ + + + + + + + + + 工具包 | VuePress 生态系统 + + + + + + + + +